public static IGenericContext CreateCustomMap(IMethod methodDefinition, IMethod methodSpecialization, IMethod additionalMethodDefinition = null) { var context = new MetadataGenericContext(); var customTypeSubstitution = new MutableTypeMap(); var methodSpecAdapter = methodSpecialization as MetadataMethodAdapter; if (methodSpecAdapter != null) { var methodSymbolSpec = methodSpecAdapter.MethodDef; AppendMapping(customTypeSubstitution, methodSymbolSpec); } var methodDefAdapter = methodDefinition as MetadataMethodAdapter; if (methodDefAdapter != null) { var methodSymbolDef = methodDefAdapter.MethodDef; AppendMapping(customTypeSubstitution, methodSymbolDef, true); } if (additionalMethodDefinition != null) { var metadataMethodAdapter = additionalMethodDefinition as MetadataMethodAdapter; if (metadataMethodAdapter != null && methodSpecAdapter != null) { var additionalMethodSymbolDef = metadataMethodAdapter.MethodDef; AppendMethodDirectMapping(customTypeSubstitution, methodSpecAdapter.MethodDef, additionalMethodSymbolDef); } } context.CustomTypeSubstitution = customTypeSubstitution; return(context); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> public static bool CanUnify(TypeSymbol t1, TypeSymbol t2) { MutableTypeMap substitution = null; bool result = CanUnifyHelper(t1, t2, ref substitution); Debug.Assert(!result || (substitution == null && t1 == t2) || substitution.SubstituteType(t1) == substitution.SubstituteType(t2)); return(result); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> public static bool CanUnify(TypeSymbol t1, TypeSymbol t2) { MutableTypeMap substitution = null; bool result = CanUnifyHelper(t1, t2, ref substitution); #if DEBUG Debug.Assert(!result || SubstituteAllTypeParameters(substitution, t1) == SubstituteAllTypeParameters(substitution, t2)); #endif return(result); }
private static void AddSubstitution(ref MutableTypeMap?substitution, TypeParameterSymbol tp1, TypeWithAnnotations 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 static void AppendMethodDirectMapping(MutableTypeMap customTypeSubstitution, MethodSymbol methodSymbolSpec, MethodSymbol methodSymbolDef) { for (var i = 0; i < methodSymbolSpec.TypeParameters.Length; i++) { var typeParameterSymbol = methodSymbolDef.TypeParameters[i]; var typeArgument = methodSymbolSpec.TypeArguments[i]; if (!ReferenceEquals(typeParameterSymbol, typeArgument)) { customTypeSubstitution.Add(typeParameterSymbol, typeArgument); } } }
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); }
/// <summary> /// Tries to produce a substitution of type parameters that will make /// two types identical. /// </summary> public static bool CanUnify(TypeSymbol t1, TypeSymbol t2, ref MutableTypeMap substitution, ImmutableHashSet <TypeParameterSymbol> untouchables) { if (t1 == t2) { return(true); } bool result = CanUnifyHelper((object)t1 == null ? default(TypeWithModifiers) : new TypeWithModifiers(t1), (object)t2 == null ? default(TypeWithModifiers) : new TypeWithModifiers(t2), ref substitution, untouchables); #if DEBUG Debug.Assert(!result || SubstituteAllTypeParameters(substitution, new TypeWithModifiers(t1)).Equals(SubstituteAllTypeParameters(substitution, new TypeWithModifiers(t2)), TypeCompareKind.IgnoreTupleNames)); #endif return(result); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> public static bool CanUnify(TypeSymbol t1, TypeSymbol t2) { if (t1 == t2) { return(true); } MutableTypeMap substitution = null; bool result = CanUnifyHelper((object)t1 == null ? default(TypeWithModifiers) : new TypeWithModifiers(t1), (object)t2 == null ? default(TypeWithModifiers) : new TypeWithModifiers(t2), ref substitution); #if DEBUG Debug.Assert(!result || SubstituteAllTypeParameters(substitution, new TypeWithModifiers(t1)) == SubstituteAllTypeParameters(substitution, new TypeWithModifiers(t2))); #endif return(result); }
private static void AppendMapping(MutableTypeMap customTypeSubstitution, NamedTypeSymbol namedTypeSymbol, bool invert = false) { for (var i = 0; i < namedTypeSymbol.TypeParameters.Length; i++) { var typeParameterSymbol = namedTypeSymbol.TypeParameters[i]; var typeArgument = namedTypeSymbol.TypeArguments[i]; if (!ReferenceEquals(typeParameterSymbol, typeArgument)) { if (invert) { Debug.Assert(typeArgument is TypeParameterSymbol, "TypeParameterSymbol is required"); customTypeSubstitution.Add(typeArgument as TypeParameterSymbol, customTypeSubstitution.SubstituteType(typeParameterSymbol)); } else { customTypeSubstitution.Add(typeParameterSymbol, typeArgument); } } } }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> public static bool CanUnify(TypeSymbol t1, TypeSymbol t2) { if (TypeSymbol.Equals(t1, t2, TypeCompareKind.ConsiderEverything2)) { return(true); } MutableTypeMap substitution = null; bool result = CanUnifyHelper(t1, t2, ref substitution); #if DEBUG if (result && ((object)t1 != null && (object)t2 != null)) { var substituted1 = SubstituteAllTypeParameters(substitution, TypeSymbolWithAnnotations.Create(t1)); var substituted2 = SubstituteAllTypeParameters(substitution, TypeSymbolWithAnnotations.Create(t2)); Debug.Assert(substituted1.TypeSymbol.Equals(substituted2.TypeSymbol, TypeCompareKind.IgnoreTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); Debug.Assert(substituted1.CustomModifiers.SequenceEqual(substituted2.CustomModifiers)); } #endif return(result); }
/// <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(TypeSymbolWithAnnotations t1, TypeSymbolWithAnnotations t2, ref MutableTypeMap substitution) { if (t1.IsNull || t2.IsNull) { return(t1.IsSameAs(t2)); } if (TypeSymbol.Equals(t1.TypeSymbol, t2.TypeSymbol, TypeCompareKind.ConsiderEverything2) && t1.CustomModifiers.SequenceEqual(t2.CustomModifiers)) { return(true); } 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 (TypeSymbol.Equals(t1.TypeSymbol, t2.TypeSymbol, TypeCompareKind.ConsiderEverything2) && t1.CustomModifiers.SequenceEqual(t2.CustomModifiers)) { 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.TypeSymbol.IsTypeParameter() && t2.TypeSymbol.IsTypeParameter()) { TypeSymbolWithAnnotations tmp = t1; t1 = t2; t2 = tmp; } // If t1 is not a type parameter, then neither is t2 Debug.Assert(t1.TypeSymbol.IsTypeParameter() || !t2.TypeSymbol.IsTypeParameter()); switch (t1.Kind) { case SymbolKind.ArrayType: { if (t2.TypeKind != t1.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return(false); } ArrayTypeSymbol at1 = (ArrayTypeSymbol)t1.TypeSymbol; ArrayTypeSymbol at2 = (ArrayTypeSymbol)t2.TypeSymbol; return(CanUnifyHelper(at1.ElementType, at2.ElementType, ref substitution)); } case SymbolKind.PointerType: { if (t2.TypeKind != t1.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return(false); } PointerTypeSymbol pt1 = (PointerTypeSymbol)t1.TypeSymbol; PointerTypeSymbol pt2 = (PointerTypeSymbol)t2.TypeSymbol; return(CanUnifyHelper(pt1.PointedAtType, pt2.PointedAtType, ref substitution)); } case SymbolKind.NamedType: case SymbolKind.ErrorType: { if (t2.TypeKind != t1.TypeKind || !t2.CustomModifiers.SequenceEqual(t1.CustomModifiers)) { return(false); } NamedTypeSymbol nt1 = (NamedTypeSymbol)t1.TypeSymbol; NamedTypeSymbol nt2 = (NamedTypeSymbol)t2.TypeSymbol; if (nt1.IsTupleType) { if (!nt2.IsTupleType) { return(false); } return(CanUnifyHelper(nt1.TupleUnderlyingType, nt2.TupleUnderlyingType, ref substitution)); } if (!nt1.IsGenericType) { return(!nt2.IsGenericType && TypeSymbol.Equals(nt1, nt2, TypeCompareKind.ConsiderEverything2)); } else if (!nt2.IsGenericType) { return(false); } int arity = nt1.Arity; if (nt2.Arity != arity || !TypeSymbol.Equals(nt2.OriginalDefinition, nt1.OriginalDefinition, TypeCompareKind.ConsiderEverything2)) { return(false); } var nt1Arguments = nt1.TypeArgumentsNoUseSiteDiagnostics; var nt2Arguments = nt2.TypeArgumentsNoUseSiteDiagnostics; for (int i = 0; i < arity; i++) { if (!CanUnifyHelper(nt1Arguments[i], nt2Arguments[i], 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(nt1.ContainingType, nt2.ContainingType, ref substitution)); } case SymbolKind.TypeParameter: { // These substitutions are not allowed in C# if (t2.TypeKind == TypeKind.Pointer || t2.SpecialType == SpecialType.System_Void) { return(false); } TypeParameterSymbol tp1 = (TypeParameterSymbol)t1.TypeSymbol; // 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.TypeSymbol, tp1)) { return(false); } if (t1.CustomModifiers.IsDefaultOrEmpty) { AddSubstitution(ref substitution, tp1, t2); return(true); } if (t1.CustomModifiers.SequenceEqual(t2.CustomModifiers)) { AddSubstitution(ref substitution, tp1, TypeSymbolWithAnnotations.Create(t2.TypeSymbol)); return(true); } if (t1.CustomModifiers.Length < t2.CustomModifiers.Length && t1.CustomModifiers.SequenceEqual(t2.CustomModifiers.Take(t1.CustomModifiers.Length))) { AddSubstitution(ref substitution, tp1, TypeSymbolWithAnnotations.Create(t2.TypeSymbol, customModifiers: ImmutableArray.Create(t2.CustomModifiers, t1.CustomModifiers.Length, t2.CustomModifiers.Length - t1.CustomModifiers.Length))); return(true); } if (t2.TypeSymbol.IsTypeParameter()) { var tp2 = (TypeParameterSymbol)t2.TypeSymbol; 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, TypeSymbolWithAnnotations.Create(t1.TypeSymbol, customModifiers: ImmutableArray.Create(t1.CustomModifiers, t2.CustomModifiers.Length, t1.CustomModifiers.Length - t2.CustomModifiers.Length))); return(true); } } return(false); } default: { return(false); } } }
private static bool CanUnifyHelper(TypeSymbol t1, TypeSymbol t2, ref MutableTypeMap substitution) { return(CanUnifyHelper(TypeSymbolWithAnnotations.Create(t1), TypeSymbolWithAnnotations.Create(t2), ref substitution)); }
/// <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 subsitutions 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(TypeSymbol t1, TypeSymbol t2, ref MutableTypeMap substitution) { if (ReferenceEquals(t1, t2)) { return(true); } else if ((object)t1 == null || (object)t2 == null) { // Can't both be null or they would have been ReferenceEquals return(false); } if (substitution != null) { t1 = substitution.SubstituteType(t1); t2 = substitution.SubstituteType(t2); } // If one of the types is a type parameter, then the substitution could make them ReferenceEquals. if (ReferenceEquals(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.IsTypeParameter() && t2.IsTypeParameter()) { TypeSymbol tmp = t1; t1 = t2; t2 = tmp; } // If t1 is not a type parameter, then neither is t2 Debug.Assert(t1.IsTypeParameter() || !t2.IsTypeParameter()); switch (t1.Kind) { case SymbolKind.ArrayType: { if (t2.TypeKind != t1.TypeKind) { return(false); } ArrayTypeSymbol at1 = (ArrayTypeSymbol)t1; ArrayTypeSymbol at2 = (ArrayTypeSymbol)t2; if (at1.Rank != at2.Rank || !at1.CustomModifiers.SequenceEqual(at2.CustomModifiers)) { return(false); } return(CanUnifyHelper(at1.ElementType, at2.ElementType, ref substitution)); } case SymbolKind.PointerType: { if (t2.TypeKind != t1.TypeKind) { return(false); } PointerTypeSymbol pt1 = (PointerTypeSymbol)t1; PointerTypeSymbol pt2 = (PointerTypeSymbol)t2; if (!pt1.CustomModifiers.SequenceEqual(pt2.CustomModifiers)) { return(false); } return(CanUnifyHelper(pt1.PointedAtType, pt2.PointedAtType, ref substitution)); } case SymbolKind.NamedType: case SymbolKind.ErrorType: { if (t2.TypeKind != t1.TypeKind) { return(false); } NamedTypeSymbol nt1 = (NamedTypeSymbol)t1; NamedTypeSymbol nt2 = (NamedTypeSymbol)t2; 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); } for (int i = 0; i < arity; i++) { if (!CanUnifyHelper(nt1.TypeArgumentsNoUseSiteDiagnostics[i], nt2.TypeArgumentsNoUseSiteDiagnostics[i], ref substitution)) { return(false); } } // Note: Dev10 folds this into the loop since GetTypeArgsAll includes type args for containing types return((object)nt1.ContainingType == null || CanUnifyHelper(nt1.ContainingType, nt2.ContainingType, ref substitution)); } case SymbolKind.TypeParameter: { // These substitutions are not allowed in C# if (t2.TypeKind == TypeKind.Pointer || t2.SpecialType == SpecialType.System_Void) { return(false); } TypeParameterSymbol tp1 = (TypeParameterSymbol)t1; // 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, tp1)) { return(false); } 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 this method and we wouldn't be here. substitution.Add(tp1, t2); return(true); } default: { return(t1 == t2); } } }
/// <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); } } }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> public static bool CanUnify(TypeSymbol t1, TypeSymbol t2) { MutableTypeMap substitution = null; return(CanUnify(t1, t2, ref substitution, ImmutableHashSet <TypeParameterSymbol> .Empty)); }