/// <summary> /// Substitute types, and return the results without duplicates, preserving the original order. /// Note, all occurrences of 'dynamic' in resulting types will be replaced with 'object'. /// </summary> internal void SubstituteConstraintTypesDistinctWithoutModifiers( TypeParameterSymbol owner, ImmutableArray <TypeWithAnnotations> original, ArrayBuilder <TypeWithAnnotations> result, HashSet <TypeParameterSymbol> ignoreTypesDependentOnTypeParametersOpt) { DynamicTypeEraser dynamicEraser = null; if (original.Length == 0) { return; } else if (original.Length == 1) { var type = original[0]; if (ignoreTypesDependentOnTypeParametersOpt == null || !type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt)) { result.Add(substituteConstraintType(type)); } } else { var map = PooledDictionary <TypeSymbol, int> .GetInstance(); foreach (var type in original) { if (ignoreTypesDependentOnTypeParametersOpt == null || !type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt)) { var substituted = substituteConstraintType(type); if (!map.TryGetValue(substituted.Type, out int mergeWith)) { map.Add(substituted.Type, result.Count); result.Add(substituted); } else { result[mergeWith] = ConstraintsHelper.ConstraintWithMostSignificantNullability(result[mergeWith], substituted); } } } map.Free(); } TypeWithAnnotations substituteConstraintType(TypeWithAnnotations type) { if (dynamicEraser == null) { dynamicEraser = new DynamicTypeEraser(owner.ContainingAssembly.CorLibrary.GetSpecialType(SpecialType.System_Object)); } TypeWithAnnotations substituted = SubstituteType(type); return(substituted.WithTypeAndModifiers(dynamicEraser.EraseDynamic(substituted.Type), substituted.CustomModifiers)); } }
// Based on SymbolLoader::ResolveBounds. public static TypeParameterBounds ResolveBounds( this TypeParameterSymbol typeParameter, AssemblySymbol corLibrary, ConsList <TypeParameterSymbol> inProgress, ImmutableArray <TypeSymbol> constraintTypes, bool inherited, CSharpCompilation currentCompilation, ArrayBuilder <TypeParameterDiagnosticInfo> diagnosticsBuilder, ref ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder) { Debug.Assert(currentCompilation == null || typeParameter.IsFromCompilation(currentCompilation)); ImmutableArray <NamedTypeSymbol> interfaces; NamedTypeSymbol effectiveBaseClass = corLibrary.GetSpecialType(typeParameter.HasValueTypeConstraint ? SpecialType.System_ValueType : SpecialType.System_Object); TypeSymbol deducedBaseType = effectiveBaseClass; DynamicTypeEraser dynamicEraser = null; if (constraintTypes.Length == 0) { interfaces = ImmutableArray <NamedTypeSymbol> .Empty; } else { var constraintTypesBuilder = ArrayBuilder <TypeSymbol> .GetInstance(); var interfacesBuilder = ArrayBuilder <NamedTypeSymbol> .GetInstance(); var conversions = new TypeConversions(corLibrary); HashSet <DiagnosticInfo> useSiteDiagnostics = null; // Resolve base types, determine the effective base class and // interfaces, and filter out any constraint types that cause cycles. foreach (var constraintType in constraintTypes) { NamedTypeSymbol constraintEffectiveBase; TypeSymbol constraintDeducedBase; switch (constraintType.TypeKind) { case TypeKind.Dynamic: Debug.Assert(inherited || currentCompilation == null); continue; case TypeKind.TypeParameter: { var containingSymbol = typeParameter.ContainingSymbol; var constraintTypeParameter = (TypeParameterSymbol)constraintType; ConsList <TypeParameterSymbol> constraintsInProgress; if (constraintTypeParameter.ContainingSymbol == containingSymbol) { // The constraint type parameter is from the same containing type or method. if (inProgress.ContainsReference(constraintTypeParameter)) { // "Circular constraint dependency involving '{0}' and '{1}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(constraintTypeParameter, new CSDiagnosticInfo(ErrorCode.ERR_CircularConstraint, constraintTypeParameter, typeParameter))); continue; } constraintsInProgress = inProgress; } else { // The constraint type parameter is from a different containing symbol so no cycle. constraintsInProgress = ConsList <TypeParameterSymbol> .Empty; } // Use the calculated bounds from the constraint type parameter. constraintEffectiveBase = constraintTypeParameter.GetEffectiveBaseClass(constraintsInProgress); constraintDeducedBase = constraintTypeParameter.GetDeducedBaseType(constraintsInProgress); AddInterfaces(interfacesBuilder, constraintTypeParameter.GetInterfaces(constraintsInProgress)); if (constraintTypeParameter.HasValueTypeConstraint && !inherited && currentCompilation != null && constraintTypeParameter.IsFromCompilation(currentCompilation)) { // "Type parameter '{1}' has the 'struct' constraint so '{1}' cannot be used as a constraint for '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ConWithValCon, typeParameter, constraintTypeParameter))); continue; } } break; case TypeKind.Interface: case TypeKind.Class: case TypeKind.Delegate: NamedTypeSymbol erasedConstraintType; if (inherited || currentCompilation == null) { // only inherited constraints may contain dynamic if (dynamicEraser == null) { dynamicEraser = new DynamicTypeEraser(corLibrary.GetSpecialType(SpecialType.System_Object)); } erasedConstraintType = (NamedTypeSymbol)dynamicEraser.EraseDynamic(constraintType); } else { Debug.Assert(!constraintType.ContainsDynamic()); Debug.Assert(constraintType.TypeKind != TypeKind.Delegate); erasedConstraintType = (NamedTypeSymbol)constraintType; } if (constraintType.IsInterfaceType()) { AddInterface(interfacesBuilder, erasedConstraintType); constraintTypesBuilder.Add(constraintType); continue; } else { constraintEffectiveBase = erasedConstraintType; constraintDeducedBase = constraintType; break; } case TypeKind.Struct: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_ValueType); constraintDeducedBase = constraintType; break; case TypeKind.Enum: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_Enum); constraintDeducedBase = constraintType; break; case TypeKind.Array: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_Array); constraintDeducedBase = constraintType; break; case TypeKind.Error: constraintEffectiveBase = (NamedTypeSymbol)constraintType; constraintDeducedBase = constraintType; break; case TypeKind.Submission: default: throw ExceptionUtilities.UnexpectedValue(constraintType.TypeKind); } CheckEffectiveAndDeducedBaseTypes(conversions, constraintEffectiveBase, constraintDeducedBase); constraintTypesBuilder.Add(constraintType); // Determine the more encompassed of the current effective base // class and the previously computed effective base class. if (!deducedBaseType.IsErrorType() && !constraintDeducedBase.IsErrorType()) { if (!IsEncompassedBy(conversions, deducedBaseType, constraintDeducedBase, ref useSiteDiagnostics)) { if (!IsEncompassedBy(conversions, constraintDeducedBase, deducedBaseType, ref useSiteDiagnostics)) { // "Type parameter '{0}' inherits conflicting constraints '{1}' and '{2}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BaseConstraintConflict, typeParameter, constraintDeducedBase, deducedBaseType))); } else { deducedBaseType = constraintDeducedBase; effectiveBaseClass = constraintEffectiveBase; } } } } AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder); CheckEffectiveAndDeducedBaseTypes(conversions, effectiveBaseClass, deducedBaseType); constraintTypes = constraintTypesBuilder.ToImmutableAndFree(); interfaces = interfacesBuilder.ToImmutableAndFree(); } Debug.Assert((effectiveBaseClass.SpecialType == SpecialType.System_Object) || (deducedBaseType.SpecialType != SpecialType.System_Object)); // Only create a TypeParameterBounds instance for this type // parameter if the bounds are not the default values. if ((constraintTypes.Length == 0) && (deducedBaseType.SpecialType == SpecialType.System_Object)) { Debug.Assert(effectiveBaseClass.SpecialType == SpecialType.System_Object); Debug.Assert(interfaces.Length == 0); return(null); } var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType); // Additional constraint checks for overrides. if (inherited) { CheckOverrideConstraints(typeParameter, bounds, diagnosticsBuilder); } return(bounds); }
// Based on SymbolLoader::ResolveBounds. public static TypeParameterBounds ResolveBounds( this TypeParameterSymbol typeParameter, AssemblySymbol corLibrary, ConsList<TypeParameterSymbol> inProgress, ImmutableArray<TypeSymbol> constraintTypes, bool inherited, CSharpCompilation currentCompilation, ArrayBuilder<TypeParameterDiagnosticInfo> diagnosticsBuilder, ref ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder) { Debug.Assert(currentCompilation == null || typeParameter.IsFromCompilation(currentCompilation)); ImmutableArray<NamedTypeSymbol> interfaces; NamedTypeSymbol effectiveBaseClass = corLibrary.GetSpecialType(typeParameter.HasValueTypeConstraint ? SpecialType.System_ValueType : SpecialType.System_Object); TypeSymbol deducedBaseType = effectiveBaseClass; DynamicTypeEraser dynamicEraser = null; if (constraintTypes.Length == 0) { interfaces = ImmutableArray<NamedTypeSymbol>.Empty; } else { var constraintTypesBuilder = ArrayBuilder<TypeSymbol>.GetInstance(); var interfacesBuilder = ArrayBuilder<NamedTypeSymbol>.GetInstance(); var conversions = new TypeConversions(corLibrary); HashSet<DiagnosticInfo> useSiteDiagnostics = null; // Resolve base types, determine the effective base class and // interfaces, and filter out any constraint types that cause cycles. foreach (var constraintType in constraintTypes) { NamedTypeSymbol constraintEffectiveBase; TypeSymbol constraintDeducedBase; switch (constraintType.TypeKind) { case TypeKind.Dynamic: Debug.Assert(inherited || currentCompilation == null); continue; case TypeKind.TypeParameter: { var containingSymbol = typeParameter.ContainingSymbol; var constraintTypeParameter = (TypeParameterSymbol)constraintType; ConsList<TypeParameterSymbol> constraintsInProgress; if (constraintTypeParameter.ContainingSymbol == containingSymbol) { // The constraint type parameter is from the same containing type or method. if (inProgress.ContainsReference(constraintTypeParameter)) { // "Circular constraint dependency involving '{0}' and '{1}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(constraintTypeParameter, new CSDiagnosticInfo(ErrorCode.ERR_CircularConstraint, constraintTypeParameter, typeParameter))); continue; } constraintsInProgress = inProgress; } else { // The constraint type parameter is from a different containing symbol so no cycle. constraintsInProgress = ConsList<TypeParameterSymbol>.Empty; } // Use the calculated bounds from the constraint type parameter. constraintEffectiveBase = constraintTypeParameter.GetEffectiveBaseClass(constraintsInProgress); constraintDeducedBase = constraintTypeParameter.GetDeducedBaseType(constraintsInProgress); AddInterfaces(interfacesBuilder, constraintTypeParameter.GetInterfaces(constraintsInProgress)); if (constraintTypeParameter.HasValueTypeConstraint && !inherited && currentCompilation != null && constraintTypeParameter.IsFromCompilation(currentCompilation)) { // "Type parameter '{1}' has the 'struct' constraint so '{1}' cannot be used as a constraint for '{0}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ConWithValCon, typeParameter, constraintTypeParameter))); continue; } } break; case TypeKind.Interface: case TypeKind.Class: case TypeKind.Delegate: NamedTypeSymbol erasedConstraintType; if (inherited || currentCompilation == null) { // only inherited constraints may contain dynamic if (dynamicEraser == null) { dynamicEraser = new DynamicTypeEraser(corLibrary.GetSpecialType(SpecialType.System_Object)); } erasedConstraintType = (NamedTypeSymbol)dynamicEraser.EraseDynamic(constraintType); } else { Debug.Assert(!constraintType.ContainsDynamic()); Debug.Assert(constraintType.TypeKind != TypeKind.Delegate); erasedConstraintType = (NamedTypeSymbol)constraintType; } if (constraintType.IsInterfaceType()) { AddInterface(interfacesBuilder, erasedConstraintType); constraintTypesBuilder.Add(constraintType); continue; } else { constraintEffectiveBase = erasedConstraintType; constraintDeducedBase = constraintType; break; } case TypeKind.Struct: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_ValueType); constraintDeducedBase = constraintType; break; case TypeKind.Enum: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_Enum); constraintDeducedBase = constraintType; break; case TypeKind.Array: Debug.Assert(inherited || currentCompilation == null); constraintEffectiveBase = corLibrary.GetSpecialType(SpecialType.System_Array); constraintDeducedBase = constraintType; break; case TypeKind.Error: constraintEffectiveBase = (NamedTypeSymbol)constraintType; constraintDeducedBase = constraintType; break; case TypeKind.Submission: default: throw ExceptionUtilities.UnexpectedValue(constraintType.TypeKind); } CheckEffectiveAndDeducedBaseTypes(conversions, constraintEffectiveBase, constraintDeducedBase); constraintTypesBuilder.Add(constraintType); // Determine the more encompassed of the current effective base // class and the previously computed effective base class. if (!deducedBaseType.IsErrorType() && !constraintDeducedBase.IsErrorType()) { if (!IsEncompassedBy(conversions, deducedBaseType, constraintDeducedBase, ref useSiteDiagnostics)) { if (!IsEncompassedBy(conversions, constraintDeducedBase, deducedBaseType, ref useSiteDiagnostics)) { // "Type parameter '{0}' inherits conflicting constraints '{1}' and '{2}'" diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BaseConstraintConflict, typeParameter, constraintDeducedBase, deducedBaseType))); } else { deducedBaseType = constraintDeducedBase; effectiveBaseClass = constraintEffectiveBase; } } } } AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder); CheckEffectiveAndDeducedBaseTypes(conversions, effectiveBaseClass, deducedBaseType); constraintTypes = constraintTypesBuilder.ToImmutableAndFree(); interfaces = interfacesBuilder.ToImmutableAndFree(); } Debug.Assert((effectiveBaseClass.SpecialType == SpecialType.System_Object) || (deducedBaseType.SpecialType != SpecialType.System_Object)); // Only create a TypeParameterBounds instance for this type // parameter if the bounds are not the default values. if ((constraintTypes.Length == 0) && (deducedBaseType.SpecialType == SpecialType.System_Object)) { Debug.Assert(effectiveBaseClass.SpecialType == SpecialType.System_Object); Debug.Assert(interfaces.Length == 0); return null; } var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType); // Additional constraint checks for overrides. if (inherited) { CheckOverrideConstraints(typeParameter, bounds, diagnosticsBuilder); } return bounds; }
public void DynamicTypeEraser() { string source = @" using System; using System.Collections.Generic; public struct A<S, T> { public enum E { A } public class B<R> { } } unsafe public class C<X> { public Func<A<dynamic, A<dynamic, bool>.E*[]>.B<X>, Dictionary<dynamic[], int>> F; } "; var compilation = CreateCompilationWithMscorlibAndSystemCore(source, compOptions: TestOptions.UnsafeDll); compilation.VerifyDiagnostics(); var c = compilation.GlobalNamespace.GetMember<TypeSymbol>("C"); var f = c.GetMember<FieldSymbol>("F"); var eraser = new DynamicTypeEraser(compilation.GetSpecialType(SpecialType.System_Object)); var erasedType = eraser.EraseDynamic(f.Type); Assert.Equal("System.Func<A<System.Object, A<System.Object, System.Boolean>.E*[]>.B<X>, System.Collections.Generic.Dictionary<System.Object[], System.Int32>>", erasedType.ToTestDisplayString()); }