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 == TypeMatch.Same || m == TypeMatch.Substitute) { return(m); } else if (m == TypeMatch.InConversion || m == TypeMatch.AutoDereference || m == TypeMatch.ImplicitReference || m == TypeMatch.OutConversion) { match = m; } else if (!m.IsMismatch()) { throw new NotImplementedException(); } } return(match); }
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); }
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); } }
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); } }
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); }
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); }
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); } }
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); }
public bool HasFlag(TypeMatch other) { return((this.flag & other.flag) != 0); }
public bool Equals(TypeMatch obj) { return(this.flag == obj.flag); }
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); }