private static bool ParametersMatch(ParameterSymbol candidateParam, TypeMap candidateMethodTypeMap, ref ParamInfo <TypeSymbol> targetParam) { // This could be combined into a single return statement with a more complicated expression, but that would // be harder to debug. if ((candidateParam.RefKind != RefKind.None) != targetParam.IsByRef || candidateParam.CountOfCustomModifiersPrecedingByRef != targetParam.CountOfCustomModifiersPrecedingByRef) { return(false); } // CONSIDER: Do we want to add special handling for error types? Right now, we expect they'll just fail to match. var substituted = new TypeWithModifiers(candidateParam.Type, candidateParam.CustomModifiers).SubstituteType(candidateMethodTypeMap); if (substituted.Type != targetParam.Type) { return(false); } if (!CustomModifiersMatch(substituted.CustomModifiers, targetParam.CustomModifiers)) { return(false); } return(true); }
private static bool ReturnTypesMatch(MethodSymbol candidateMethod, TypeMap candidateMethodTypeMap, ref ParamInfo <TypeSymbol> targetReturnParam) { if (candidateMethod.ReturnsByRef != targetReturnParam.IsByRef || candidateMethod.CountOfCustomModifiersPrecedingByRef != targetReturnParam.CountOfCustomModifiersPrecedingByRef) { return(false); } TypeSymbol candidateReturnType = candidateMethod.ReturnType; TypeSymbol targetReturnType = targetReturnParam.Type; // CONSIDER: Do we want to add special handling for error types? Right now, we expect they'll just fail to match. var substituted = new TypeWithModifiers(candidateReturnType, candidateMethod.ReturnTypeCustomModifiers).SubstituteType(candidateMethodTypeMap); if (substituted.Type != targetReturnType) { return(false); } if (!CustomModifiersMatch(substituted.CustomModifiers, targetReturnParam.CustomModifiers)) { return(false); } return(true); }
private static TypeWithModifiers SubstituteAllTypeParameters(AbstractTypeMap substitution, TypeWithModifiers type) { if (substitution != null) { TypeWithModifiers previous; do { previous = type; type = type.SubstituteType(substitution); } while (type != previous); } return type; }
private static TypeWithModifiers SubstituteAllTypeParameters(AbstractTypeMap substitution, TypeWithModifiers type) { if (substitution != null) { TypeWithModifiers previous; do { previous = type; type = type.SubstituteType(substitution); } while (type != previous); } return(type); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> /// <param name="t1">LHS</param> /// <param name="t2">RHS</param> /// <param name="substitution"> /// Substitutions performed so far (or null for none). /// Keys are type parameters, values are types (possibly type parameters). /// Will be updated with new substitutions by the callee. /// Should be ignored when false is returned. /// </param> /// <returns>True if there exists a type map such that Map(LHS) == Map(RHS).</returns> /// <remarks> /// Derived from Dev10's BSYMMGR::UnifyTypes. /// Two types will not unify if they have different custom modifiers. /// </remarks> private static bool CanUnifyHelper(TypeWithModifiers t1, TypeWithModifiers t2, ref MutableTypeMap substitution) { if (t1 == t2) { return true; } else if ((object)t1.Type == null || (object)t2.Type == null) { // Can't both be null or they would have been equal return false; } if (substitution != null) { t1 = t1.SubstituteType(substitution); t2 = t2.SubstituteType(substitution); } // If one of the types is a type parameter, then the substitution could make them equal. if (t1 == t2) { return true; } // We can avoid a lot of redundant checks if we ensure that we only have to check // for type parameters on the LHS if (!t1.Type.IsTypeParameter() && t2.Type.IsTypeParameter()) { TypeWithModifiers tmp = t1; t1 = t2; t2 = tmp; } // If t1 is not a type parameter, then neither is t2 Debug.Assert(t1.Type.IsTypeParameter() || !t2.Type.IsTypeParameter()); switch (t1.Type.Kind) { case SymbolKind.ArrayType: { if (t2.Type.TypeKind != t1.Type.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return false; } ArrayTypeSymbol at1 = (ArrayTypeSymbol)t1.Type; ArrayTypeSymbol at2 = (ArrayTypeSymbol)t2.Type; if (!at1.HasSameShapeAs(at2)) { return false; } return CanUnifyHelper(new TypeWithModifiers(at1.ElementType, at1.CustomModifiers), new TypeWithModifiers(at2.ElementType, at2.CustomModifiers), ref substitution); } case SymbolKind.PointerType: { if (t2.Type.TypeKind != t1.Type.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return false; } PointerTypeSymbol pt1 = (PointerTypeSymbol)t1.Type; PointerTypeSymbol pt2 = (PointerTypeSymbol)t2.Type; return CanUnifyHelper(new TypeWithModifiers(pt1.PointedAtType, pt1.CustomModifiers), new TypeWithModifiers(pt2.PointedAtType, pt2.CustomModifiers), ref substitution); } case SymbolKind.NamedType: case SymbolKind.ErrorType: { if (t2.Type.TypeKind != t1.Type.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return false; } NamedTypeSymbol nt1 = (NamedTypeSymbol)t1.Type; NamedTypeSymbol nt2 = (NamedTypeSymbol)t2.Type; if (nt1.IsTupleType) { if (!nt2.IsTupleType) { return false; } return CanUnifyHelper(new TypeWithModifiers(nt1.TupleUnderlyingType), new TypeWithModifiers(nt2.TupleUnderlyingType), ref substitution); } if (!nt1.IsGenericType) { return !nt2.IsGenericType && nt1 == nt2; } else if (!nt2.IsGenericType) { return false; } int arity = nt1.Arity; if (nt2.Arity != arity || nt2.OriginalDefinition != nt1.OriginalDefinition) { return false; } var nt1Arguments = nt1.TypeArgumentsNoUseSiteDiagnostics; var nt2Arguments = nt2.TypeArgumentsNoUseSiteDiagnostics; var nt1HasModifiers = nt1.HasTypeArgumentsCustomModifiers; var nt2HasModifiers = nt2.HasTypeArgumentsCustomModifiers; for (int i = 0; i < arity; i++) { if (!CanUnifyHelper(new TypeWithModifiers(nt1Arguments[i], nt1HasModifiers ? nt1.GetTypeArgumentCustomModifiers(i) : default(ImmutableArray<CustomModifier>)), new TypeWithModifiers(nt2Arguments[i], nt2HasModifiers ? nt2.GetTypeArgumentCustomModifiers(i) : default(ImmutableArray<CustomModifier>)), ref substitution)) { return false; } } // Note: Dev10 folds this into the loop since GetTypeArgsAll includes type args for containing types // TODO: Calling CanUnifyHelper for the containing type is an overkill, we simply need to go through type arguments for all containers. return (object)nt1.ContainingType == null || CanUnifyHelper(new TypeWithModifiers(nt1.ContainingType), new TypeWithModifiers(nt2.ContainingType), ref substitution); } case SymbolKind.TypeParameter: { // These substitutions are not allowed in C# if (t2.Type.TypeKind == TypeKind.Pointer || t2.Type.SpecialType == SpecialType.System_Void) { return false; } TypeParameterSymbol tp1 = (TypeParameterSymbol)t1.Type; // Perform the "occurs check" - i.e. ensure that t2 doesn't contain t1 to avoid recursive types // Note: t2 can't be the same type param - we would have caught that with ReferenceEquals above if (Contains(t2.Type, tp1)) { return false; } if (t1.CustomModifiers.IsDefaultOrEmpty) { AddSubstitution(ref substitution, tp1, t2); return true; } if (t1.CustomModifiers.SequenceEqual(t2.CustomModifiers)) { AddSubstitution(ref substitution, tp1, new TypeWithModifiers(t2.Type)); return true; } if (t1.CustomModifiers.Length < t2.CustomModifiers.Length && t1.CustomModifiers.SequenceEqual(t2.CustomModifiers.Take(t1.CustomModifiers.Length))) { AddSubstitution(ref substitution, tp1, new TypeWithModifiers(t2.Type, ImmutableArray.Create(t2.CustomModifiers, t1.CustomModifiers.Length, t2.CustomModifiers.Length - t1.CustomModifiers.Length))); return true; } if (t2.Type.IsTypeParameter()) { var tp2 = (TypeParameterSymbol)t2.Type; if (t2.CustomModifiers.IsDefaultOrEmpty) { AddSubstitution(ref substitution, tp2, t1); return true; } if (t2.CustomModifiers.Length < t1.CustomModifiers.Length && t2.CustomModifiers.SequenceEqual(t1.CustomModifiers.Take(t2.CustomModifiers.Length))) { AddSubstitution(ref substitution, tp2, new TypeWithModifiers(t1.Type, ImmutableArray.Create(t1.CustomModifiers, t2.CustomModifiers.Length, t1.CustomModifiers.Length - t2.CustomModifiers.Length))); return true; } } return false; } default: { return t1 == t2; } } }
private static void AddSubstitution(ref MutableTypeMap substitution, TypeParameterSymbol tp1, TypeWithModifiers t2) { if (substitution == null) { substitution = new MutableTypeMap(); } // MutableTypeMap.Add will throw if the key has already been added. However, // if t1 was already in the substitution, it would have been substituted at the // start of CanUnifyHelper and we wouldn't be here. substitution.Add(tp1, t2); }
private ArrayTypeSymbol SubstituteArrayType(ArrayTypeSymbol t) { var oldElement = new TypeWithModifiers(t.ElementType, t.CustomModifiers); TypeWithModifiers element = oldElement.SubstituteType(this); if (element == oldElement) { return t; } if (t.IsSZArray) { ImmutableArray<NamedTypeSymbol> interfaces = t.Interfaces; //.InterfacesNoUseSiteDiagnostics(); Debug.Assert(0 <= interfaces.Length && interfaces.Length <= 2); if (interfaces.Length == 1) { Debug.Assert(interfaces[0] is NamedTypeSymbol); // IList<T> interfaces = ImmutableArray.Create<NamedTypeSymbol>((NamedTypeSymbol)SubstituteType(interfaces[0]).AsTypeSymbolOnly()); } else if (interfaces.Length == 2) { Debug.Assert(interfaces[0] is NamedTypeSymbol); // IList<T> interfaces = ImmutableArray.Create<NamedTypeSymbol>((NamedTypeSymbol)SubstituteType(interfaces[0]).AsTypeSymbolOnly(), (NamedTypeSymbol)SubstituteType(interfaces[1]).AsTypeSymbolOnly()); } else if (interfaces.Length != 0) { throw ExceptionUtilities.Unreachable; } return ArrayTypeSymbol.CreateSZArray( element.Type, t.BaseType, //.BaseTypeNoUseSiteDiagnostics, interfaces, element.CustomModifiers); } return ArrayTypeSymbol.CreateMDArray( element.Type, t.Rank, t.Sizes, t.LowerBounds, t.BaseType, //.BaseTypeNoUseSiteDiagnostics, element.CustomModifiers); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> /// <param name="t1">LHS</param> /// <param name="t2">RHS</param> /// <param name="substitution"> /// Substitutions performed so far (or null for none). /// Keys are type parameters, values are types (possibly type parameters). /// Will be updated with new substitutions by the callee. /// Should be ignored when false is returned. /// </param> /// <param name="untouchables"> /// Set of type symbols that cannot be replaced by substitution. /// </param> /// <returns>True if there exists a type map such that Map(LHS) == Map(RHS).</returns> /// <remarks> /// Derived from Dev10's BSYMMGR::UnifyTypes. /// Two types will not unify if they have different custom modifiers. /// </remarks> private static bool CanUnifyHelper(TypeWithModifiers t1, TypeWithModifiers t2, ref MutableTypeMap substitution, ImmutableHashSet <TypeParameterSymbol> untouchables) { if (t1 == t2) { return(true); } else if ((object)t1.Type == null || (object)t2.Type == null) { // Can't both be null or they would have been equal return(false); } if (substitution != null) { t1 = t1.SubstituteType(substitution); t2 = t2.SubstituteType(substitution); } // If one of the types is a type parameter, then the substitution could make them equal. if (t1 == t2) { return(true); } // We can avoid a lot of redundant checks if we ensure that we only have to check // for type parameters on the LHS if (!t1.Type.IsTypeParameter() && t2.Type.IsTypeParameter()) { TypeWithModifiers tmp = t1; t1 = t2; t2 = tmp; } // If t1 is not a type parameter, then neither is t2 Debug.Assert(t1.Type.IsTypeParameter() || !t2.Type.IsTypeParameter()); switch (t1.Type.Kind) { case SymbolKind.ArrayType: { if (t2.Type.TypeKind != t1.Type.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return(false); } ArrayTypeSymbol at1 = (ArrayTypeSymbol)t1.Type; ArrayTypeSymbol at2 = (ArrayTypeSymbol)t2.Type; if (!at1.HasSameShapeAs(at2)) { return(false); } return(CanUnifyHelper(new TypeWithModifiers(at1.ElementType, at1.CustomModifiers), new TypeWithModifiers(at2.ElementType, at2.CustomModifiers), ref substitution, untouchables)); } case SymbolKind.PointerType: { if (t2.Type.TypeKind != t1.Type.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return(false); } PointerTypeSymbol pt1 = (PointerTypeSymbol)t1.Type; PointerTypeSymbol pt2 = (PointerTypeSymbol)t2.Type; return(CanUnifyHelper(new TypeWithModifiers(pt1.PointedAtType, pt1.CustomModifiers), new TypeWithModifiers(pt2.PointedAtType, pt2.CustomModifiers), ref substitution, untouchables)); } case SymbolKind.NamedType: case SymbolKind.ErrorType: { if (t2.Type.TypeKind != t1.Type.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return(false); } NamedTypeSymbol nt1 = (NamedTypeSymbol)t1.Type; NamedTypeSymbol nt2 = (NamedTypeSymbol)t2.Type; if (nt1.IsTupleType) { if (!nt2.IsTupleType) { return(false); } return(CanUnifyHelper(new TypeWithModifiers(nt1.TupleUnderlyingType), new TypeWithModifiers(nt2.TupleUnderlyingType), ref substitution, untouchables)); } if (!nt1.IsGenericType) { return(!nt2.IsGenericType && nt1 == nt2); } else if (!nt2.IsGenericType) { return(false); } int arity = nt1.Arity; if (nt2.Arity != arity || nt2.OriginalDefinition != nt1.OriginalDefinition) { return(false); } var nt1Arguments = nt1.TypeArgumentsNoUseSiteDiagnostics; var nt2Arguments = nt2.TypeArgumentsNoUseSiteDiagnostics; var nt1HasModifiers = nt1.HasTypeArgumentsCustomModifiers; var nt2HasModifiers = nt2.HasTypeArgumentsCustomModifiers; for (int i = 0; i < arity; i++) { if (!CanUnifyHelper(new TypeWithModifiers(nt1Arguments[i], nt1HasModifiers ? nt1.GetTypeArgumentCustomModifiers(i) : default(ImmutableArray <CustomModifier>)), new TypeWithModifiers(nt2Arguments[i], nt2HasModifiers ? nt2.GetTypeArgumentCustomModifiers(i) : default(ImmutableArray <CustomModifier>)), ref substitution, untouchables)) { return(false); } } // Note: Dev10 folds this into the loop since GetTypeArgsAll includes type args for containing types // TODO: Calling CanUnifyHelper for the containing type is an overkill, we simply need to go through type arguments for all containers. return((object)nt1.ContainingType == null || CanUnifyHelper(new TypeWithModifiers(nt1.ContainingType), new TypeWithModifiers(nt2.ContainingType), ref substitution, untouchables)); } case SymbolKind.TypeParameter: { // These substitutions are not allowed in C# if (t2.Type.TypeKind == TypeKind.Pointer || t2.Type.SpecialType == SpecialType.System_Void) { return(false); } TypeParameterSymbol tp1 = (TypeParameterSymbol)t1.Type; // Perform the "occurs check" - i.e. ensure that t2 doesn't contain t1 to avoid recursive types // Note: t2 can't be the same type param - we would have caught that with ReferenceEquals above if (Contains(t2.Type, tp1)) { return(false); } // @MattWindsor91 (Concept-C# 2017) // Quickfix to make sure that, when there are two TPs // to be unified, and both are associated, we //var isAssocFlowingInwards = // t2.Type.IsTypeParameter() // && ((TypeParameterSymbol)t2.Type).IsAssociatedType // && tp1.IsAssociatedType; if (!untouchables.Contains(tp1)) // && !isAssocFlowingInwards) { if (t1.CustomModifiers.IsDefaultOrEmpty) { AddSubstitution(ref substitution, tp1, t2); return(true); } if (t1.CustomModifiers.SequenceEqual(t2.CustomModifiers)) { AddSubstitution(ref substitution, tp1, new TypeWithModifiers(t2.Type)); return(true); } if (t1.CustomModifiers.Length < t2.CustomModifiers.Length && t1.CustomModifiers.SequenceEqual(t2.CustomModifiers.Take(t1.CustomModifiers.Length))) { AddSubstitution(ref substitution, tp1, new TypeWithModifiers(t2.Type, ImmutableArray.Create(t2.CustomModifiers, t1.CustomModifiers.Length, t2.CustomModifiers.Length - t1.CustomModifiers.Length))); return(true); } } if (t2.Type.IsTypeParameter()) { var tp2 = (TypeParameterSymbol)t2.Type; if (!untouchables.Contains(tp2)) { if (t2.CustomModifiers.IsDefaultOrEmpty) { AddSubstitution(ref substitution, tp2, t1); return(true); } if (t2.CustomModifiers.Length < t1.CustomModifiers.Length && t2.CustomModifiers.SequenceEqual(t1.CustomModifiers.Take(t2.CustomModifiers.Length))) { AddSubstitution(ref substitution, tp2, new TypeWithModifiers(t1.Type, ImmutableArray.Create(t1.CustomModifiers, t2.CustomModifiers.Length, t1.CustomModifiers.Length - t2.CustomModifiers.Length))); return(true); } } } return(false); } default: { return(t1 == t2); } } }
private static void AddSubstitution(ref MutableTypeMap substitution, TypeParameterSymbol tp1, TypeWithModifiers t2) { if (substitution == null) { substitution = new MutableTypeMap(); } // MutableTypeMap.Add will throw if the key has already been added. However, // if t1 was already in the substitution, it would have been substituted at the // start of CanUnifyHelper and we wouldn't be here. // @MattWindsor91 (Concept-C# 2017) // use AddAndPropagate here to avoid denormalised typemaps. substitution.AddAndPropagate(tp1, t2); }
private PointerTypeSymbol SubstitutePointerType(PointerTypeSymbol t) { var oldPointedAtType = new TypeWithModifiers(t.PointedAtType, t.CustomModifiers); TypeWithModifiers pointedAtType = oldPointedAtType.SubstituteType(this); if (pointedAtType == oldPointedAtType) { return t; } return new PointerTypeSymbol(pointedAtType.Type, pointedAtType.CustomModifiers); }
/// <summary> /// Same as <see cref="SubstituteType"/>, but with special behavior around tuples. /// In particular, if substitution makes type tuple compatible, transform it into a tuple type. /// </summary> internal TypeWithModifiers SubstituteTypeWithTupleUnification(TypeSymbol previous) { TypeWithModifiers result = SubstituteType(previous); // Make it a tuple if it became compatible with one. if ((object)result.Type != null && !previous.IsTupleCompatible()) { var possiblyTuple = TupleTypeSymbol.TransformToTupleIfCompatible(result.Type); if ((object)result.Type != possiblyTuple) { result = new TypeWithModifiers(possiblyTuple, result.CustomModifiers); } } return result; }
internal void Add(TypeParameterSymbol key, TypeWithModifiers value) { this.Mapping.Add(key, value); }
private static bool ReturnTypesMatch(MethodSymbol candidateMethod, TypeMap candidateMethodTypeMap, ref ParamInfo<TypeSymbol> targetReturnParam) { Debug.Assert(candidateMethodTypeMap != null); if (candidateMethod.ReturnsByRef != targetReturnParam.IsByRef) { return false; } TypeSymbol candidateReturnType = candidateMethod.ReturnType; TypeSymbol targetReturnType = targetReturnParam.Type; // CONSIDER: Do we want to add special handling for error types? Right now, we expect they'll just fail to match. var substituted = new TypeWithModifiers(candidateReturnType, candidateMethod.ReturnTypeCustomModifiers).SubstituteType(candidateMethodTypeMap); if (substituted.Type != targetReturnType) { return false; } if (!CustomModifiersMatch(substituted.CustomModifiers, targetReturnParam.CustomModifiers) || !CustomModifiersMatch(candidateMethodTypeMap.SubstituteCustomModifiers(candidateMethod.RefCustomModifiers), targetReturnParam.RefCustomModifiers)) { return false; } return true; }
private static bool ParametersMatch(ParameterSymbol candidateParam, TypeMap candidateMethodTypeMap, ref ParamInfo<TypeSymbol> targetParam) { Debug.Assert(candidateMethodTypeMap != null); // This could be combined into a single return statement with a more complicated expression, but that would // be harder to debug. if ((candidateParam.RefKind != RefKind.None) != targetParam.IsByRef) { return false; } // CONSIDER: Do we want to add special handling for error types? Right now, we expect they'll just fail to match. var substituted = new TypeWithModifiers(candidateParam.Type, candidateParam.CustomModifiers).SubstituteType(candidateMethodTypeMap); if (substituted.Type != targetParam.Type) { return false; } if (!CustomModifiersMatch(substituted.CustomModifiers, targetParam.CustomModifiers) || !CustomModifiersMatch(candidateMethodTypeMap.SubstituteCustomModifiers(candidateParam.RefCustomModifiers), targetParam.RefCustomModifiers)) { return false; } return true; }
private TypeSymbol SubstituteNonNullableType(NonNullableReferenceTypeSymbol t) { var oldUnderlyingType = new TypeWithModifiers(t.UnderlyingType); TypeWithModifiers underlyingType = oldUnderlyingType.SubstituteType(this); if (underlyingType == oldUnderlyingType) { return t; } if (underlyingType.Type.IsValueType) return underlyingType.Type; return underlyingType.Type as NonNullableReferenceTypeSymbol ?? NonNullableReferenceTypeSymbol.CreateNonNullableReference(underlyingType.Type); }