Beispiel #1
0
        private static bool strictTemplateMatches(ComputationContext ctx, EntityInstance input, EntityInstance target,
                                                  TypeMatching matching,
                                                  out TypeMatch failMatch)
        {
            if (input.Target != target.Target)
            {
                failMatch = TypeMatch.No;
                return(false);
            }

            TemplateDefinition template = target.TargetTemplate;

            for (int i = 0; i < template.Name.Arity; ++i)
            {
                failMatch = input.TemplateArguments[i].TemplateMatchesTarget(ctx,
                                                                             target.TemplateArguments[i],
                                                                             template.Name.Parameters[i].Variance,
                                                                             matching);

                if (!failMatch.Passed)
                {
                    return(false);
                }
            }

            failMatch = TypeMatch.Same;
            return(true);
        }
Beispiel #2
0
        public override TypeMatch TemplateMatchesInput(ComputationContext ctx, EntityInstance input, VarianceMode variance,
                                                       TypeMatching matching)
        {
            TypeMatch match = TypeMatch.No;

            foreach (IEntityInstance target in this.elements)
            {
                TypeMatch m = target.TemplateMatchesInput(ctx, input, variance, matching);
                if (m == TypeMatch.Same || m == TypeMatch.Substitute)
                {
                    return(m);
                }
                else if (m == TypeMatch.AutoDereference ||
                         m == TypeMatch.InConversion ||
                         m == TypeMatch.ImplicitReference ||
                         m == TypeMatch.OutConversion)
                {
                    match = m;
                }
                else if (!m.IsMismatch())
                {
                    throw new NotImplementedException();
                }
            }

            return(match);
        }
Beispiel #3
0
        public TypeMatching WithIgnoredMutability(bool ignored)
        {
            TypeMatching result = this;

            result.ForcedIgnoreMutability = ignored;
            return(result);
        }
Beispiel #4
0
        internal TypeMatching WithSlicing(bool slicing)
        {
            TypeMatching result = this;

            result.AllowSlicing = slicing;
            return(result);
        }
Beispiel #5
0
        internal TypeMatching AllowedLifetimeChecking(bool value)
        {
            TypeMatching result = this;

            result.allowLifetimeChecking = value;
            return(result);
        }
Beispiel #6
0
        private static bool templateMatches(ComputationContext ctx, EntityInstance input,
                                            EntityInstance target, TypeMatching matching,
                                            out TypeMatch failMatch)
        {
            if (input.IsJoker || target.IsJoker)
            {
                failMatch = TypeMatch.Same;
                return(true);
            }

            bool matches = strictTemplateMatches(ctx, input, target, matching, out failMatch);

            TypeDefinition target_type = target.TargetType;

            if (matches || (!(matching.DuckTyping && target_type.IsInterface) && !target_type.IsProtocol))
            {
                return(matches);
            }

            VirtualTable vtable = EntityInstanceExtension.BuildDuckVirtualTable(ctx, input, target, allowPartial: false);

            if (vtable == null)
            {
                return(false);
            }
            else
            {
                failMatch = TypeMatch.Same;
                return(true);
            }
        }
Beispiel #7
0
        public TypeMatch TemplateMatchesInput(ComputationContext ctx, EntityInstance input,
                                              VarianceMode variance, TypeMatching matching)
        {
            // todo: this is correct but it is just a hack really, because we should check constraints
            // and if they are compatible we should return match
            if (input.TargetsTemplateParameter && this.TargetsTemplateParameter &&
                !input.TargetTemplate.Constraints.Any() && !this.TargetTemplate.Constraints.Any())
            {
                return(TypeMatch.Same);
            }

            matching.Position = matching.Position.Flipped(variance);
            switch (matching.Position)
            {
            case VarianceMode.None:
                return(this.IsExactlySame(input, jokerMatchesAll: true) ? TypeMatch.Same : TypeMatch.No);

            case VarianceMode.In:
                return(TypeMatcher.Matches(ctx, this, input, matching));

            case VarianceMode.Out:
                return(TypeMatcher.Matches(ctx, input, this, matching));
            }

            throw new NotImplementedException();
        }
Beispiel #8
0
        public static TypeMatch inversedTypeMatching(ComputationContext ctx, EntityInstance input, IEnumerable <TypeAncestor> targets,
                                                     TypeMatching matching)
        {
            if (!targets.Any())
            {
                return(TypeMatch.No);
            }

            EntityInstance target = targets.First().AncestorInstance;

            TypeMutability target_mutability = target.MutabilityOfType(ctx);

            foreach (TypeAncestor inherited_target in targets)
            {
                // please note that unlike normal type matching we reversed the types, in enum you can
                // pass base type as descendant!
                if (matchTypes(ctx, target_mutability, target.Lifetime, inherited_target.AncestorInstance,
                               input, matching, inherited_target.Distance,
                               out TypeMatch match))
                {
                    return(match);
                }
            }

            return(TypeMatch.No);
        }
Beispiel #9
0
        private static bool matchTypes(ComputationContext ctx, TypeMutability inputMutability,
                                       Lifetime inputLifetime,
                                       EntityInstance input, EntityInstance target,
                                       TypeMatching matching, int distance, out TypeMatch match)
        {
            bool is_matched = templateMatches(ctx, input, target, matching, out TypeMatch fail_match);

            if (!is_matched)
            {
                match = fail_match;
                return(false);
            }

            // we cannot shove mutable type in disguise as immutable one, consider such scenario
            // user could create const wrapper over "*Object" (this is immutable type) and then create its instance
            // passing some mutable instance, wrapper would be still immutable despite the fact it holds mutable data
            // this would be disastrous when working concurrently (see more in Documentation/Mutability)

            bool mutability_matches;

            {
                TypeMutability target_mutability = target.MutabilityOfType(ctx);
                // cheapest part to compute
                mutability_matches = MutabilityMatches(ctx.Env.Options, inputMutability, target_mutability);

                // fixing mutability mismatch
                if (!mutability_matches
                    &&
                    (matching.ForcedIgnoreMutability
                     // in case when we have two value types in hand mutability does not matter, because we copy
                     // instances and they are separate beings afterwards
                     || (!matching.MutabilityCheckRequestByData &&
                         input.TargetType.IsValueType(ctx) && target.TargetType.IsValueType(ctx))))
                {
                    mutability_matches = true;
                }
            }

            if (!mutability_matches)
            {
                match = TypeMatch.Mismatched(mutability: true);
                return(true);
            }
            else if (matching.LifetimeCheck && matching.TargetLifetime.Outlives(matching.InputLifetime))
            {
                match = TypeMatch.Lifetime;
                return(true);
            }
            else if (distance == 0 && input.Target == target.Target)
            {
                match = TypeMatch.Same;
                return(true);
            }
            else
            {
                match = TypeMatch.Substituted(distance);
                return(true);
            }
        }
Beispiel #10
0
        internal TypeMatching WithMutabilityCheckRequest(bool value)
        {
            // do not grant check request if we are already ignoring mutability
            if (this.ForcedIgnoreMutability)
            {
                return(this);
            }

            TypeMatching result = this;

            result.MutabilityCheckRequestByData = value;
            return(result);
        }
Beispiel #11
0
 internal TypeMatching WithLifetimeCheck(bool value, Lifetime inputLifetime, Lifetime targetLifetime)
 {
     if (this.allowLifetimeChecking)
     {
         TypeMatching result = this;
         result.LifetimeCheck  = value;
         result.InputLifetime  = inputLifetime;
         result.TargetLifetime = targetLifetime;
         return(result);
     }
     else
     {
         return(this);
     }
 }
Beispiel #12
0
        private static bool interchangeableTypesTo(ComputationContext ctx, IEntityInstance input, IEntityInstance target)
        {
            bool is_input_sealed = input.EnumerateAll()
                                   .Select(it => { ctx.Env.Dereference(it, out IEntityInstance result); return(result); })
                                   .SelectMany(it => it.EnumerateAll())
                                   .All(it => it.Target.Modifier.IsSealed);

            if (is_input_sealed)
            {
                TypeMatch match = input.MatchesTarget(ctx, target, TypeMatching.Create(ctx.Env.Options.InterfaceDuckTyping, allowSlicing: true).WithIgnoredMutability(true));

                // example: we cannot check if x (of Int) is IAlien because Int is sealed and there is no way
                // it could be something else not available in its inheritance tree
                if (!match.Passed)
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #13
0
        public override TypeMatch TemplateMatchesTarget(ComputationContext ctx, IEntityInstance target, VarianceMode variance,
                                                        TypeMatching matching)
        {
            IEnumerable <TypeMatch> matches = this.elements.Select(it => it.TemplateMatchesTarget(ctx, target, variance, matching));

            if (matches.All(it => it == TypeMatch.Same))
            {
                return(TypeMatch.Same);
            }

            IEnumerable <TypeMatch> substitutions = matches.Where(it => it == TypeMatch.Same || it == TypeMatch.Substitute)
                                                    .OrderByDescending(it => it.Distance);

            if (substitutions.Count() == matches.Count())
            {
                return(substitutions.First());
            }
            else
            {
                return(TypeMatch.No);
            }
        }
Beispiel #14
0
        public void Validate(ComputationContext ctx)
        {
            foreach (NameReference base_of in this.BaseOfNames)
            {
                // we allow slicing because we just need if the hierarchy is not reversed, not to pass actual values
                if (this.InheritsNames.Any(it
                                           =>
                {
                    TypeMatch match = it.Evaluation.Components.MatchesTarget(ctx, base_of.Evaluation.Components,
                                                                             TypeMatching.Create(ctx.Env.Options.InterfaceDuckTyping, allowSlicing: true));
                    return(match == TypeMatch.Same || match == TypeMatch.Substitute);
                }))
                {
                    ctx.AddError(ErrorCode.ConstraintConflictingTypeHierarchy, base_of);
                }
            }

            // the left side: target match (i.e. template inner parameter type) -> template
            // the right side: this -> template parameter -> name definition -> template
            if (this.Name.Binding.Match.Instance.Target.Owner != this.Owner.Owner.Owner)
            {
                ctx.AddError(ErrorCode.MisplacedConstraint, this);
            }
        }
Beispiel #15
0
        // this is somewhat limiting, because when we have multiple targets we go easy way not allowing
        // type conversion, some day improve it
        public override TypeMatch MatchesTarget(ComputationContext ctx, IEntityInstance target, TypeMatching matching)
        {
            IEnumerable <TypeMatch> matches = this.elements.Select(it => it.MatchesTarget(ctx, target, matching)).ToArray();

            if (matches.Any(it => it == TypeMatch.Same))
            {
                return(TypeMatch.Same);
            }

            IEnumerable <TypeMatch> substitutions = matches.Where(it => it == TypeMatch.Substitute).OrderBy(it => it.Distance);

            if (substitutions.Any())
            {
                return(substitutions.First());
            }
            else
            {
                return(TypeMatch.No);
            }
        }
Beispiel #16
0
        public override TypeMatch MatchesInput(ComputationContext ctx, EntityInstance input, TypeMatching matching)
        {
            TypeMatch match = TypeMatch.No;

            foreach (IEntityInstance target in this.elements)
            {
                TypeMatch m = target.MatchesInput(ctx, input, matching);
                if (m.IsMismatch())
                {
                    return(m);
                }
                else if (match.IsMismatch())
                {
                    match = m;
                }
                else if (match != m)
                {
                    return(TypeMatch.No);
                }
            }

            return(match);
        }
Beispiel #17
0
 public abstract TypeMatch TemplateMatchesInput(ComputationContext ctx, EntityInstance input,
                                                VarianceMode variance, TypeMatching matching);
Beispiel #18
0
 public abstract TypeMatch TemplateMatchesTarget(ComputationContext ctx, IEntityInstance target,
                                                 VarianceMode variance, TypeMatching matching);
Beispiel #19
0
 public abstract TypeMatch MatchesTarget(ComputationContext ctx, IEntityInstance target, TypeMatching matching);
Beispiel #20
0
 public abstract TypeMatch MatchesInput(ComputationContext ctx, EntityInstance input, TypeMatching matching);
Beispiel #21
0
 public TypeMatch TemplateMatchesTarget(ComputationContext ctx, IEntityInstance target, VarianceMode variance, TypeMatching matching)
 {
     return(target.TemplateMatchesInput(ctx, this, variance, matching));
 }
Beispiel #22
0
 public TypeMatch MatchesTarget(ComputationContext ctx, IEntityInstance target, TypeMatching matching)
 {
     return(target.MatchesInput(ctx, this, matching));
 }
Beispiel #23
0
        public ConstraintMatch ArgumentMatchesParameterConstraints(ComputationContext ctx, EntityInstance closedTemplate,
                                                                   TemplateParameter param)
        {
            if (this.TargetsTemplateParameter && this.TemplateParameterTarget == param)
            {
                return(ConstraintMatch.Yes);
            }

            TypeMutability arg_mutability = this.SurfaceMutabilityOfType(ctx);

            if (param.Constraint.Modifier.HasConst &&
                arg_mutability != TypeMutability.ForceConst && arg_mutability != TypeMutability.ConstAsSource)
            {
                return(ConstraintMatch.MutabilityViolation);
            }
            else if (param.Constraint.Modifier.HasMutable && arg_mutability != TypeMutability.ForceMutable)
            {
                return(ConstraintMatch.MutabilityViolation);
            }
            else if (param.Constraint.Modifier.HasReassignable && !arg_mutability.HasFlag(TypeMutability.Reassignable))
            {
                return(ConstraintMatch.AssignabilityViolation);
            }

            // 'inherits' part of constraint
            foreach (EntityInstance constraint_inherits in param.Constraint.TranslateInherits(closedTemplate))
            {
                TypeMatch match = TypeMatcher.Matches(ctx, this, constraint_inherits,
                                                      TypeMatching.Create(ctx.Env.Options.InterfaceDuckTyping, allowSlicing: true));
                if (match.IsMismatch())
                {
                    return(ConstraintMatch.InheritsViolation);
                }
            }

            {
                VirtualTable vtable = EntityInstanceExtension.BuildDuckVirtualTable(ctx, this, param.AssociatedType.InstanceOf,
                                                                                    allowPartial: false);
                if (vtable == null)
                {
                    return(ConstraintMatch.MissingFunction);
                }
            }


            // 'base-of' part of constraint
            IEnumerable <IEntityInstance> arg_bases = (this.TargetsTemplateParameter
                ? this.TargetType.TemplateParameter.Constraint.TranslateBaseOf(closedTemplate)
                : Enumerable.Empty <EntityInstance>()
                                                       )
                                                      .Concat(this);

            foreach (EntityInstance constraint_base in param.Constraint.TranslateBaseOf(closedTemplate))
            {
                if (!arg_bases.Any(it => !constraint_base.MatchesTarget(ctx, it, TypeMatching.Create(ctx.Env.Options.InterfaceDuckTyping, allowSlicing: true))
                                   .IsMismatch()))
                {
                    return(ConstraintMatch.BaseViolation);
                }
            }

            return(ConstraintMatch.Yes);
        }
Beispiel #24
0
        public static TypeMatch Matches(ComputationContext ctx, EntityInstance input, EntityInstance target, TypeMatching matching)
        {
            if (input.IsJoker || target.IsJoker)
            {
                return(TypeMatch.Same);
            }

            if (!target.Target.IsType() || !input.Target.IsType())
            {
                return(TypeMatch.No);
            }

            if (input.Lifetime.IsAttached)
            {
                matching = matching.WithLifetimeCheck(true, input.Lifetime, target.Lifetime);
            }


            {
                IEnumerable <FunctionDefinition> in_conv = target.TargetType.ImplicitInConverters().StoreReadOnly();
                bool conv_slicing_sub = input.TargetType.AllowSlicedSubstitution;
                foreach (FunctionDefinition func in in_conv)
                {
                    IEntityInstance conv_type = func.Parameters.Single().TypeName.Evaluated(ctx, EvaluationCall.AdHocCrossJump).TranslateThrough(target);
                    if (target.IsIdentical(conv_type)) // preventing circular conversion check
                    {
                        continue;
                    }

                    TypeMatch m = input.MatchesTarget(ctx, conv_type, matching.WithSlicing(conv_slicing_sub));
                    if (m == TypeMatch.Same || m == TypeMatch.Substitute)
                    {
                        return(TypeMatch.InConversion);
                    }
                }
            }


            if (ctx.Env.IsReferenceOfType(target))
            {
                if (ctx.Env.IsPointerLikeOfType(input))
                {
                    // note, that we could have reference to pointer in case of the target, we have to unpack both
                    // to the level of the value types
                    int target_dereferences = ctx.Env.Dereference(target, out IEntityInstance inner_target_type);
                    int input_dereferences  = ctx.Env.Dereference(input, out IEntityInstance inner_input_type);

                    TypeMatch m = inner_input_type.MatchesTarget(ctx, inner_target_type, matching
                                                                 .WithSlicing(true)
                                                                 .WithMutabilityCheckRequest(true)
                                                                 .WithLifetimeCheck(true, ctx.Env.IsReferenceOfType(input) ? input.Lifetime : Lifetime.Timeless, target.Lifetime));
                    if (target_dereferences > input_dereferences && !m.IsMismatch())
                    {
                        m |= TypeMatch.ImplicitReference;
                    }
                    return(m);
                }
                else
                {
                    ctx.Env.Dereferenced(target, out IEntityInstance inner_target_type);

                    TypeMatch m = input.MatchesTarget(ctx, inner_target_type, matching
                                                      .WithSlicing(true)
                                                      .WithMutabilityCheckRequest(true)
                                                      .WithLifetimeCheck(true, input.Lifetime, target.Lifetime));
                    if (m == TypeMatch.Same || m == TypeMatch.Substitute)
                    {
                        return(m | TypeMatch.ImplicitReference);
                    }
                    else
                    {
                        return(m);
                    }
                }
            }
            // automatic dereferencing pointers
            else if (ctx.Env.IsPointerLikeOfType(input))
            {
                int input_dereferences;
                {
                    input_dereferences = ctx.Env.Dereference(input, out IEntityInstance dummy);
                }

                // we check if we have more refs/ptrs in input than in target
                if (input_dereferences > (ctx.Env.IsPointerOfType(target) ? 1 : 0))
                {
                    ctx.Env.DereferencedOnce(input, out IEntityInstance inner_input_type, out bool dummy);

                    TypeMatch m = inner_input_type.MatchesTarget(ctx, target, matching.WithSlicing(true));
                    if (m.Passed)
                    {
                        return(m | TypeMatch.AutoDereference);
                    }
                }
                else
                {
                    matching = matching.WithMutabilityCheckRequest(true)
                    ;
                }
            }

            if (ctx.Env.IsReferenceOfType(input) && ctx.Env.IsPointerOfType(target))
            {
                // note, that we could have reference to pointer in case of the target, we have to unpack both
                // to the level of the value types
                ctx.Env.DereferencedOnce(target, out IEntityInstance inner_target_type, out bool dummy1);
                ctx.Env.DereferencedOnce(input, out IEntityInstance inner_input_type, out bool dummy2);

                TypeMatch m = inner_input_type.MatchesTarget(ctx, inner_target_type, matching
                                                             .WithSlicing(true)
                                                             .WithMutabilityCheckRequest(true)
                                                             .WithLifetimeCheck(true, input.Lifetime, Lifetime.Timeless));
                if (!m.IsMismatch())
                {
                    m |= TypeMatch.Attachment;
                }
                return(m);
            }


            {
                IEnumerable <FunctionDefinition> out_conv = input.TargetType.ImplicitOutConverters().StoreReadOnly();
                bool conv_slicing_sub = input.TargetType.AllowSlicedSubstitution;
                foreach (FunctionDefinition func in out_conv)
                {
                    IEntityInstance conv_type = func.ResultTypeName.Evaluated(ctx, EvaluationCall.AdHocCrossJump).TranslateThrough(input);
                    //if (target == conv_type) // preventing circular conversion check
                    //  continue;

                    TypeMatch m = conv_type.MatchesTarget(ctx, target, matching.WithSlicing(conv_slicing_sub));
                    if (m == TypeMatch.Same || m == TypeMatch.Substitute)
                    {
                        return(TypeMatch.OutConversion);
                    }
                }
            }

            if (target.TargetType.AllowSlicedSubstitution)
            {
                matching.AllowSlicing = true;
            }


            TypeMatch match = TypeMatch.No;

            TypeMutability input_mutability = input.MutabilityOfType(ctx);

            if (matching.AllowSlicing)
            {
                IEnumerable <TypeAncestor> input_family = new[] { new TypeAncestor(input, 0) }
                .Concat(input.Inheritance(ctx).OrderedTypeAncestorsIncludingObject
                        // enum substitution works in reverse so we have to exclude these from here
                        .Where(it => !it.AncestorInstance.TargetType.Modifier.HasEnum));

                foreach (TypeAncestor inherited_input in input_family)
                {
                    if (matchTypes(ctx, input_mutability, input.Lifetime, inherited_input.AncestorInstance,
                                   target, matching, inherited_input.Distance, out TypeMatch m))
                    {
                        return(m);
                    }
                    else if (m != TypeMatch.No) // getting more specific rejection than just bare one
                    {
                        match = m;
                    }
                }
            }
            // when slicing is disabled we can compare try to substitute only the same type
            else if (input.Target == target.Target)
            {
                if (matchTypes(ctx, input_mutability, input.Lifetime, input, target, matching, distance: 0, match: out match))
                {
                    if (match != TypeMatch.Same && !match.IsMismatch())
                    {
                        throw new Exception($"Internal exception {match}");
                    }
                    return(match);
                }
            }

            if (input.TargetType.Modifier.HasEnum)
            {
                // another option for enum-"inheritance" would be dropping it altogether, and copying all the values from the
                // base enum. Adding conversion constructor from base to child type will suffice and allow to get rid
                // of those enum-inheritance matching

                // since we compare only enums here we allow slicing (because it is not slicing, just passing single int)
                match = inversedTypeMatching(ctx, input, new[] { new TypeAncestor(target, 0) }
                                             .Concat(target.Inheritance(ctx).OrderedTypeAncestorsIncludingObject)
                                             .Where(it => it.AncestorInstance.TargetType.Modifier.HasEnum), matching.WithSlicing(true));

                if (!match.IsMismatch())
                {
                    return(match);
                }
            }

            if (target.TargetsTemplateParameter)
            {
                // template parameter can have reversed inheritance defined via base-of constraints
                // todo: at this it should be already evaluated, so constraints should be surfables
                IEnumerable <IEntityInstance> base_of
                    = target.TemplateParameterTarget.Constraint.BaseOfNames.Select(it => it.Evaluated(ctx, EvaluationCall.AdHocCrossJump));

                match = inversedTypeMatching(ctx, input, new[] { new TypeAncestor(target, 0) }
                                             .Concat(base_of.Select(it => new TypeAncestor(it.Cast <EntityInstance>(), 1))), matching);

                if (!match.IsMismatch())
                {
                    return(match);
                }
            }

            return(match);
        }
Beispiel #25
0
 public TypeMatch MatchesInput(ComputationContext ctx, EntityInstance input, TypeMatching matching)
 {
     return(TypeMatcher.Matches(ctx, input, this, matching));
 }