diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs index 12eea0080e2d2..f1944cf6f8bd2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs @@ -92,7 +92,6 @@ private TypeParameterConstraintClause BindTypeParameterConstraints( { var constraints = TypeParameterConstraintKind.None; var constraintTypes = ArrayBuilder.GetInstance(); - var isStruct = false; for (int i = 0, n = constraintsSyntax.Count; i < n; i++) { @@ -113,14 +112,17 @@ private TypeParameterConstraintClause BindTypeParameterConstraints( diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } - isStruct = true; constraints |= TypeParameterConstraintKind.ValueType; continue; case SyntaxKind.ConstructorConstraint: - if (isStruct) + if ((constraints & TypeParameterConstraintKind.ValueType) != 0) { diagnostics.Add(ErrorCode.ERR_NewBoundWithVal, syntax.GetFirstToken().GetLocation()); } + if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0) + { + diagnostics.Add(ErrorCode.ERR_NewBoundWithUnmanaged, syntax.GetFirstToken().GetLocation()); + } if (i != n - 1) { @@ -133,48 +135,91 @@ private TypeParameterConstraintClause BindTypeParameterConstraints( { var typeConstraintSyntax = (TypeConstraintSyntax)syntax; var typeSyntax = typeConstraintSyntax.Type; - if (typeSyntax.Kind() != SyntaxKind.PredefinedType && !SyntaxFacts.IsName(typeSyntax.Kind())) + var typeSyntaxKind = typeSyntax.Kind(); + if (typeSyntaxKind != SyntaxKind.PredefinedType && typeSyntaxKind != SyntaxKind.PointerType && !SyntaxFacts.IsName(typeSyntax.Kind())) { diagnostics.Add(ErrorCode.ERR_BadConstraintType, typeSyntax.GetLocation()); } - var type = this.BindType(typeSyntax, diagnostics); + var type = BindTypeOrUnmanagedKeyword(typeSyntax, diagnostics, out var isUnmanaged); - // Only valid constraint types are included in ConstraintTypes - // since, in general, it may be difficult to support all invalid types. - // In the future, we may want to include some invalid types - // though so the public binding API has the most information. - if (!IsValidConstraintType(typeConstraintSyntax, type, diagnostics)) + if (isUnmanaged) { - continue; - } + if (constraints != 0 || constraintTypes.Any()) + { + diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, typeSyntax.GetLocation()); + continue; + } - if (constraintTypes.Contains(type)) - { - // "Duplicate constraint '{0}' for type parameter '{1}'" - Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type, name); + // This should produce diagnostics if the types are missing + GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType, diagnostics, typeSyntax); + GetSpecialType(SpecialType.System_ValueType, diagnostics, typeSyntax); + + constraints |= TypeParameterConstraintKind.Unmanaged; continue; } - - if (type.TypeKind == TypeKind.Class) + else { - // If there is already a struct or class constraint (class constraint could be - // 'class' or explicit type), report an error and drop this class. If we don't - // drop this additional class, we may end up with conflicting class constraints. - - if (constraintTypes.Count > 0) + // Only valid constraint types are included in ConstraintTypes + // since, in general, it may be difficult to support all invalid types. + // In the future, we may want to include some invalid types + // though so the public binding API has the most information. + if (!IsValidConstraintType(typeConstraintSyntax, type, diagnostics)) { - // "The class type constraint '{0}' must come before any other constraints" - Error(diagnostics, ErrorCode.ERR_ClassBoundNotFirst, syntax, type); continue; } - if ((constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != 0) + if (constraintTypes.Contains(type)) { - // "'{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint" - Error(diagnostics, ErrorCode.ERR_RefValBoundWithClass, syntax, type); + // "Duplicate constraint '{0}' for type parameter '{1}'" + Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type, name); continue; } + + if (type.TypeKind == TypeKind.Class) + { + // If there is already a struct or class constraint (class constraint could be + // 'class' or explicit type), report an error and drop this class. If we don't + // drop this additional class, we may end up with conflicting class constraints. + + if (constraintTypes.Count > 0) + { + // "The class type constraint '{0}' must come before any other constraints" + Error(diagnostics, ErrorCode.ERR_ClassBoundNotFirst, syntax, type); + continue; + } + + if ((constraints & (TypeParameterConstraintKind.ReferenceType)) != 0) + { + switch (type.SpecialType) + { + case SpecialType.System_Enum: + case SpecialType.System_Delegate: + case SpecialType.System_MulticastDelegate: + break; + + default: + // "'{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint" + Error(diagnostics, ErrorCode.ERR_RefValBoundWithClass, syntax, type); + continue; + } + } + else if (type.SpecialType != SpecialType.System_Enum) + { + if ((constraints & TypeParameterConstraintKind.ValueType) != 0) + { + // "'{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint" + Error(diagnostics, ErrorCode.ERR_RefValBoundWithClass, syntax, type); + continue; + } + else if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0) + { + // "'{0}': cannot specify both a constraint class and the 'unmanaged' constraint" + Error(diagnostics, ErrorCode.ERR_UnmanagedBoundWithClass, syntax, type); + continue; + } + } + } } constraintTypes.Add(type); @@ -196,11 +241,17 @@ private static bool IsValidConstraintType(TypeConstraintSyntax syntax, TypeSymbo { switch (type.SpecialType) { - case SpecialType.System_Object: - case SpecialType.System_ValueType: case SpecialType.System_Enum: + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureEnumGenericTypeConstraint, diagnostics); + break; + case SpecialType.System_Delegate: case SpecialType.System_MulticastDelegate: + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureDelegateGenericTypeConstraint, diagnostics); + break; + + case SpecialType.System_Object: + case SpecialType.System_ValueType: case SpecialType.System_Array: // "Constraint cannot be special class '{0}'" Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 665d34320bd10..6c00cf1268bcc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -2342,7 +2342,7 @@ private BoundExpression BindOutVariableDeclarationArgument( if (typeSyntax.IsVar) { var ignored = DiagnosticBag.GetInstance(); - BindTypeOrAlias(typeSyntax, ignored, out isVar); + BindTypeOrAliasOrVarKeyword(typeSyntax, ignored, out isVar); ignored.Free(); if (isVar) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 7633bfa95db1d..886f0c0518c71 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -245,7 +245,7 @@ private BoundPattern BindDeclarationPattern( bool isVar; AliasSymbol aliasOpt; - TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out aliasOpt); + TypeSymbol declType = BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar, out aliasOpt); if (isVar) { declType = operandType; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index f602707b68ddd..90a3934143175 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -640,7 +640,7 @@ declarationNode is VariableDesignationSyntax || // or it might not; if it is not then we do not want to report an error. If it is, then // we want to treat the declaration as an explicitly typed declaration. - TypeSymbol declType = BindType(typeSyntax.SkipRef(out _), diagnostics, out isVar, out alias); + TypeSymbol declType = BindTypeOrVarKeyword(typeSyntax.SkipRef(out _), diagnostics, out isVar, out alias); Debug.Assert((object)declType != null || isVar); if (isVar) @@ -2209,7 +2209,7 @@ internal BoundStatement BindForOrUsingOrFixedDeclarations(VariableDeclarationSyn AliasSymbol alias; bool isVar; - TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); + TypeSymbol declType = BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar, out alias); Debug.Assert((object)declType != null || isVar); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 1f655730c50fb..4bb8047510fed 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -29,13 +28,33 @@ internal partial class Binder /// Bound type if syntax binds to a type in the current context and /// null if syntax binds to "var" keyword in the current context. /// - internal TypeSymbol BindType(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isVar) + internal TypeSymbol BindTypeOrVarKeyword(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isVar) { - var symbol = BindTypeOrAlias(syntax, diagnostics, out isVar); + var symbol = BindTypeOrAliasOrVarKeyword(syntax, diagnostics, out isVar); Debug.Assert(isVar == ((object)symbol == null)); return isVar ? null : (TypeSymbol)UnwrapAlias(symbol, diagnostics, syntax); } + /// + /// Binds the type for the syntax taking into account possibility of "unmanaged" type. + /// + /// Type syntax to bind. + /// Diagnostics. + /// + /// Set to false if syntax binds to a type in the current context and true if + /// syntax is "unmanaged" and it binds to "unmanaged" keyword in the current context. + /// + /// + /// Bound type if syntax binds to a type in the current context and + /// null if syntax binds to "unmanaged" keyword in the current context. + /// + internal TypeSymbol BindTypeOrUnmanagedKeyword(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isUnmanaged) + { + var symbol = BindTypeOrAliasOrUnmanagedKeyword(syntax, diagnostics, out isUnmanaged); + Debug.Assert(isUnmanaged == ((object)symbol == null)); + return isUnmanaged ? null : (TypeSymbol)UnwrapAlias(symbol, diagnostics, syntax); + } + /// /// Binds the type for the syntax taking into account possibility of "var" type. /// @@ -50,9 +69,9 @@ internal TypeSymbol BindType(TypeSyntax syntax, DiagnosticBag diagnostics, out b /// Bound type if syntax binds to a type in the current context and /// null if syntax binds to "var" keyword in the current context. /// - internal TypeSymbol BindType(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isVar, out AliasSymbol alias) + internal TypeSymbol BindTypeOrVarKeyword(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isVar, out AliasSymbol alias) { - var symbol = BindTypeOrAlias(syntax, diagnostics, out isVar); + var symbol = BindTypeOrAliasOrVarKeyword(syntax, diagnostics, out isVar); Debug.Assert(isVar == ((object)symbol == null)); if (isVar) { @@ -64,137 +83,150 @@ internal TypeSymbol BindType(TypeSyntax syntax, DiagnosticBag diagnostics, out b return (TypeSymbol)UnwrapAlias(symbol, out alias, diagnostics, syntax); } } - - /// - /// Binds the type for the syntax taking into account possibility of "var" type. - /// If the syntax binds to an alias symbol to a type, it returns the alias symbol. - /// - /// Type syntax to bind. - /// Diagnostics. - /// - /// Set to false if syntax binds to a type or alias to a type in the current context and true if - /// syntax is "var" and it binds to "var" keyword in the current context. - /// - /// - /// Bound type or alias if syntax binds to a type or alias to a type in the current context and - /// null if syntax binds to "var" keyword in the current context. - /// - private Symbol BindTypeOrAlias(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isVar) + + private Symbol BindTypeOrAliasOrVarKeyword(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isVar) { - ConsList basesBeingResolved = null; - if (!syntax.IsVar) + if (syntax.IsVar) { - isVar = false; - return BindTypeOrAlias(syntax, diagnostics, basesBeingResolved); + var symbol = BindTypeOrAliasOrKeyword(syntax, diagnostics, out isVar); + + if (isVar) + { + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureImplicitLocal, diagnostics); + } + + return symbol; } else { - // Only IdentifierNameSyntax instances may be 'var'. - var identifierValueText = ((IdentifierNameSyntax)syntax).Identifier.ValueText; + isVar = false; + return BindTypeOrAlias(syntax, diagnostics, basesBeingResolved: null); + } + } + + private Symbol BindTypeOrAliasOrUnmanagedKeyword(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isUnmanaged) + { + if (syntax.IsUnmanaged) + { + var symbol = BindTypeOrAliasOrKeyword(syntax, diagnostics, out isUnmanaged); - Symbol symbol = null; + if (isUnmanaged) + { + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureUnmanagedGenericTypeConstraint, diagnostics); + } - // Perform name lookup without generating diagnostics as it could possibly be - // "var" keyword in the current context. - var lookupResult = LookupResult.GetInstance(); - HashSet useSiteDiagnostics = null; - this.LookupSymbolsInternal(lookupResult, identifierValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, basesBeingResolved: basesBeingResolved, - options: LookupOptions.NamespacesOrTypesOnly, diagnose: false); - diagnostics.Add(syntax, useSiteDiagnostics); + return symbol; + } + else + { + isUnmanaged = false; + return BindTypeOrAlias(syntax, diagnostics, basesBeingResolved: null); + } + } - // We have following possible cases for lookup: + /// + /// Binds the type for the syntax taking into account possibility of the type being a keyword. + /// If the syntax binds to an alias symbol to a type, it returns the alias symbol. + /// PREREQUISITE: syntax should be checked to match the keyword, like or . + /// Otherwise, call instead. + /// + private Symbol BindTypeOrAliasOrKeyword(TypeSyntax syntax, DiagnosticBag diagnostics, out bool isKeyword) + { + // Keywords can only be IdentifierNameSyntax + var identifierValueText = ((IdentifierNameSyntax)syntax).Identifier.ValueText; + Symbol symbol = null; + + // Perform name lookup without generating diagnostics as it could possibly be a keyword in the current context. + var lookupResult = LookupResult.GetInstance(); + HashSet useSiteDiagnostics = null; + this.LookupSymbolsInternal(lookupResult, identifierValueText, arity: 0, useSiteDiagnostics: ref useSiteDiagnostics, basesBeingResolved: null, options: LookupOptions.NamespacesOrTypesOnly, diagnose: false); - // 1) LookupResultKind.Empty: isVar = true + // We have following possible cases for lookup: - // 2) LookupResultKind.Viable: - // a) Single viable result that corresponds to 1) a non-error type: isVar = false - // 2) an error type: isVar = true - // b) Single viable result that corresponds to namespace: isVar = true - // c) Multi viable result (ambiguous result), we must return an error type: isVar = false + // 1) LookupResultKind.Empty: must be a keyword - // 3) Non viable, non empty lookup result: isVar = true + // 2) LookupResultKind.Viable: + // a) Single viable result that corresponds to 1) a non-error type: cannot be a keyword + // 2) an error type: must be a keyword + // b) Single viable result that corresponds to namespace: must be a keyword + // c) Multi viable result (ambiguous result), we must return an error type: cannot be a keyword - // BREAKING CHANGE: Case (2)(c) is a breaking change from the native compiler. - // BREAKING CHANGE: Native compiler interprets lookup with ambiguous result to correspond to bind - // BREAKING CHANGE: to "var" keyword (isVar = true), rather than reporting an error. - // BREAKING CHANGE: See test SemanticErrorTests.ErrorMeansSuccess_var() for an example. + // 3) Non viable, non empty lookup result: must be a keyword - switch (lookupResult.Kind) - { - case LookupResultKind.Empty: - // Case (1) - isVar = true; - symbol = null; - break; + // BREAKING CHANGE: Case (2)(c) is a breaking change from the native compiler. + // BREAKING CHANGE: Native compiler interprets lookup with ambiguous result to correspond to bind + // BREAKING CHANGE: to "var" keyword (isVar = true), rather than reporting an error. + // BREAKING CHANGE: See test SemanticErrorTests.ErrorMeansSuccess_var() for an example. - case LookupResultKind.Viable: - // Case (2) - DiagnosticBag resultDiagnostics = DiagnosticBag.GetInstance(); - bool wasError; - symbol = ResultSymbol( - lookupResult, - identifierValueText, - arity: 0, - where: syntax, - diagnostics: resultDiagnostics, - suppressUseSiteDiagnostics: false, - wasError: out wasError); - - // Here, we're mimicking behavior of dev10. If "var" fails to bind - // as a type, even if the reason is (e.g.) a type/alias conflict, then treat - // it as the contextual keyword. - if (wasError && lookupResult.IsSingleViable) - { - // NOTE: don't report diagnostics - we're not going to use the lookup result. - resultDiagnostics.Free(); - // Case (2)(a)(2) - goto default; - } + switch (lookupResult.Kind) + { + case LookupResultKind.Empty: + // Case (1) + isKeyword = true; + symbol = null; + break; - diagnostics.AddRange(resultDiagnostics); + case LookupResultKind.Viable: + // Case (2) + DiagnosticBag resultDiagnostics = DiagnosticBag.GetInstance(); + bool wasError; + symbol = ResultSymbol( + lookupResult, + identifierValueText, + arity: 0, + where: syntax, + diagnostics: resultDiagnostics, + suppressUseSiteDiagnostics: false, + wasError: out wasError); + + // Here, we're mimicking behavior of dev10. If the identifier fails to bind + // as a type, even if the reason is (e.g.) a type/alias conflict, then treat + // it as the contextual keyword. + if (wasError && lookupResult.IsSingleViable) + { + // NOTE: don't report diagnostics - we're not going to use the lookup result. resultDiagnostics.Free(); + // Case (2)(a)(2) + goto default; + } - if (lookupResult.IsSingleViable) - { - var type = UnwrapAlias(symbol, diagnostics, syntax) as TypeSymbol; + diagnostics.AddRange(resultDiagnostics); + resultDiagnostics.Free(); - if ((object)type != null) - { - // Case (2)(a)(1) - isVar = false; - } - else - { - // Case (2)(b) - Debug.Assert(UnwrapAliasNoDiagnostics(symbol) is NamespaceSymbol); - isVar = true; - symbol = null; - } + if (lookupResult.IsSingleViable) + { + var type = UnwrapAlias(symbol, diagnostics, syntax) as TypeSymbol; + + if ((object)type != null) + { + // Case (2)(a)(1) + isKeyword = false; } else { - // Case (2)(c) - isVar = false; + // Case (2)(b) + Debug.Assert(UnwrapAliasNoDiagnostics(symbol) is NamespaceSymbol); + isKeyword = true; + symbol = null; } + } + else + { + // Case (2)(c) + isKeyword = false; + } - break; - - default: - // Case (3) - isVar = true; - symbol = null; - break; - } - - lookupResult.Free(); - - if (isVar) - { - CheckFeatureAvailability(syntax, MessageID.IDS_FeatureImplicitLocal, diagnostics); - } + break; - return symbol; + default: + // Case (3) + isKeyword = true; + symbol = null; + break; } + + lookupResult.Free(); + return symbol; } // Binds the given expression syntax as Type. @@ -371,7 +403,13 @@ internal Symbol BindNamespaceOrTypeOrAliasSymbol(ExpressionSyntax syntax, Diagno var elementType = BindType(node.ElementType, diagnostics, basesBeingResolved); ReportUnsafeIfNotAllowed(node, diagnostics); - if (elementType.IsManagedType) + // Checking BinderFlags.GenericConstraintsClause to prevent cycles in binding + if (Flags.HasFlag(BinderFlags.GenericConstraintsClause) && elementType.TypeKind == TypeKind.TypeParameter) + { + // Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + Error(diagnostics, ErrorCode.ERR_BadConstraintType, node); + } + else if (elementType.IsManagedType) { // "Cannot take the address of, get the size of, or declare a pointer to a managed type ('{0}')" Error(diagnostics, ErrorCode.ERR_ManagedAddr, node, elementType); diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 8b68e71ae2829..5d16b2f0c3e8b 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -221,7 +221,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, bool isVar; AliasSymbol alias; - TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); + TypeSymbol declType = BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar, out alias); if (isVar) { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 41e4debe55a1c..5cf4d6c1ce383 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -3175,6 +3175,15 @@ internal static string ERR_ConvertToStaticClass { } } + /// + /// Looks up a localized string similar to Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}'. + /// + internal static string ERR_ConWithUnmanagedCon { + get { + return ResourceManager.GetString("ERR_ConWithUnmanagedCon", resourceCulture); + } + } + /// /// Looks up a localized string similar to Type parameter '{1}' has the 'struct' constraint so '{1}' cannot be used as a constraint for '{0}'. /// @@ -6784,6 +6793,15 @@ internal static string ERR_NewBoundMustBeLast { } } + /// + /// Looks up a localized string similar to The 'new()' constraint cannot be used with the 'unmanaged' constraint. + /// + internal static string ERR_NewBoundWithUnmanaged { + get { + return ResourceManager.GetString("ERR_NewBoundWithUnmanaged", resourceCulture); + } + } + /// /// Looks up a localized string similar to The 'new()' constraint cannot be used with the 'struct' constraint. /// @@ -9754,6 +9772,42 @@ internal static string ERR_UnimplementedInterfaceMember { } } + /// + /// Looks up a localized string similar to '{0}': cannot specify both a constraint class and the 'unmanaged' constraint. + /// + internal static string ERR_UnmanagedBoundWithClass { + get { + return ResourceManager.GetString("ERR_UnmanagedBoundWithClass", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'unmanaged' constraint must come before any other constraints. + /// + internal static string ERR_UnmanagedConstraintMustBeFirst { + get { + return ResourceManager.GetString("ERR_UnmanagedConstraintMustBeFirst", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}'. + /// + internal static string ERR_UnmanagedConstraintNotSatisfied { + get { + return ResourceManager.GetString("ERR_UnmanagedConstraintNotSatisfied", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Using 'unmanaged' constraint on local functions type parameters is not supported.. + /// + internal static string ERR_UnmanagedConstraintWithLocalFunctions { + get { + return ResourceManager.GetString("ERR_UnmanagedConstraintWithLocalFunctions", resourceCulture); + } + } + /// /// Looks up a localized string similar to A previous catch clause already catches all exceptions of this or of a super type ('{0}'). /// @@ -10394,6 +10448,15 @@ internal static string IDS_FeatureDefaultLiteral { } } + /// + /// Looks up a localized string similar to delegate generic type constraints. + /// + internal static string IDS_FeatureDelegateGenericTypeConstraint { + get { + return ResourceManager.GetString("IDS_FeatureDelegateGenericTypeConstraint", resourceCulture); + } + } + /// /// Looks up a localized string similar to dictionary initializer. /// @@ -10421,6 +10484,15 @@ internal static string IDS_FeatureDynamic { } } + /// + /// Looks up a localized string similar to enum generic type constraints. + /// + internal static string IDS_FeatureEnumGenericTypeConstraint { + get { + return ResourceManager.GetString("IDS_FeatureEnumGenericTypeConstraint", resourceCulture); + } + } + /// /// Looks up a localized string similar to exception filter. /// @@ -10853,6 +10925,15 @@ internal static string IDS_FeatureTypeVariance { } } + /// + /// Looks up a localized string similar to unmanaged generic type constraints. + /// + internal static string IDS_FeatureUnmanagedGenericTypeConstraint { + get { + return ResourceManager.GetString("IDS_FeatureUnmanagedGenericTypeConstraint", resourceCulture); + } + } + /// /// Looks up a localized string similar to using static. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index fbd7f00d17c57..541bfe600e4a6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -1329,6 +1329,9 @@ '{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'struct' constraint @@ -1473,7 +1476,6 @@ If such a class is used as a base class and if the deriving class defines a dest Predefined type '{0}' is not defined or imported - Predefined type '{0}' is not defined or imported @@ -5271,4 +5273,28 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. - + + enum generic type constraints + + + delegate generic type constraints + + + unmanaged generic type constraints + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + The 'unmanaged' constraint must come before any other constraints + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs index d38e5ca9da354..54323231324f3 100644 --- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs +++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpSymbolMatcher.cs @@ -772,6 +772,7 @@ private static bool AreTypeParametersEqual(TypeParameterSymbol type, TypeParamet // edit. Furthermore, comparing constraint types might lead to a cycle. Debug.Assert(type.HasConstructorConstraint == other.HasConstructorConstraint); Debug.Assert(type.HasValueTypeConstraint == other.HasValueTypeConstraint); + Debug.Assert(type.HasUnmanagedTypeConstraint == other.HasUnmanagedTypeConstraint); Debug.Assert(type.HasReferenceTypeConstraint == other.HasReferenceTypeConstraint); Debug.Assert(type.ConstraintTypesNoUseSiteDiagnostics.Length == other.ConstraintTypesNoUseSiteDiagnostics.Length); return true; diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs index 3324153ed0cb6..e3d293a46f0b0 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEAssemblyBuilder.cs @@ -21,6 +21,7 @@ internal abstract class PEAssemblyBuilderBase : PEModuleBuilder, Cci.IAssemblyRe private SynthesizedEmbeddedAttributeSymbol _lazyEmbeddedAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsReadOnlyAttribute; private SynthesizedEmbeddedAttributeSymbol _lazyIsByRefLikeAttribute; + private SynthesizedEmbeddedAttributeSymbol _lazyIsUnmanagedAttribute; /// /// The behavior of the C# command-line compiler is as follows: @@ -81,6 +82,11 @@ internal override ImmutableArray GetEmbeddedTypes(DiagnosticBag builder.Add(_lazyIsReadOnlyAttribute); } + if ((object)_lazyIsUnmanagedAttribute != null) + { + builder.Add(_lazyIsUnmanagedAttribute); + } + if ((object)_lazyIsByRefLikeAttribute != null) { builder.Add(_lazyIsByRefLikeAttribute); @@ -182,6 +188,19 @@ protected override SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() return base.TrySynthesizeIsReadOnlyAttribute(); } + protected override SynthesizedAttributeData TrySynthesizeIsUnmanagedAttribute() + { + if ((object)_lazyIsUnmanagedAttribute != null) + { + return new SynthesizedAttributeData( + _lazyIsUnmanagedAttribute.Constructor, + ImmutableArray.Empty, + ImmutableArray>.Empty); + } + + return base.TrySynthesizeIsUnmanagedAttribute(); + } + protected override SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() { if ((object)_lazyIsByRefLikeAttribute != null) @@ -216,6 +235,16 @@ private void CreateEmbeddedAttributesIfNeeded(DiagnosticBag diagnostics) diagnostics, AttributeDescription.IsByRefLikeAttribute); } + + if (this.NeedsGeneratedIsUnmanagedAttribute) + { + CreateEmbeddedAttributeItselfIfNeeded(diagnostics); + + CreateEmbeddedAttributeIfNeeded( + ref _lazyIsUnmanagedAttribute, + diagnostics, + AttributeDescription.IsUnmanagedAttribute); + } } private void CreateEmbeddedAttributeItselfIfNeeded(DiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 6f8c168cf03ba..12fc5d1bfdc1b 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -42,7 +42,7 @@ public override NoPia.EmbeddedTypesManager EmbeddedTypesManagerOpt private Dictionary _fixedImplementationTypes; private bool _needsGeneratedIsReadOnlyAttribute_Value; - + private bool _needsGeneratedAttributes_IsFrozen; /// @@ -70,6 +70,15 @@ internal bool NeedsGeneratedIsByRefLikeAttribute } } + internal bool NeedsGeneratedIsUnmanagedAttribute + { + get + { + _needsGeneratedAttributes_IsFrozen = true; + return Compilation.NeedsGeneratedIsUnmanagedAttribute; + } + } + internal PEModuleBuilder( SourceModuleSymbol sourceModule, EmitOptions emitOptions, @@ -1440,6 +1449,17 @@ internal SynthesizedAttributeData SynthesizeIsReadOnlyAttribute(Symbol symbol) return TrySynthesizeIsReadOnlyAttribute(); } + internal SynthesizedAttributeData SynthesizeIsUnmanagedAttribute(Symbol symbol) + { + if ((object)Compilation.SourceModule != symbol.ContainingModule) + { + // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute. + return null; + } + + return TrySynthesizeIsUnmanagedAttribute(); + } + internal SynthesizedAttributeData SynthesizeIsByRefLikeAttribute(Symbol symbol) { if ((object)Compilation.SourceModule != symbol.ContainingModule) @@ -1457,6 +1477,12 @@ protected virtual SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute() return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor); } + protected virtual SynthesizedAttributeData TrySynthesizeIsUnmanagedAttribute() + { + // For modules, this attribute should be present. Only assemblies generate and embed this type. + return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor); + } + protected virtual SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() { // For modules, this attribute should be present. Only assemblies generate and embed this type. diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs index f217005d60a91..95656cd43b086 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/TypeParameterSymbolAdapter.cs @@ -223,7 +223,25 @@ Cci.ITypeReference Cci.IGenericTypeParameterReference.DefiningType IEnumerable Cci.IGenericParameter.GetConstraints(EmitContext context) { var moduleBeingBuilt = (PEModuleBuilder)context.Module; + var seenValueType = false; + if (this.HasUnmanagedTypeConstraint) + { + var typeRef = moduleBeingBuilt.GetSpecialType( + SpecialType.System_ValueType, + syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNodeOpt, + diagnostics: context.Diagnostics); + + var modifier = CSharpCustomModifier.CreateRequired( + moduleBeingBuilt.Compilation.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType)); + + // emit "(class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType" pattern as "unmanaged" + yield return new Cci.TypeReferenceWithAttributes(new Cci.ModifiedTypeReference(typeRef, ImmutableArray.Create(modifier))); + + // do not emit another one for Dev11 similarities + seenValueType = true; + } + foreach (var type in this.ConstraintTypesNoUseSiteDiagnostics) { switch (type.SpecialType) @@ -236,18 +254,19 @@ Cci.ITypeReference Cci.IGenericTypeParameterReference.DefiningType break; } var typeRef = moduleBeingBuilt.Translate(type, - syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNodeOpt, - diagnostics: context.Diagnostics); + syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNodeOpt, + diagnostics: context.Diagnostics); yield return type.GetTypeRefWithAttributes(this.DeclaringCompilation, - typeRef); + typeRef); } + if (this.HasValueTypeConstraint && !seenValueType) { // Add System.ValueType constraint to comply with Dev11 output var typeRef = moduleBeingBuilt.GetSpecialType(SpecialType.System_ValueType, - syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNodeOpt, - diagnostics: context.Diagnostics); + syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNodeOpt, + diagnostics: context.Diagnostics); yield return new Cci.TypeReferenceWithAttributes(typeRef); } @@ -265,7 +284,7 @@ bool Cci.IGenericParameter.MustBeValueType { get { - return this.HasValueTypeConstraint; + return this.HasValueTypeConstraint || this.HasUnmanagedTypeConstraint; } } @@ -275,7 +294,8 @@ bool Cci.IGenericParameter.MustHaveDefaultConstructor { // add constructor constraint for value type constrained // type parameters to comply with Dev11 output - return this.HasConstructorConstraint || this.HasValueTypeConstraint; + // do this for "unmanaged" constraint too + return this.HasConstructorConstraint || this.HasValueTypeConstraint || this.HasUnmanagedTypeConstraint; } } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 502d335d95d87..6508a6bb91878 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1431,7 +1431,7 @@ internal enum ErrorCode #endregion diagnostics for ref locals and ref returns introduced in C# 7 #region stragglers for C# 7 - ERR_PredefinedValueTupleTypeNotFound = 8179, + ERR_PredefinedValueTupleTypeNotFound = 8179, // We need a specific error code for ValueTuple as an IDE codefix depends on it (AddNuget) ERR_SemiOrLBraceOrArrowExpected = 8180, ERR_NewWithTupleTypeSyntax = 8181, ERR_PredefinedValueTupleTypeMustBeStruct = 8182, @@ -1561,6 +1561,14 @@ internal enum ErrorCode ERR_RefLocalOrParamExpected = 8373, ERR_RefAssignNarrower = 8374, + ERR_NewBoundWithUnmanaged = 8375, + ERR_UnmanagedConstraintMustBeFirst = 8376, + ERR_UnmanagedConstraintNotSatisfied = 8377, + ERR_UnmanagedConstraintWithLocalFunctions = 8378, + ERR_ConWithUnmanagedCon = 8379, + + ERR_UnmanagedBoundWithClass = 8380, + // Note: you will need to re-generate compiler code after adding warnings (build\scripts\generate-compiler-code.cmd) } } diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 8eccde0a659bb..6cf20613a29a3 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -151,7 +151,10 @@ internal enum MessageID IDS_FeatureImprovedOverloadCandidates = MessageBase + 12733, IDS_FeatureRefReassignment = MessageBase + 12734, IDS_FeatureRefFor = MessageBase + 12735, - IDS_FeatureRefForEach = MessageBase + 12736 + IDS_FeatureRefForEach = MessageBase + 12736, + IDS_FeatureEnumGenericTypeConstraint = MessageBase + 12737, + IDS_FeatureDelegateGenericTypeConstraint = MessageBase + 12738, + IDS_FeatureUnmanagedGenericTypeConstraint = MessageBase + 12739, } // Message IDs may refer to strings that need to be localized. @@ -197,6 +200,9 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureRefReassignment: case MessageID.IDS_FeatureRefFor: case MessageID.IDS_FeatureRefForEach: + case MessageID.IDS_FeatureEnumGenericTypeConstraint: // semantic check + case MessageID.IDS_FeatureDelegateGenericTypeConstraint: // semantic check + case MessageID.IDS_FeatureUnmanagedGenericTypeConstraint: // semantic check return LanguageVersion.CSharp7_3; // C# 7.2 features. diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 90241730d320d..b3ca14527c7d8 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,10 +1,10 @@ +*REMOVED*Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics = false, string moduleName = null, string mainTypeName = null, string scriptClassName = null, System.Collections.Generic.IEnumerable usings = null, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel.Debug, bool checkOverflow = false, bool allowUnsafe = false, string cryptoKeyContainer = null, string cryptoKeyFile = null, System.Collections.Immutable.ImmutableArray cryptoPublicKey = default(System.Collections.Immutable.ImmutableArray), bool? delaySign = null, Microsoft.CodeAnalysis.Platform platform = Microsoft.CodeAnalysis.Platform.AnyCpu, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption = Microsoft.CodeAnalysis.ReportDiagnostic.Default, int warningLevel = 4, System.Collections.Generic.IEnumerable> specificDiagnosticOptions = null, bool concurrentBuild = true, bool deterministic = false, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver = null, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver = null, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver = null, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer = null, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider = null, bool publicSign = false) -> void *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.WithRefKindKeyword(Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax *REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Argument(Microsoft.CodeAnalysis.CSharp.Syntax.NameColonSyntax nameColon, Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax *REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CrefParameter(Microsoft.CodeAnalysis.SyntaxToken refOrOutKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics = false, string moduleName = null, string mainTypeName = null, string scriptClassName = null, System.Collections.Generic.IEnumerable usings = null, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel.Debug, bool checkOverflow = false, bool allowUnsafe = false, string cryptoKeyContainer = null, string cryptoKeyFile = null, System.Collections.Immutable.ImmutableArray cryptoPublicKey = default(System.Collections.Immutable.ImmutableArray), bool? delaySign = null, Microsoft.CodeAnalysis.Platform platform = Microsoft.CodeAnalysis.Platform.AnyCpu, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption = Microsoft.CodeAnalysis.ReportDiagnostic.Default, int warningLevel = 4, System.Collections.Generic.IEnumerable> specificDiagnosticOptions = null, bool concurrentBuild = true, bool deterministic = false, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver = null, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver = null, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver = null, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer = null, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider = null, bool publicSign = false, Microsoft.CodeAnalysis.MetadataImportOptions metadataImportOptions = Microsoft.CodeAnalysis.MetadataImportOptions.Public) -> void -*REMOVED*Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics = false, string moduleName = null, string mainTypeName = null, string scriptClassName = null, System.Collections.Generic.IEnumerable usings = null, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel.Debug, bool checkOverflow = false, bool allowUnsafe = false, string cryptoKeyContainer = null, string cryptoKeyFile = null, System.Collections.Immutable.ImmutableArray cryptoPublicKey = default(System.Collections.Immutable.ImmutableArray), bool? delaySign = null, Microsoft.CodeAnalysis.Platform platform = Microsoft.CodeAnalysis.Platform.AnyCpu, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption = Microsoft.CodeAnalysis.ReportDiagnostic.Default, int warningLevel = 4, System.Collections.Generic.IEnumerable> specificDiagnosticOptions = null, bool concurrentBuild = true, bool deterministic = false, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver = null, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver = null, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver = null, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer = null, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider = null, bool publicSign = false) -> void Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics, string moduleName, string mainTypeName, string scriptClassName, System.Collections.Generic.IEnumerable usings, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel, bool checkOverflow, bool allowUnsafe, string cryptoKeyContainer, string cryptoKeyFile, System.Collections.Immutable.ImmutableArray cryptoPublicKey, bool? delaySign, Microsoft.CodeAnalysis.Platform platform, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption, int warningLevel, System.Collections.Generic.IEnumerable> specificDiagnosticOptions, bool concurrentBuild, bool deterministic, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider, bool publicSign) -> void Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.WithMetadataImportOptions(Microsoft.CodeAnalysis.MetadataImportOptions value) -> Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions Microsoft.CodeAnalysis.CSharp.DeconstructionInfo @@ -18,6 +18,7 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax.WithRefKindKeyword(Microsoft Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.RefKindKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken refKindKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.WithRefKindKeyword(Microsoft.CodeAnalysis.SyntaxToken refKindKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax.IsUnmanaged.get -> bool static Microsoft.CodeAnalysis.CSharp.CSharpCommandLineParser.Script.get -> Microsoft.CodeAnalysis.CSharp.CSharpCommandLineParser static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeconstructionInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.AssignmentExpressionSyntax assignment) -> Microsoft.CodeAnalysis.CSharp.DeconstructionInfo static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeconstructionInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.ForEachVariableStatementSyntax foreach) -> Microsoft.CodeAnalysis.CSharp.DeconstructionInfo diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index c9ea1b39edfa7..515e26fb4e116 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -690,6 +690,11 @@ private void AddTypeParameterConstraints(ImmutableArray typeArgumen AddKeyword(SyntaxKind.ClassKeyword); needComma = true; } + else if (typeParam.HasUnmanagedTypeConstraint) + { + builder.Add(new SymbolDisplayPart(SymbolDisplayPartKind.Keyword, null, "unmanaged")); + needComma = true; + } else if (typeParam.HasValueTypeConstraint) { AddKeyword(SyntaxKind.StructKeyword); diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs index e56a0376cb9a2..c834fc57a7449 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs @@ -73,6 +73,11 @@ public override bool HasValueTypeConstraint get { return false; } } + public override bool HasUnmanagedTypeConstraint + { + get { return false; } + } + public override bool IsImplicitlyDeclared { get { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index 3b056bb52c8c1..fdb98a49cfb11 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -34,6 +34,7 @@ public partial class CSharpCompilation private bool _needsGeneratedIsReadOnlyAttribute_Value; private bool _needsGeneratedIsByRefLikeAttribute_Value; + private bool _needsGeneratedIsUnmanagedAttribute_Value; private bool _needsGeneratedAttributes_IsFrozen; @@ -65,6 +66,20 @@ internal bool NeedsGeneratedIsByRefLikeAttribute } } + /// + /// Returns a value indicating whether this compilation has a member that needs IsUnmanagedAttribute to be generated during emit phase. + /// The value is set during binding the symbols that need that attribute, and is frozen on first trial to get it. + /// Freezing is needed to make sure that nothing tries to modify the value after the value is read. + /// + internal bool NeedsGeneratedIsUnmanagedAttribute + { + get + { + _needsGeneratedAttributes_IsFrozen = true; + return _needsGeneratedIsUnmanagedAttribute_Value; + } + } + /// /// Lookup member declaration in well known type used by this Compilation. /// @@ -489,6 +504,18 @@ internal void EnsureIsByRefLikeAttributeExists(DiagnosticBag diagnostics, Locati } } + internal void EnsureIsUnmanagedAttributeExists(DiagnosticBag diagnostics, Location location, bool modifyCompilationForIsUnmanaged) + { + Debug.Assert(!modifyCompilationForIsUnmanaged || !_needsGeneratedAttributes_IsFrozen); + + var isNeeded = CheckIfIsUnmanagedAttributeShouldBeEmbedded(diagnostics, location); + + if (isNeeded && modifyCompilationForIsUnmanaged) + { + _needsGeneratedIsUnmanagedAttribute_Value = true; + } + } + internal bool CheckIfIsReadOnlyAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) { return CheckIfAttributeShouldBeEmbedded( @@ -501,12 +528,21 @@ internal bool CheckIfIsReadOnlyAttributeShouldBeEmbedded(DiagnosticBag diagnosti internal bool CheckIfIsByRefLikeAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) { return CheckIfAttributeShouldBeEmbedded( - diagnosticsOpt, - locationOpt, - WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); } + internal bool CheckIfIsUnmanagedAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt) + { + return CheckIfAttributeShouldBeEmbedded( + diagnosticsOpt, + locationOpt, + WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute, + WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor); + } + private bool CheckIfAttributeShouldBeEmbedded(DiagnosticBag diagnosticsOpt, Location locationOpt, WellKnownType attributeType, WellKnownMember attributeCtor) { var userDefinedAttribute = GetWellKnownType(attributeType); diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 2c22277f35828..996f8907b2787 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -163,10 +163,24 @@ public static TypeParameterBounds ResolveBounds( constraintDeducedBase = constraintTypeParameter.GetDeducedBaseType(constraintsInProgress); AddInterfaces(interfacesBuilder, constraintTypeParameter.GetInterfaces(constraintsInProgress)); - if (constraintTypeParameter.HasValueTypeConstraint && !inherited && currentCompilation != null && constraintTypeParameter.IsFromCompilation(currentCompilation)) + if (!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))); + ErrorCode errorCode; + if (constraintTypeParameter.HasUnmanagedTypeConstraint) + { + errorCode = ErrorCode.ERR_ConWithUnmanagedCon; + } + else if (constraintTypeParameter.HasValueTypeConstraint) + { + errorCode = ErrorCode.ERR_ConWithValCon; + } + else + { + break; + } + + // "Type parameter '{1}' has the '?' constraint so '{1}' cannot be used as a constraint for '{0}'" + diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(errorCode, typeParameter, constraintTypeParameter))); continue; } } @@ -803,6 +817,13 @@ private static bool CheckConstraints( return false; } + if (typeParameter.HasUnmanagedTypeConstraint && (typeArgument.IsManagedType || !typeArgument.IsNonNullableValueType())) + { + // "The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}'" + diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument))); + return false; + } + if (typeParameter.HasValueTypeConstraint && !typeArgument.IsNonNullableValueType()) { // "The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}'" diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs index 98f5f127d6e6d..46b65e11a84e8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.ErrorTypeParameterSymbol.cs @@ -71,6 +71,14 @@ public override bool HasValueTypeConstraint } } + public override bool HasUnmanagedTypeConstraint + { + get + { + return false; + } + } + public override int Ordinal { get diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index cd440de323eef..818bf99427401 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -555,6 +555,7 @@ public static bool HaveSameConstraints(TypeParameterSymbol typeParameter1, TypeM if ((typeParameter1.HasConstructorConstraint != typeParameter2.HasConstructorConstraint) || (typeParameter1.HasReferenceTypeConstraint != typeParameter2.HasReferenceTypeConstraint) || (typeParameter1.HasValueTypeConstraint != typeParameter2.HasValueTypeConstraint) || + (typeParameter1.HasUnmanagedTypeConstraint != typeParameter2.HasUnmanagedTypeConstraint) || (typeParameter1.Variance != typeParameter2.Variance)) { return false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index 05d1f83fd6dde..8ae36d66cdd22 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -25,16 +25,17 @@ internal sealed class PETypeParameterSymbol #region Metadata private readonly string _name; private readonly ushort _ordinal; // 0 for first, 1 for second, ... - private readonly GenericParameterAttributes _flags; #endregion - private TypeParameterBounds _lazyBounds = TypeParameterBounds.Unset; - /// /// First error calculating bounds. /// - private DiagnosticInfo _lazyBoundsErrorInfo = CSDiagnosticInfo.EmptyErrorInfo; // Indicates unknown state. + private DiagnosticInfo _lazyConstraintsUseSiteErrorInfo = CSDiagnosticInfo.EmptyErrorInfo; // Indicates unknown state. + private readonly GenericParameterAttributes _flags; + private ThreeState _lazyHasIsUnmanagedConstraint; + private TypeParameterBounds _lazyBounds = TypeParameterBounds.Unset; + private ImmutableArray _lazyDeclaredConstraintTypes; private ImmutableArray _lazyCustomAttributes; internal PETypeParameterSymbol( @@ -81,7 +82,7 @@ private PETypeParameterSymbol( _name = string.Empty; } - _lazyBoundsErrorInfo = new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this); + _lazyConstraintsUseSiteErrorInfo = new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this); } // Clear the '.ctor' flag if both '.ctor' and 'valuetype' are @@ -138,80 +139,106 @@ public override AssemblySymbol ContainingAssembly private ImmutableArray GetDeclaredConstraintTypes() { - PEMethodSymbol containingMethod = null; - PENamedTypeSymbol containingType; - - if (_containingSymbol.Kind == SymbolKind.Method) - { - containingMethod = (PEMethodSymbol)_containingSymbol; - containingType = (PENamedTypeSymbol)containingMethod.ContainingSymbol; - } - else - { - containingType = (PENamedTypeSymbol)_containingSymbol; - } - - var moduleSymbol = containingType.ContainingPEModule; - var metadataReader = moduleSymbol.Module.MetadataReader; - GenericParameterConstraintHandleCollection constraints; - - try + if (_lazyDeclaredConstraintTypes.IsDefault) { - constraints = metadataReader.GetGenericParameter(_handle).GetConstraints(); - } - catch (BadImageFormatException) - { - constraints = default(GenericParameterConstraintHandleCollection); - Interlocked.CompareExchange(ref _lazyBoundsErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo); - } + ImmutableArray declaredConstraintTypes; - if (constraints.Count > 0) - { - var symbolsBuilder = ArrayBuilder.GetInstance(); - MetadataDecoder tokenDecoder; + PEMethodSymbol containingMethod = null; + PENamedTypeSymbol containingType; - if ((object)containingMethod != null) + if (_containingSymbol.Kind == SymbolKind.Method) { - tokenDecoder = new MetadataDecoder(moduleSymbol, containingMethod); + containingMethod = (PEMethodSymbol)_containingSymbol; + containingType = (PENamedTypeSymbol)containingMethod.ContainingSymbol; } else { - tokenDecoder = new MetadataDecoder(moduleSymbol, containingType); + containingType = (PENamedTypeSymbol)_containingSymbol; } - foreach (var constraintHandle in constraints) + var moduleSymbol = containingType.ContainingPEModule; + var metadataReader = moduleSymbol.Module.MetadataReader; + GenericParameterConstraintHandleCollection constraints; + + try { - var constraint = metadataReader.GetGenericParameterConstraint(constraintHandle); - var constraintTypeHandle = constraint.Type; + constraints = metadataReader.GetGenericParameter(_handle).GetConstraints(); + } + catch (BadImageFormatException) + { + constraints = default(GenericParameterConstraintHandleCollection); + Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo); + } + + bool hasUnmanagedModreqPattern = false; - TypeSymbol typeSymbol = tokenDecoder.GetTypeOfToken(constraintTypeHandle); + if (constraints.Count > 0) + { + var symbolsBuilder = ArrayBuilder.GetInstance(); + MetadataDecoder tokenDecoder; - // Drop 'System.Object' constraint type. - if (typeSymbol.SpecialType == SpecialType.System_Object) + if ((object)containingMethod != null) + { + tokenDecoder = new MetadataDecoder(moduleSymbol, containingMethod); + } + else { - continue; + tokenDecoder = new MetadataDecoder(moduleSymbol, containingType); } - // Drop 'System.ValueType' constraint type if the 'valuetype' constraint was also specified. - if (((_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) && - (typeSymbol.SpecialType == SpecialType.System_ValueType)) + foreach (var constraintHandle in constraints) { - continue; + var constraint = metadataReader.GetGenericParameterConstraint(constraintHandle); + var typeSymbol = tokenDecoder.DecodeGenericParameterConstraint(constraint.Type, out bool hasUnmanagedModreq); + + if (typeSymbol.SpecialType == SpecialType.System_ValueType) + { + // recognize "(class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType" pattern as "unmanaged" + if (hasUnmanagedModreq) + { + hasUnmanagedModreqPattern = true; + } + + // Drop 'System.ValueType' constraint type if the 'valuetype' constraint was also specified. + if (((_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)) + { + continue; + } + } + + // Drop 'System.Object' constraint type. + if (typeSymbol.SpecialType == SpecialType.System_Object) + { + continue; + } + + typeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeSymbol, constraintHandle, moduleSymbol); + + symbolsBuilder.Add(typeSymbol); } - typeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeSymbol, - constraintHandle, - moduleSymbol); + declaredConstraintTypes = symbolsBuilder.ToImmutableAndFree(); + } + else + { + declaredConstraintTypes = ImmutableArray.Empty; + } - symbolsBuilder.Add(typeSymbol); + // - presence of unmanaged pattern has to be matched with `valuetype` + // - IsUnmanagedAttribute is allowed iif there is an unmanaged pattern + if (hasUnmanagedModreqPattern && (_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) == 0 || + hasUnmanagedModreqPattern != moduleSymbol.Module.HasIsUnmanagedAttribute(_handle)) + { + // we do not recognize these combinations as "unmanaged" + hasUnmanagedModreqPattern = false; + Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo); } - return symbolsBuilder.ToImmutableAndFree(); - } - else - { - return ImmutableArray.Empty; + _lazyHasIsUnmanagedConstraint = hasUnmanagedModreqPattern.ToThreeState(); + ImmutableInterlocked.InterlockedInitialize(ref _lazyDeclaredConstraintTypes, declaredConstraintTypes); } + + return _lazyDeclaredConstraintTypes; } public override ImmutableArray Locations @@ -254,6 +281,15 @@ public override bool HasValueTypeConstraint } } + public override bool HasUnmanagedTypeConstraint + { + get + { + GetDeclaredConstraintTypes(); + return this._lazyHasIsUnmanagedConstraint.Value(); + } + } + public override VarianceKind Variance { get @@ -302,8 +338,16 @@ public override ImmutableArray GetAttributes() if (_lazyCustomAttributes.IsDefault) { var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule; - containingPEModuleSymbol.LoadCustomAttributes(this.Handle, ref _lazyCustomAttributes); + + var loadedCustomAttributes = containingPEModuleSymbol.GetCustomAttributesForToken( + Handle, + out _, + // Filter out [IsUnmanagedAttribute] + HasUnmanagedTypeConstraint ? AttributeDescription.IsUnmanagedAttribute : default); + + ImmutableInterlocked.InterlockedInitialize(ref _lazyCustomAttributes, loadedCustomAttributes); } + return _lazyCustomAttributes; } @@ -346,19 +390,19 @@ private TypeParameterBounds GetBounds(ConsList inProgress) diagnostics.Free(); - Interlocked.CompareExchange(ref _lazyBoundsErrorInfo, errorInfo, CSDiagnosticInfo.EmptyErrorInfo); + Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, errorInfo, CSDiagnosticInfo.EmptyErrorInfo); Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset); } - Debug.Assert(!ReferenceEquals(_lazyBoundsErrorInfo, CSDiagnosticInfo.EmptyErrorInfo)); + Debug.Assert(!ReferenceEquals(_lazyConstraintsUseSiteErrorInfo, CSDiagnosticInfo.EmptyErrorInfo)); return _lazyBounds; } internal override DiagnosticInfo GetConstraintsUseSiteErrorInfo() { EnsureAllConstraintsAreResolved(); - Debug.Assert(!ReferenceEquals(_lazyBoundsErrorInfo, CSDiagnosticInfo.EmptyErrorInfo)); - return _lazyBoundsErrorInfo; + Debug.Assert(!ReferenceEquals(_lazyConstraintsUseSiteErrorInfo, CSDiagnosticInfo.EmptyErrorInfo)); + return _lazyConstraintsUseSiteErrorInfo; } private NamedTypeSymbol GetDefaultBaseType() diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/SymbolFactory.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/SymbolFactory.cs index 8873480c2a77e..a979e0aaa0fbb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/SymbolFactory.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/SymbolFactory.cs @@ -62,6 +62,11 @@ internal override bool IsAcceptedInAttributeModifierType(TypeSymbol type) return type.IsWellKnownTypeInAttribute(); } + internal override bool IsAcceptedUnmanagedTypeModifierType(TypeSymbol type) + { + return type.IsWellKnownTypeUnmanagedType(); + } + internal override TypeSymbol GetSZArrayTypeSymbol(PEModuleSymbol moduleSymbol, TypeSymbol elementType, ImmutableArray> customModifiers) { if (elementType is UnsupportedMetadataTypeSymbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs index 5bc75fda6242a..d6a00e421987b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/CrefTypeParameterSymbol.cs @@ -126,6 +126,11 @@ public override bool HasReferenceTypeConstraint get { return false; } } + public override bool HasUnmanagedTypeConstraint + { + get { return false; } + } + public override bool HasConstructorConstraint { get { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs index 8cc9d2edc2605..c0b65baeba477 100755 --- a/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/FieldSymbolWithAttributesAndModifiers.cs @@ -206,6 +206,11 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu // IsReadOnlyAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsReadOnlyAttribute.FullName); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.IsUnmanagedAttribute)) + { + // IsUnmanagedAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsUnmanagedAttribute.FullName); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.IsByRefLikeAttribute)) { // IsByRefLikeAttribute should not be set explicitly. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 526396520b9c9..fcf25587189bb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -78,7 +78,7 @@ internal override TypeSymbol GetFieldType(ConsList fieldsBeingBound var binder = binderFactory.GetBinder(typeSyntax); bool isVar; - type = binder.BindType(typeSyntax, diagnostics, out isVar); + type = binder.BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar); Debug.Assert((object)type != null || isVar); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs index 46fc07791c2a3..2592ff648a431 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/IndexedTypeParameterSymbol.cs @@ -125,6 +125,11 @@ public override bool HasReferenceTypeConstraint get { return false; } } + public override bool HasUnmanagedTypeConstraint + { + get { return false; } + } + public override bool HasConstructorConstraint { get { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 182b3db4ae931..6b81275172ef9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -108,6 +108,11 @@ internal void GetDeclarationDiagnostics(DiagnosticBag addTo) foreach (var typeParam in _typeParameters) { typeParam.ForceComplete(null, default(CancellationToken)); + + if (typeParam.HasUnmanagedTypeConstraint) + { + addTo.Add(ErrorCode.ERR_UnmanagedConstraintWithLocalFunctions, typeParam.GetNonNullSyntaxNode().Location); + } } // force lazy init diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 7608653e1348d..4ff82b92a6e8e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -561,6 +561,11 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu // IsReadOnlyAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsReadOnlyAttribute.FullName); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.IsUnmanagedAttribute)) + { + // IsUnmanagedAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsUnmanagedAttribute.FullName); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.IsByRefLikeAttribute)) { // IsByRefLikeAttribute should not be set explicitly. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index bf504f73374ee..53dcc84201198 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -310,7 +310,7 @@ public bool IsVar if (_typeSyntax.IsVar) { bool isVar; - TypeSymbol declType = this.TypeSyntaxBinder.BindType(_typeSyntax, new DiagnosticBag(), out isVar); + TypeSymbol declType = this.TypeSyntaxBinder.BindTypeOrVarKeyword(_typeSyntax, new DiagnosticBag(), out isVar); return isVar; } @@ -326,7 +326,7 @@ private TypeSymbol GetTypeSymbol() bool isVar; RefKind refKind; - TypeSymbol declType = typeBinder.BindType(_typeSyntax.SkipRef(out refKind), diagnostics, out isVar); + TypeSymbol declType = typeBinder.BindTypeOrVarKeyword(_typeSyntax.SkipRef(out refKind), diagnostics, out isVar); if (isVar) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 974a5a357c54c..ac9a659f76d95 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -482,6 +482,15 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok foreach (var typeParameter in this.TypeParameters) { typeParameter.ForceComplete(locationOpt, cancellationToken); + var diagnostics = DiagnosticBag.GetInstance(); + + if (typeParameter.HasUnmanagedTypeConstraint) + { + this.DeclaringCompilation.EnsureIsUnmanagedAttributeExists(diagnostics, typeParameter.GetNonNullSyntaxNode().Location, modifyCompilationForIsUnmanaged: true); + } + + AddDeclarationDiagnostics(diagnostics); + diagnostics.Free(); } state.NotePartComplete(CompletionPart.TypeParameters); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index d283ea77968e4..669de6e3e1893 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -439,7 +439,7 @@ internal sealed override TypeSymbol GetFieldType(ConsList fieldsBei else { bool isVar; - type = binder.BindType(typeSyntax, diagnostics, out isVar); + type = binder.BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar); Debug.Assert((object)type != null || isVar); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 6066c790ae5d0..091903a62f1df 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -1110,6 +1110,11 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut // IsReadOnlyAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsReadOnlyAttribute.FullName); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.IsUnmanagedAttribute)) + { + // IsUnmanagedAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsUnmanagedAttribute.FullName); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.IsByRefLikeAttribute)) { // IsByRefLikeAttribute should not be set explicitly. @@ -1235,6 +1240,11 @@ private void DecodeWellKnownAttributeAppliedToReturnValue(ref DecodeWellKnownAtt // DynamicAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitDynamicAttr, arguments.AttributeSyntaxOpt.Location); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.IsUnmanagedAttribute)) + { + // IsUnmanagedAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsUnmanagedAttribute.FullName); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.IsReadOnlyAttribute)) { // IsReadOnlyAttribute should not be set explicitly. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index b1f30afab327e..3d87f40de18d7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -723,6 +723,11 @@ internal sealed override void DecodeWellKnownAttribute(ref DecodeWellKnownAttrib // IsReadOnlyAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsReadOnlyAttribute.FullName); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.IsUnmanagedAttribute)) + { + // IsUnmanagedAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsUnmanagedAttribute.FullName); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.IsByRefLikeAttribute)) { // IsByRefLikeAttribute should not be set explicitly. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index 1f4723485ea33..1f9546182584c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -368,6 +368,14 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB } CheckModifiers(location, diagnostics); + + foreach (var typeParameter in _typeParameters) + { + if (typeParameter.HasUnmanagedTypeConstraint) + { + DeclaringCompilation.EnsureIsUnmanagedAttributeExists(diagnostics, typeParameter.GetNonNullSyntaxNode().Location, modifyCompilationForIsUnmanaged: true); + } + } } // This is also used for async lambdas. Probably not the best place to locate this method, but where else could it go? diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 72907a72cbf79..2654ef88be59c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -1263,6 +1263,11 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu // IsReadOnlyAttribute should not be set explicitly. arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsReadOnlyAttribute.FullName); } + else if (attribute.IsTargetAttribute(this, AttributeDescription.IsUnmanagedAttribute)) + { + // IsUnmanagedAttribute should not be set explicitly. + arguments.Diagnostics.Add(ErrorCode.ERR_ExplicitReservedAttr, arguments.AttributeSyntaxOpt.Location, AttributeDescription.IsUnmanagedAttribute.FullName); + } else if (attribute.IsTargetAttribute(this, AttributeDescription.IsByRefLikeAttribute)) { // IsByRefLikeAttribute should not be set explicitly. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 6cdf27683e545..c463505f8ed82 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -8,6 +8,7 @@ using Roslyn.Utilities; using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Emit; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -305,6 +306,16 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok _state.SpinWaitComplete(incompletePart, cancellationToken); } } + + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + + if (this.HasUnmanagedTypeConstraint) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsUnmanagedAttribute(this)); + } + } } internal sealed class SourceTypeParameterSymbol : SourceTypeParameterSymbolBase @@ -351,7 +362,7 @@ public override bool HasValueTypeConstraint get { var constraints = this.GetDeclaredConstraints(); - return (constraints & TypeParameterConstraintKind.ValueType) != 0; + return (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.Unmanaged)) != 0; } } @@ -364,6 +375,15 @@ public override bool HasReferenceTypeConstraint } } + public override bool HasUnmanagedTypeConstraint + { + get + { + var constraints = this.GetDeclaredConstraints(); + return (constraints & TypeParameterConstraintKind.Unmanaged) != 0; + } + } + protected override ImmutableArray ContainerTypeParameters { get { return _owner.TypeParameters; } @@ -421,7 +441,7 @@ public override bool HasValueTypeConstraint get { var constraints = this.GetDeclaredConstraints(); - return (constraints & TypeParameterConstraintKind.ValueType) != 0; + return (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.Unmanaged)) != 0; } } @@ -434,6 +454,15 @@ public override bool HasReferenceTypeConstraint } } + public override bool HasUnmanagedTypeConstraint + { + get + { + var constraints = this.GetDeclaredConstraints(); + return (constraints & TypeParameterConstraintKind.Unmanaged) != 0; + } + } + protected override ImmutableArray ContainerTypeParameters { get { return _owner.TypeParameters; } @@ -628,6 +657,15 @@ public override bool HasReferenceTypeConstraint } } + public override bool HasUnmanagedTypeConstraint + { + get + { + var typeParameter = this.OverriddenTypeParameter; + return ((object)typeParameter != null) && typeParameter.HasUnmanagedTypeConstraint; + } + } + protected override ImmutableArray ContainerTypeParameters { get { return this.Owner.TypeParameters; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs index 57fa1a48c78d9..86cf5dee6e730 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterConstraintClause.cs @@ -16,6 +16,7 @@ internal enum TypeParameterConstraintKind ReferenceType = 0x01, ValueType = 0x02, Constructor = 0x04, + Unmanaged = 0x08, } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs index 9c03e26d494cd..6efb1d2193a70 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.PooledObjects; + namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// @@ -16,5 +19,15 @@ public override bool IsImplicitlyDeclared { get { return true; } } + + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + + if (this.HasUnmanagedTypeConstraint) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsUnmanagedAttribute(this)); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index b81e90f23f2e0..fa1ea1ee1a483 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -465,7 +465,7 @@ internal sealed override bool IsManagedType { get { - return true; + return !this.HasUnmanagedTypeConstraint; } } @@ -496,6 +496,8 @@ internal sealed override ObsoleteAttributeData ObsoleteAttributeData public abstract bool HasValueTypeConstraint { get; } + public abstract bool HasUnmanagedTypeConstraint { get; } + public abstract VarianceKind Variance { get; } internal sealed override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 2fb92536bb557..25deaa57e9f33 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1528,9 +1528,13 @@ internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes( return new Cci.TypeReferenceWithAttributes(typeRef); } - internal static bool IsWellKnownTypeInAttribute(this ITypeSymbol typeSymbol) + internal static bool IsWellKnownTypeInAttribute(this ITypeSymbol typeSymbol) => typeSymbol.IsWellKnownInteropServicesTopLevelType("InAttribute"); + + internal static bool IsWellKnownTypeUnmanagedType(this ITypeSymbol typeSymbol) => typeSymbol.IsWellKnownInteropServicesTopLevelType("UnmanagedType"); + + private static bool IsWellKnownInteropServicesTopLevelType(this ITypeSymbol typeSymbol, string name) { - if (typeSymbol.Name != "InAttribute" || typeSymbol.ContainingType != null) + if (typeSymbol.Name != name || typeSymbol.ContainingType != null) { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs index 0329e9a653a76..fc60e10ecad87 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedTypeParameterSymbol.cs @@ -73,6 +73,14 @@ public override bool HasReferenceTypeConstraint } } + public override bool HasUnmanagedTypeConstraint + { + get + { + return _underlyingTypeParameter.HasUnmanagedTypeConstraint; + } + } + public override bool HasValueTypeConstraint { get diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs index 57b762b76a7da..2b3fa55215fb8 100644 --- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeSyntax.cs @@ -4,13 +4,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax { internal abstract partial class TypeSyntax { - public bool IsVar - { - get - { - var ts = this as IdentifierNameSyntax; - return ts != null && ts.Identifier.ToString() == "var"; - } - } + public bool IsVar => this is IdentifierNameSyntax name && name.Identifier.ToString() == "var"; + + public bool IsUnmanaged => this is IdentifierNameSyntax name && name.Identifier.ToString() == "unmanaged"; } } diff --git a/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs index 8cdb0e5c92a96..741b77a5d8875 100644 --- a/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs @@ -4,12 +4,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax { public abstract partial class TypeSyntax { - public bool IsVar - { - get - { - return ((InternalSyntax.TypeSyntax)this.Green).IsVar; - } - } + public bool IsVar => ((InternalSyntax.TypeSyntax)this.Green).IsVar; + + public bool IsUnmanaged => ((InternalSyntax.TypeSyntax)this.Green).IsUnmanaged; } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 40d40e53d83c7..0926de5be9f16 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -8640,6 +8640,51 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 8d96db6c0b1c3..2f40d3d19e603 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -8640,6 +8640,51 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index e0d12ccdfc525..304afb89cdb3e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -8640,6 +8640,51 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 3fa3f75e71fa9..9544dcf73c0b0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -8640,6 +8640,51 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8eb653242b052..17c37461668f5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -8640,6 +8640,51 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index c6aef2157f82d..ff3d6e1d08d99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -8640,6 +8640,51 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index d82fe8a31a1a5..8eeb61bbab368 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -8640,6 +8640,51 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 5a0403af571f3..c302307bafdf0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -8640,6 +8640,51 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1164a6e3b2301..edae634569241 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -8640,6 +8640,51 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 9ba39bf1f8298..c9e047494f210 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -8640,6 +8640,51 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 8a133627ddd8f..145559423d915 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -8640,6 +8640,51 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 88170c7fde4fa..79967abcf5719 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -8640,6 +8640,51 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index f79e92aeda63a..56b42b328a219 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -8640,6 +8640,51 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ref foreach iteration variables + + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + '{0}': cannot specify both a constraint class and the 'unmanaged' constraint + + + + enum generic type constraints + enum generic type constraints + + + + delegate generic type constraints + delegate generic type constraints + + + + unmanaged generic type constraints + unmanaged generic type constraints + + + + The 'new()' constraint cannot be used with the 'unmanaged' constraint + The 'new()' constraint cannot be used with the 'unmanaged' constraint + + + + The 'unmanaged' constraint must come before any other constraints + The 'unmanaged' constraint must come before any other constraints + + + + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + The type '{2}' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter '{1}' in the generic type or method '{0}' + + + + Using 'unmanaged' constraint on local functions type parameters is not supported. + Using 'unmanaged' constraint on local functions type parameters is not supported. + + + + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + Type parameter '{1}' has the 'unmanaged' constraint so '{1}' cannot be used as a constraint for '{0}' + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_IsUnmanaged.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_IsUnmanaged.cs new file mode 100644 index 0000000000000..c1a05e8b519d9 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_IsUnmanaged.cs @@ -0,0 +1,593 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.UnitTests; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using System; +using System.Collections.Immutable; +using System.Linq; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class AttributeTests_IsUnmanaged : CSharpTestBase + { + [Fact] + public void AttributeUsedIfExists_FromSource_Method() + { + var text = @" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} +public class Test +{ + public void M() where T : unmanaged { } +} +"; + + CompileAndVerify(text, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + Assert.Null(module.ContainingAssembly.GetTypeByMetadataName(AttributeDescription.CodeAnalysisEmbeddedAttribute.FullName)); + AssertReferencedIsUnmanagedAttribute(Accessibility.Public, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void AttributeUsedIfExists_FromSource_Class() + { + var text = @" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} +public class Test where T : unmanaged +{ +} +"; + + CompileAndVerify(text, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + Assert.Null(module.ContainingAssembly.GetTypeByMetadataName(AttributeDescription.CodeAnalysisEmbeddedAttribute.FullName)); + AssertReferencedIsUnmanagedAttribute(Accessibility.Public, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void AttributeUsedIfExists_FromReference_Method_Reference() + { + var reference = CreateCompilation(@" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +}").EmitToImageReference(); + + var text = @" +public class Test +{ + public void M() where T : unmanaged { } +} +"; + + CompileAndVerify(text, references: new[] { reference }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Public, typeParameter, reference.Display); + AssertNoIsUnmanagedAttributeExists(module.ContainingAssembly); + }); + } + + [Fact] + public void AttributeUsedIfExists_FromReference_Class_Reference() + { + var reference = CreateCompilation(@" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +}").EmitToImageReference(); + + var text = @" +public class Test where T : unmanaged +{ +} +"; + + CompileAndVerify(text, references: new[] { reference }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Public, typeParameter, reference.Display); + AssertNoIsUnmanagedAttributeExists(module.ContainingAssembly); + }); + } + + [Fact] + public void AttributeUsedIfExists_FromReference_Method_Module() + { + var reference = CreateCompilation(@" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +}").EmitToImageReference(); + + var text = @" +public class Test +{ + public void M() where T : unmanaged { } +} +"; + + CompileAndVerify(text, verify: Verification.Fails, references: new[] { reference }, options: TestOptions.ReleaseModule, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Public, typeParameter, reference.Display); + AssertNoIsUnmanagedAttributeExists(module.ContainingAssembly); + }); + } + + [Fact] + public void AttributeUsedIfExists_FromReference_Class_Module() + { + var reference = CreateCompilation(@" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +}").EmitToImageReference(); + + var text = @" +public class Test where T : unmanaged +{ +} +"; + + CompileAndVerify(text, verify: Verification.Fails, references: new[] { reference }, options: TestOptions.ReleaseModule, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Public, typeParameter, reference.Display); + AssertNoIsUnmanagedAttributeExists(module.ContainingAssembly); + }); + } + + [Fact] + public void AttributeGeneratedIfNotExists_FromSource_Method() + { + var text = @" +public class Test +{ + public void M() where T : unmanaged { } +} +"; + + CompileAndVerify(text, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void AttributeGeneratedIfNotExists_FromSource_Class() + { + var text = @" +public class Test where T : unmanaged +{ +} +"; + + CompileAndVerify(text, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void IsUnmanagedAttributeIsDisallowedEverywhereInSource_Delegates() + { + var code = @" +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} + +[IsUnmanaged] +public delegate void D([IsUnmanaged]int x); +"; + + CreateCompilation(code).VerifyDiagnostics( + // (9,2): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(9, 2), + // (10,25): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // public delegate void D([IsUnmanaged]int x); + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(10, 25)); + } + + [Fact] + public void IsUnmanagedAttributeIsDisallowedEverywhereInSource_Types() + { + var code = @" +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} + +[IsUnmanaged] +public class Test +{ +} +"; + + CreateCompilation(code).VerifyDiagnostics( + // (9,2): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(9, 2)); + } + + [Fact] + public void IsUnmanagedAttributeIsDisallowedEverywhereInSource_Fields() + { + var code = @" +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} + +public class Test +{ + [IsUnmanaged] + public int x = 0; +} +"; + + CreateCompilation(code).VerifyDiagnostics( + // (11,6): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(11, 6)); + } + + [Fact] + public void IsUnmanagedAttributeIsDisallowedEverywhereInSource_Properties() + { + var code = @" +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} + +public class Test +{ + [IsUnmanaged] + public int Property => 0; +} +"; + + CreateCompilation(code).VerifyDiagnostics( + // (11,6): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(11, 6)); + } + + [Fact] + public void IsUnmanagedAttributeIsDisallowedEverywhereInSource_Methods() + { + var code = @" +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} + +public class Test +{ + [IsUnmanaged] + [return: IsUnmanaged] + public int Method([IsUnmanaged]int x) + { + return x; + } +} +"; + + CreateCompilation(code).VerifyDiagnostics( + // (11,6): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(11, 6), + // (12,14): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [return: IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(12, 14), + // (13,24): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // public int Method([IsUnmanaged]int x) + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(13, 24)); + } + + [Fact] + public void IsUnmanagedAttributeIsDisallowedEverywhereInSource_Indexers() + { + var code = @" +using System.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} + +public class Test +{ + [IsUnmanaged] + public int this[[IsUnmanaged]int x] => x; +} +"; + + CreateCompilation(code).VerifyDiagnostics( + // (11,6): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(11, 6), + // (12,22): error CS8335: Do not use 'System.Runtime.CompilerServices.IsUnmanagedAttribute'. This is reserved for compiler usage. + // public int this[[IsUnmanaged]int x] => x; + Diagnostic(ErrorCode.ERR_ExplicitReservedAttr, "IsUnmanaged").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(12, 22)); + } + + [Fact] + public void UserReferencingIsUnmanagedAttributeShouldResultInAnError() + { + var code = @" +[IsUnmanaged] +public class Test +{ +} +"; + + CreateCompilation(code).VerifyDiagnostics( + // (2,2): error CS0246: The type or namespace name 'IsUnmanagedAttribute' could not be found (are you missing a using directive or an assembly reference?) + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "IsUnmanaged").WithArguments("IsUnmanagedAttribute").WithLocation(2, 2), + // (2,2): error CS0246: The type or namespace name 'IsUnmanaged' could not be found (are you missing a using directive or an assembly reference?) + // [IsUnmanaged] + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "IsUnmanaged").WithArguments("IsUnmanaged").WithLocation(2, 2)); + } + + [Fact] + public void TypeReferencingAnotherTypeThatUsesAPublicAttributeFromAThirdNotReferencedAssemblyShouldGenerateItsOwn() + { + var options = TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All); + + var code1 = CreateCompilation(@" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +}"); + + var code2 = CreateCompilation(@" +public class Test1 where T : unmanaged { } +", references: new[] { code1.ToMetadataReference() }, options: options); + + CompileAndVerify(code2, symbolValidator: module => + { + AssertNoIsUnmanagedAttributeExists(module.ContainingAssembly); + }); + + var code3 = CreateCompilation(@" +public class Test2 : Test1 where T : unmanaged { } +", references: new[] { code2.ToMetadataReference() }, options: options); + + CompileAndVerify(code3, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test2`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void BuildingAModuleRequiresIsUnmanagedAttributeToBeThere_Missing_Type() + { + var code = @" +public class Test where T : unmanaged +{ +}"; + + CreateCompilation(code, options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (2,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsUnmanagedAttribute' is not defined or imported + // public class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(2, 19)); + } + + [Fact] + public void BuildingAModuleRequiresIsUnmanagedAttributeToBeThere_Missing_Method() + { + var code = @" +public class Test +{ + public void M() where T : unmanaged {} +}"; + + CreateCompilation(code, options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (4,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsUnmanagedAttribute' is not defined or imported + // public void M() where T : unmanaged {} + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute").WithLocation(4, 19)); + } + + [Fact] + public void ReferencingAnEmbeddedIsUnmanagedAttributeDoesNotUseIt_InternalsVisible() + { + var options = TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All); + + var code1 = @" +[assembly:System.Runtime.CompilerServices.InternalsVisibleToAttribute(""Assembly2"")] +public class Test1 where T : unmanaged +{ +}"; + + var comp1 = CompileAndVerify(code1, options: options, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test1`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + var code2 = @" +public class Test2 : Test1 where T : unmanaged +{ +}"; + + CompileAndVerify(code2, options: options.WithModuleName("Assembly2"), references: new[] { comp1.Compilation.ToMetadataReference() }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Test2`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Theory] + [InlineData(OutputKind.DynamicallyLinkedLibrary)] + [InlineData(OutputKind.NetModule)] + public void IsUnmanagedAttributeExistsWithWrongConstructorSignature(OutputKind outputKind) + { + var text = @" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute + { + public IsUnmanagedAttribute(int p) { } + } +} +class Test where T : unmanaged +{ +}"; + + CreateCompilation(text, options: new CSharpCompilationOptions(outputKind)).VerifyDiagnostics( + // (9,12): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsUnmanagedAttribute..ctor' + // class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute", ".ctor").WithLocation(9, 12)); + } + + [Theory] + [InlineData(OutputKind.DynamicallyLinkedLibrary)] + [InlineData(OutputKind.NetModule)] + public void IsUnmanagedAttributeExistsWithPrivateConstructor(OutputKind outputKind) + { + var text = @" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute + { + private IsUnmanagedAttribute() { } + } +} +class Test where T : unmanaged +{ +}"; + + CreateCompilation(text, options: new CSharpCompilationOptions(outputKind)).VerifyDiagnostics( + // (9,12): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsUnmanagedAttribute..ctor' + // class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute", ".ctor").WithLocation(9, 12)); + } + + [Theory] + [InlineData(OutputKind.DynamicallyLinkedLibrary)] + [InlineData(OutputKind.NetModule)] + public void IsUnmanagedAttributeExistsAsInterface(OutputKind outputKind) + { + var text = @" +namespace System.Runtime.CompilerServices +{ + public interface IsUnmanagedAttribute { } +} +class Test where T : unmanaged +{ +}"; + + CreateCompilation(text, options: new CSharpCompilationOptions(outputKind)).VerifyDiagnostics( + // (6,12): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsUnmanagedAttribute..ctor' + // class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "T").WithArguments("System.Runtime.CompilerServices.IsUnmanagedAttribute", ".ctor").WithLocation(6, 12)); + } + + internal static void AssertReferencedIsUnmanagedAttribute(Accessibility accessibility, TypeParameterSymbol typeParameter, string assemblyName) + { + var attributes = ((PEModuleSymbol)typeParameter.ContainingModule).GetCustomAttributesForToken(((PETypeParameterSymbol)typeParameter).Handle); + NamedTypeSymbol attributeType = attributes.Single().AttributeClass; + + Assert.Equal("IsUnmanagedAttribute", attributeType.Name); + Assert.Equal(assemblyName, attributeType.ContainingAssembly.Name); + Assert.Equal(accessibility, attributeType.DeclaredAccessibility); + + switch (accessibility) + { + case Accessibility.Internal: + { + var isUnmanagedTypeAttributes = attributeType.GetAttributes().OrderBy(attribute => attribute.AttributeClass.Name).ToArray(); + Assert.Equal(2, isUnmanagedTypeAttributes.Length); + + Assert.Equal(WellKnownTypes.GetMetadataName(WellKnownType.System_Runtime_CompilerServices_CompilerGeneratedAttribute), isUnmanagedTypeAttributes[0].AttributeClass.ToDisplayString()); + Assert.Equal(AttributeDescription.CodeAnalysisEmbeddedAttribute.FullName, isUnmanagedTypeAttributes[1].AttributeClass.ToDisplayString()); + break; + } + + case Accessibility.Public: + { + Assert.Null(attributeType.ContainingAssembly.GetTypeByMetadataName(AttributeDescription.CodeAnalysisEmbeddedAttribute.FullName)); + + break; + } + + default: + throw ExceptionUtilities.UnexpectedValue(accessibility); + } + + } + + private void AssertNoIsUnmanagedAttributeExists(AssemblySymbol assembly) + { + var isUnmanagedAttributeTypeName = WellKnownTypes.GetMetadataName(WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute); + Assert.Null(assembly.GetTypeByMetadataName(isUnmanagedAttributeTypeName)); + } + } +} diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs index e1b2d6713b18d..fa320035ab98f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs @@ -16431,6 +16431,38 @@ .maxstack 3 IL_000a: add IL_000b: stind.i4 IL_000c: ret +}"); + } + + [Fact] + public void EnumConstraint_NoBoxing() + { + var code = @" +enum E1 +{ + A = 5 +} +class Test1 +{ + public static void M(T arg) where T : struct, System.Enum + { + } +} +class Test2 +{ + public void M() + { + Test1.M(E1.A); + } +}"; + + CompileAndVerify(code).VerifyIL("Test2.M", @" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldc.i4.5 + IL_0001: call ""void Test1.M(E1)"" + IL_0006: ret }"); } } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/UnmanagedTypeModifierTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/UnmanagedTypeModifierTests.cs new file mode 100644 index 0000000000000..3af25f2098275 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit/Emit/UnmanagedTypeModifierTests.cs @@ -0,0 +1,1195 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit +{ + public class UnmanagedTypeModifierTests : CSharpTestBase + { + [Fact] + public void LoadingADifferentModifierTypeForUnmanagedConstraint() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + var obj = new TestRef(); + + obj.M1(); // valid + obj.M2(); // invalid + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (9,13): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'TestRef.M2()'. There is no boxing conversion from 'int' to '?'. + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M2").WithArguments("TestRef.M2()", "?", "T", "int").WithLocation(9, 13), + // (9,13): error CS0570: 'T' is not supported by the language + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_BindToBogus, "M2").WithArguments("T").WithLocation(9, 13), + // (9,13): error CS0648: '' is a type not supported by the language + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_BogusType, "M2").WithArguments("").WithLocation(9, 13) + ); + } + + [Fact] + public void LoadingUnmanagedTypeModifier_OptionalIsError() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + var obj = new TestRef(); + + obj.M1(); // valid + obj.M2(); // invalid + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (9,13): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'TestRef.M2()'. There is no boxing conversion from 'int' to '?'. + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M2").WithArguments("TestRef.M2()", "?", "T", "int").WithLocation(9, 13), + // (9,13): error CS0570: 'T' is not supported by the language + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_BindToBogus, "M2").WithArguments("T").WithLocation(9, 13), + // (9,13): error CS0648: '' is a type not supported by the language + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_BogusType, "M2").WithArguments("").WithLocation(9, 13) + ); + } + + [Fact] + public void LoadingUnmanagedTypeModifier_ModreqWithoutAttribute() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + var obj = new TestRef(); + + obj.M1(); // valid + obj.M2(); // invalid + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (9,13): error CS0570: 'T' is not supported by the language + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_BindToBogus, "M2").WithArguments("T").WithLocation(9, 13) + ); + } + + [Fact] + public void LoadingUnmanagedTypeModifier_AttributeWithoutModreq() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + var obj = new TestRef(); + + obj.M1(); // valid + obj.M2(); // invalid + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (9,13): error CS0570: 'T' is not supported by the language + // obj.M2(); // invalid + Diagnostic(ErrorCode.ERR_BindToBogus, "M2").WithArguments("T").WithLocation(9, 13) + ); + } + + [Fact] + public void ProperErrorsArePropagatedIfModreqTypeIsNotAvailable_Class() + { + var code = @" +namespace System +{ + public class Object {} + public class Void {} + public class ValueType {} +} +class Test where T : unmanaged +{ +}"; + + CreateEmptyCompilation(code).VerifyDiagnostics( + // (8,25): error CS0518: Predefined type 'System.Runtime.InteropServices.UnmanagedType' is not defined or imported + // class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.Runtime.InteropServices.UnmanagedType").WithLocation(8, 25)); + } + + [Fact] + public void ProperErrorsArePropagatedIfModreqTypeIsNotAvailable_Method() + { + var code = @" +namespace System +{ + public class Object {} + public class Void {} + public class ValueType {} +} +class Test +{ + public void M() where T : unmanaged {} +}"; + + CreateEmptyCompilation(code).VerifyDiagnostics( + // (10,34): error CS0518: Predefined type 'System.Runtime.InteropServices.UnmanagedType' is not defined or imported + // public void M() where T : unmanaged {} + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.Runtime.InteropServices.UnmanagedType").WithLocation(10, 34)); + } + + [Fact] + public void ProperErrorsArePropagatedIfModreqTypeIsNotAvailable_Delegate() + { + var code = @" +namespace System +{ + public class Object {} + public class Void {} + public class ValueType {} + public class IntPtr {} + public class MulticastDelegate {} +} +public delegate void D() where T : unmanaged;"; + + CreateEmptyCompilation(code).VerifyDiagnostics( + // (10,39): error CS0518: Predefined type 'System.Runtime.InteropServices.UnmanagedType' is not defined or imported + // public delegate void D() where T : unmanaged; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.Runtime.InteropServices.UnmanagedType").WithLocation(10, 39)); + } + + [Fact] + public void ProperErrorsArePropagatedIfValueTypeIsNotAvailable_Class() + { + var code = @" +namespace System +{ + public class Object {} + public class Void {} + + namespace Runtime + { + namespace InteropServices + { + public class UnmanagedType {} + } + } +} +class Test where T : unmanaged +{ +}"; + + CreateEmptyCompilation(code).VerifyDiagnostics( + // (15,25): error CS0518: Predefined type 'System.ValueType' is not defined or imported + // class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.ValueType").WithLocation(15, 25)); + } + + [Fact] + public void ProperErrorsArePropagatedIfValueTypeIsNotAvailable_Method() + { + var code = @" +namespace System +{ + public class Object {} + public class Void {} + + namespace Runtime + { + namespace InteropServices + { + public class UnmanagedType {} + } + } +} +class Test +{ + public void M() where T : unmanaged {} +}"; + + CreateEmptyCompilation(code).VerifyDiagnostics( + // (17,34): error CS0518: Predefined type 'System.ValueType' is not defined or imported + // public void M() where T : unmanaged {} + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.ValueType").WithLocation(17, 34)); + } + + [Fact] + public void ProperErrorsArePropagatedIfValueTypeIsNotAvailable_Delegate() + { + var code = @" +namespace System +{ + public class Object {} + public class Void {} + public class IntPtr {} + public class MulticastDelegate {} + + namespace Runtime + { + namespace InteropServices + { + public class UnmanagedType {} + } + } +} +public delegate void M() where T : unmanaged;"; + + CreateEmptyCompilation(code).VerifyDiagnostics( + // (17,39): error CS0518: Predefined type 'System.ValueType' is not defined or imported + // public delegate void M() where T : unmanaged; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.ValueType").WithLocation(17, 39)); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Virtual_Compilation() + { + var reference = CompileAndVerify(@" +public class Parent +{ + public virtual string M() where T : unmanaged => ""Parent""; +} +public class Child : Parent +{ + public override string M() => ""Child""; +}", symbolValidator: module => + { + var parentTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(parentTypeParameter.HasValueTypeConstraint); + Assert.True(parentTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, parentTypeParameter, module.ContainingAssembly.Name); + + var childTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(childTypeParameter.HasValueTypeConstraint); + Assert.True(childTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, childTypeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Parent().M()); + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { reference.Compilation.EmitToImageReference() }, expectedOutput: @" +Parent +Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Virtual_Reference() + { + var parent = CompileAndVerify(@" +public class Parent +{ + public virtual string M() where T : unmanaged => ""Parent""; +}", symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + + var child = CompileAndVerify(@" +public class Child : Parent +{ + public override string M() => ""Child""; +}", references: new[] { parent.Compilation.EmitToImageReference() }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Parent().M()); + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { parent.Compilation.EmitToImageReference(), child.Compilation.EmitToImageReference() }, expectedOutput: @" +Parent +Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Abstract_Compilation() + { + var reference = CompileAndVerify(@" +public abstract class Parent +{ + public abstract string M() where T : unmanaged; +} +public class Child : Parent +{ + public override string M() => ""Child""; +}", symbolValidator: module => + { + var parentTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(parentTypeParameter.HasValueTypeConstraint); + Assert.True(parentTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, parentTypeParameter, module.ContainingAssembly.Name); + + var childTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(childTypeParameter.HasValueTypeConstraint); + Assert.True(childTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, childTypeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { reference.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Abstract_Reference() + { + var parent = CompileAndVerify(@" +public abstract class Parent +{ + public abstract string M() where T : unmanaged; +}", symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + + var child = CompileAndVerify(@" +public class Child : Parent +{ + public override string M() => ""Child""; +}", references: new[] { parent.Compilation.EmitToImageReference() }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { parent.Compilation.EmitToImageReference(), child.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Interface_Implicit_Nonvirtual_Compilation() + { + var reference = CompileAndVerify(@" +public interface Parent +{ + string M() where T : unmanaged; +} +public class Child : Parent +{ + public string M() where T : unmanaged => ""Child""; +}", symbolValidator: module => + { + var parentTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(parentTypeParameter.HasValueTypeConstraint); + Assert.True(parentTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, parentTypeParameter, module.ContainingAssembly.Name); + + var childTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(childTypeParameter.HasValueTypeConstraint); + Assert.True(childTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, childTypeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { reference.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Interface_Implicit_Nonvirtual_Reference() + { + var parent = CompileAndVerify(@" +public interface Parent +{ + string M() where T : unmanaged; +}", symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + + var child = CompileAndVerify(@" +public class Child : Parent +{ + public string M() where T : unmanaged => ""Child""; +}", references: new[] { parent.Compilation.EmitToImageReference() }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { parent.Compilation.EmitToImageReference(), child.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Interface_Implicit_Virtual_Compilation() + { + var reference = CompileAndVerify(@" +public interface Parent +{ + string M() where T : unmanaged; +} +public class Child : Parent +{ + public virtual string M() where T : unmanaged => ""Child""; +}", symbolValidator: module => + { + var parentTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(parentTypeParameter.HasValueTypeConstraint); + Assert.True(parentTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, parentTypeParameter, module.ContainingAssembly.Name); + + var childTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(childTypeParameter.HasValueTypeConstraint); + Assert.True(childTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, childTypeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { reference.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Interface_Implicit_Virtual_Reference() + { + var parent = CompileAndVerify(@" +public interface Parent +{ + string M() where T : unmanaged; +}", symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + + var child = CompileAndVerify(@" +public class Child : Parent +{ + public virtual string M() where T : unmanaged => ""Child""; +}", references: new[] { parent.Compilation.EmitToImageReference() }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + System.Console.WriteLine(new Child().M()); + } +}", references: new[] { parent.Compilation.EmitToImageReference(), child.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Interface_Explicit_Compilation() + { + var reference = CompileAndVerify(@" +public interface Parent +{ + string M() where T : unmanaged; +} +public class Child : Parent +{ + string Parent.M() => ""Child""; +}", symbolValidator: module => + { + var parentTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(parentTypeParameter.HasValueTypeConstraint); + Assert.True(parentTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, parentTypeParameter, module.ContainingAssembly.Name); + + var childTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("Parent.M").TypeParameters.Single(); + Assert.True(childTypeParameter.HasValueTypeConstraint); + Assert.True(childTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, childTypeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + Parent obj = new Child(); + System.Console.WriteLine(obj.M()); + } +}", references: new[] { reference.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToOverrides_Interface_Explicit_Reference() + { + var parent = CompileAndVerify(@" +public interface Parent +{ + string M() where T : unmanaged; +}", symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + + var child = CompileAndVerify(@" +public class Child : Parent +{ + string Parent.M() => ""Child""; +}", references: new[] { parent.Compilation.EmitToImageReference() }, symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("Parent.M").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + CompileAndVerify(@" +class Program +{ + public static void Main() + { + Parent obj = new Child(); + System.Console.WriteLine(obj.M()); + } +}", references: new[] { parent.Compilation.EmitToImageReference(), child.Compilation.EmitToImageReference() }, expectedOutput: "Child"); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToLambda_Compilation() + { + CompileAndVerify(@" +public delegate T D() where T : unmanaged; +public class TestRef +{ + public static void Print(D lambda) where T : unmanaged + { + System.Console.WriteLine(lambda()); + } +} +public class Program +{ + static void Test(T arg) where T : unmanaged + { + TestRef.Print(() => arg); + } + + public static void Main() + { + Test(5); + } +}", + expectedOutput: "5", + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + var delegateTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("D`1").TypeParameters.Single(); + Assert.True(delegateTypeParameter.HasValueTypeConstraint); + Assert.True(delegateTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, delegateTypeParameter, module.ContainingAssembly.Name); + + var lambdaTypeParameter = module.ContainingAssembly.GetTypeByMetadataName("Program").GetTypeMember("<>c__DisplayClass0_0").TypeParameters.Single(); + Assert.True(lambdaTypeParameter.HasValueTypeConstraint); + Assert.True(lambdaTypeParameter.HasUnmanagedTypeConstraint); + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, lambdaTypeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void UnmanagedTypeModreqIsCopiedToLambda_Reference() + { + var reference = CompileAndVerify(@" +public delegate T D() where T : unmanaged; +public class TestRef +{ + public static void Print(D lambda) where T : unmanaged + { + System.Console.WriteLine(lambda()); + } +}", symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("D`1").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); // .ctor is an artifact of emit, we will ignore it on importing. + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + + + CompileAndVerify(@" +public class Program +{ + static void Test(T arg) where T : unmanaged + { + TestRef.Print(() => arg); + } + + public static void Main() + { + Test(5); + } +}", + expectedOutput: "5", + references: new[] { reference.Compilation.EmitToImageReference() }, + options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: module => + { + var typeParameter = module.ContainingAssembly.GetTypeByMetadataName("Program").GetTypeMember("<>c__DisplayClass0_0").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); // .ctor is an artifact of emit, we will ignore it on importing. + + AttributeTests_IsUnmanaged.AssertReferencedIsUnmanagedAttribute(Accessibility.Internal, typeParameter, module.ContainingAssembly.Name); + }); + } + + [Fact] + public void DuplicateUnmanagedTypeInReferences() + { + var refCode = @" +namespace System.Runtime.InteropServices +{ + public class UnmanagedType {} +}"; + + var ref1 = CreateCompilation(refCode).EmitToImageReference(); + var ref2 = CreateCompilation(refCode).EmitToImageReference(); + + var user = @" +public class Test where T : unmanaged +{ +}"; + + CreateCompilation(user, references: new[] { ref1, ref2 }).VerifyDiagnostics( + // (2,32): error CS0518: Predefined type 'System.Runtime.InteropServices.UnmanagedType' is not defined or imported + // public class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.Runtime.InteropServices.UnmanagedType").WithLocation(2, 32)); + } + + [Fact] + public void UnmanagedConstraintWithClassConstraint_IL() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + new TestRef().M(); + new TestRef().M(); + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (6,23): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'TestRef.M()' + // new TestRef().M(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("TestRef.M()", "T", "int").WithLocation(6, 23), + // (7,23): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'TestRef.M()'. There is no implicit reference conversion from 'string' to 'System.ValueType'. + // new TestRef().M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "M").WithArguments("TestRef.M()", "System.ValueType", "T", "string").WithLocation(7, 23), + // (7,23): error CS0570: 'T' is not supported by the language + // new TestRef().M(); + Diagnostic(ErrorCode.ERR_BindToBogus, "M").WithArguments("T").WithLocation(7, 23) + ); + } + + [Fact] + public void UnmanagedConstraintWithConstructorConstraint_IL() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M<.ctor (class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + new TestRef().M(); + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (6,23): error CS0570: 'T' is not supported by the language + // new TestRef().M(); + Diagnostic(ErrorCode.ERR_BindToBogus, "M").WithArguments("T").WithLocation(6, 23) + ); + } + + [Fact] + public void UnmanagedConstraintWithTypeConstraint_IL() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + // OK + new TestRef().M(); + + // Not IComparable + new TestRef().M(); + } + + struct S1{} +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (10,23): error CS0315: The type 'Test.S1' cannot be used as type parameter 'T' in the generic type or method 'TestRef.M()'. There is no boxing conversion from 'Test.S1' to 'System.IComparable'. + // new TestRef().M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("TestRef.M()", "System.IComparable", "T", "Test.S1").WithLocation(10, 23) + ); + } + + [Fact] + public void UnmanagedConstraintWithNoCtorConstraint_IL() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + // OK + new TestRef().M(); + + // Not IComparable + new TestRef().M(); + } + + struct S1{} +}"; + + var c = CreateCompilation(code, references: new[] { reference }); + + c.VerifyDiagnostics( + // (10,23): error CS0315: The type 'Test.S1' cannot be used as type parameter 'T' in the generic type or method 'TestRef.M()'. There is no boxing conversion from 'Test.S1' to 'System.IComparable'. + // new TestRef().M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("TestRef.M()", "System.IComparable", "T", "Test.S1").WithLocation(10, 23) + ); + + var typeParameter = c.GlobalNamespace.GetTypeMember("TestRef").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + } + + private const string IsUnmanagedAttributeIL = @" +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly Test +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module Test.dll +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + +.class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute + extends [mscorlib]System.Attribute +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: nop + IL_0007: ret + } +} + +.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute + extends [mscorlib]System.Attribute +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: nop + IL_0007: ret + } +} +"; + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs new file mode 100644 index 0000000000000..c01bd009eafeb --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs @@ -0,0 +1,2678 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.UnitTests; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.Semantics +{ + public class GenericConstraintsTests : CompilingTestBase + { + [Fact] + public void EnumConstraint_Compilation_Alone() + { + CreateCompilation(@" +public class Test where T : System.Enum +{ +} +public enum E1 +{ + A +} +public class Test2 +{ + public void M() where U : System.Enum + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}").VerifyDiagnostics( + // (14,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(14, 26), + // (15,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Enum'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Enum", "T", "string").WithLocation(15, 26)); + } + + [Fact] + public void EnumConstraint_Compilation_ReferenceType() + { + CreateCompilation(@" +public class Test where T : class, System.Enum +{ +} +public enum E1 +{ + A +} +public class Test2 +{ + public void M() where U : class, System.Enum + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}").VerifyDiagnostics( + // (13,26): error CS0452: The type 'E1' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var a = new Test(); // enum + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "E1").WithArguments("Test", "T", "E1").WithLocation(13, 26), + // (14,26): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("Test", "T", "int").WithLocation(14, 26), + // (15,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Enum'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Enum", "T", "string").WithLocation(15, 26)); + } + + [Fact] + public void EnumConstraint_Compilation_ValueType() + { + CreateCompilation(@" +public class Test where T : struct, System.Enum +{ +} +public enum E1 +{ + A +} +public class Test2 +{ + public void M() where U : struct, System.Enum + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}").VerifyDiagnostics( + // (14,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(14, 26), + // (15,26): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(15, 26), + // (16,26): error CS0453: The type 'Enum' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Test' + // var d = new Test(); // Enum type + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "System.Enum").WithArguments("Test", "T", "System.Enum").WithLocation(16, 26)); + } + + [Fact] + public void EnumConstraint_Compilation_Constructor() + { + CreateCompilation(@" +public class Test where T : System.Enum, new() +{ +} +public enum E1 +{ + A +} +public class Test2 +{ + public void M() where U : System.Enum, new() + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}").VerifyDiagnostics( + // (14,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(14, 26), + // (15,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Enum'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Enum", "T", "string").WithLocation(15, 26), + // (15,26): error CS0310: 'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(15, 26), + // (16,26): error CS0310: 'Enum' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var d = new Test(); // Enum type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "System.Enum").WithArguments("Test", "T", "System.Enum").WithLocation(16, 26)); + } + + [Fact] + public void EnumConstraint_Reference_Alone() + { + var reference = CreateCompilation(@" +public class Test where T : System.Enum +{ +}" + ).EmitToImageReference(); + + var code = @" +public enum E1 +{ + A +} + +public class Test2 +{ + public void M() where U : System.Enum + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (12,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(12, 26), + // (13,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Enum'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Enum", "T", "string").WithLocation(13, 26)); + } + + [Fact] + public void EnumConstraint_Reference_ReferenceType() + { + var reference = CreateCompilation(@" +public class Test where T : class, System.Enum +{ +}" + ).EmitToImageReference(); + + var code = @" +public enum E1 +{ + A +} + +public class Test2 +{ + public void M() where U : class, System.Enum + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (11,26): error CS0452: The type 'E1' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var a = new Test(); // enum + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "E1").WithArguments("Test", "T", "E1").WithLocation(11, 26), + // (12,26): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("Test", "T", "int").WithLocation(12, 26), + // (13,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Enum'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Enum", "T", "string").WithLocation(13, 26)); + } + + [Fact] + public void EnumConstraint_Reference_ValueType() + { + var reference = CreateCompilation(@" +public class Test where T : struct, System.Enum +{ +}" + ).EmitToImageReference(); + + var code = @" +public enum E1 +{ + A +} + +public class Test2 +{ + public void M() where U : struct, System.Enum + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (12,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(12, 26), + // (13,26): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(13, 26), + // (14,26): error CS0453: The type 'Enum' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Test' + // var d = new Test(); // Enum type + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "System.Enum").WithArguments("Test", "T", "System.Enum").WithLocation(14, 26)); + } + + [Fact] + public void EnumConstraint_Reference_Constructor() + { + var reference = CreateCompilation(@" +public class Test where T : System.Enum, new() +{ +}" + ).EmitToImageReference(); + + var code = @" +public enum E1 +{ + A +} + +public class Test2 +{ + public void M() where U : System.Enum, new() + { + var a = new Test(); // enum + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // Enum type + var e = new Test(); // Generic type constrained to enum + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (12,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(12, 26), + // (13,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Enum'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Enum", "T", "string").WithLocation(13, 26), + // (13,26): error CS0310: 'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(13, 26), + // (14,26): error CS0310: 'Enum' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var d = new Test(); // Enum type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "System.Enum").WithArguments("Test", "T", "System.Enum").WithLocation(14, 26)); + } + + [Fact] + public void EnumConstraint_Before_7_3() + { + var code = @" +public class Test where T : System.Enum +{ +}"; + + CreateCompilation(code, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp7_2)).VerifyDiagnostics( + // (2,32): error CS8320: Feature 'enum generic type constraints' is not available in C# 7.2. Please use language version 7.3 or greater. + // public class Test where T : System.Enum + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_2, "System.Enum").WithArguments("enum generic type constraints", "7.3").WithLocation(2, 32)); + } + + [Theory] + [InlineData("byte")] + [InlineData("sbyte")] + [InlineData("short")] + [InlineData("ushort")] + [InlineData("int")] + [InlineData("uint")] + [InlineData("long")] + [InlineData("ulong")] + public void EnumConstraint_DifferentBaseTypes(string type) + { + CreateCompilation($@" +public class Test where T : System.Enum +{{ +}} +public enum E1 : {type} +{{ + A +}} +public class Test2 +{{ + public void M() + {{ + var a = new Test(); // Valid + var b = new Test(); // Invalid + }} +}} +").VerifyDiagnostics( + // (14,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Enum'. + // var b = new Test(); // Invalid + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Enum", "T", "int").WithLocation(14, 26)); + } + + [Fact] + public void EnumConstraint_InheritanceChain() + { + CreateCompilation(@" +public enum E +{ + A +} +public class Test where U : System.Enum, T +{ +} +public class Test2 +{ + public void M() + { + var a = new Test(); + + var b = new Test(); + var c = new Test(); + + var d = new Test(); + var e = new Test(); + } +}").VerifyDiagnostics( + // (13,33): error CS0315: The type 'E' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no boxing conversion from 'E' to 'Test2'. + // var a = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "E").WithArguments("Test", "Test2", "U", "E").WithLocation(13, 33), + // (18,29): error CS0311: The type 'System.Enum' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'System.Enum' to 'E'. + // var d = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "System.Enum").WithArguments("Test", "E", "U", "System.Enum").WithLocation(18, 29)); + } + + [Fact] + public void EnumConstraint_IsReflectedinSymbols_Alone() + { + var code = "public class Test where T : System.Enum { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.Equal(SpecialType.System_Enum, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void EnumConstraint_IsReflectedinSymbols_ValueType() + { + var code = "public class Test where T : struct, System.Enum { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_Enum, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void EnumConstraint_IsReflectedinSymbols_ReferenceType() + { + var code = "public class Test where T : class, System.Enum { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_Enum, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void EnumConstraint_IsReflectedinSymbols_Constructor() + { + var code = "public class Test where T : System.Enum, new() { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.True(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_Enum, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void EnumConstraint_EnforcedInInheritanceChain_Downwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : System.Enum; +} +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + } +} +public enum E +{ +}").VerifyDiagnostics( + // (12,14): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'B.M()'. There is no boxing conversion from 'int' to 'System.Enum'. + // this.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("B.M()", "System.Enum", "T", "int").WithLocation(12, 14) + ); + } + + [Fact] + public void EnumConstraint_EnforcedInInheritanceChain_Downwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : System.Enum; +}").EmitToImageReference(); + + CreateCompilation(@" +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + } +} +public enum E +{ +}", references: new[] { reference }).VerifyDiagnostics( + // (8,14): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'B.M()'. There is no boxing conversion from 'int' to 'System.Enum'. + // this.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("B.M()", "System.Enum", "T", "int").WithLocation(8, 14) + ); + } + + [Fact] + public void EnumConstraint_EnforcedInInheritanceChain_Upwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +} +public class B : A +{ + public override void M() where T : System.Enum { } +}").VerifyDiagnostics( + // (8,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : System.Enum { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(8, 33)); + } + + [Fact] + public void EnumConstraint_EnforcedInInheritanceChain_Upwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +}").EmitToImageReference(); + + CreateCompilation(@" +public class B : A +{ + public override void M() where T : System.Enum { } +}", references: new[] { reference }).VerifyDiagnostics( + // (4,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : System.Enum { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(4, 33)); + } + + [Fact] + public void EnumConstraint_ResolveParentConstraints() + { + var comp = CreateCompilation(@" +public enum MyEnum +{ +} +public abstract class A +{ + public abstract void F() where U : System.Enum, T; +} +public class B : A +{ + public override void F() { } +}"); + + Action validator = module => + { + var method = module.GlobalNamespace.GetTypeMember("B").GetMethod("F"); + var constraintTypeNames = method.TypeParameters.Single().ConstraintTypes().Select(type => type.ToTestDisplayString()); + + AssertEx.SetEqual(new[] { "System.Enum", "MyEnum" }, constraintTypeNames); + }; + + CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void EnumConstraint_TypeNotAvailable() + { + CreateEmptyCompilation(@" +namespace System +{ + public class Object {} + public class Void {} +} +public class Test where T : System.Enum +{ +}").VerifyDiagnostics( + // (7,39): error CS0234: The type or namespace name 'Enum' does not exist in the namespace 'System' (are you missing an assembly reference?) + // public class Test where T : System.Enum + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "Enum").WithArguments("Enum", "System").WithLocation(7, 39)); + } + + [Fact] + public void EnumConstraint_BindingToMethods() + { + var code = @" +enum A : short { a } +enum B : uint { b } +class Test +{ + public static void Main() + { + Print(A.a); + Print(B.b); + } + static void Print(T obj) where T : System.Enum + { + System.Console.WriteLine(obj.GetTypeCode()); + } +}"; + + CompileAndVerify(code, expectedOutput: @" +Int16 +UInt32"); + } + + [Fact] + public void DelegateConstraint_Compilation_Alone() + { + CreateCompilation(@" +public class Test where T : System.Delegate +{ +} +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.Delegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // delegate type + } +}").VerifyDiagnostics( + // (11,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Delegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Delegate", "T", "int").WithLocation(11, 26), + // (12,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Delegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Delegate", "T", "string").WithLocation(12, 26)); + } + + [Fact] + public void DelegateConstraint_Compilation_ReferenceType() + { + CreateCompilation(@" +public class Test where T : class, System.Delegate +{ +} +public delegate void D1(); +public class Test2 +{ + public void M() where U : class, System.Delegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // delegate type + } +}").VerifyDiagnostics( + // (11,26): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("Test", "T", "int").WithLocation(11, 26), + // (12,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Delegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Delegate", "T", "string").WithLocation(12, 26)); + } + + [Fact] + public void DelegateConstraint_Compilation_ValueType() + { + CreateCompilation(@" +public class Test where T : struct, System.Delegate +{ +}").VerifyDiagnostics( + // (2,40): error CS0450: 'Delegate': cannot specify both a constraint class and the 'class' or 'struct' constraint + // public class Test where T : struct, System.Delegate + Diagnostic(ErrorCode.ERR_RefValBoundWithClass, "System.Delegate").WithArguments("System.Delegate").WithLocation(2, 40) + ); + } + + [Fact] + public void DelegateConstraint_Compilation_Constructor() + { + CreateCompilation(@" +public class Test where T : System.Delegate, new() +{ +} +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.Delegate, new() + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // delegate type + } +}").VerifyDiagnostics( + // (10,26): error CS0310: 'D1' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var a = new Test(); // delegate + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "D1").WithArguments("Test", "T", "D1").WithLocation(10, 26), + // (11,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Delegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Delegate", "T", "int").WithLocation(11, 26), + // (12,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Delegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Delegate", "T", "string").WithLocation(12, 26), + // (12,26): error CS0310: 'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(12, 26)); + } + + [Fact] + public void DelegateConstraint_Reference_Alone() + { + var reference = CreateCompilation(@" +public class Test where T : System.Delegate +{ +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.Delegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // delegate type + } +}", references: new[] { reference }).VerifyDiagnostics( + // (8,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Delegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Delegate", "T", "int").WithLocation(8, 26), + // (9,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Delegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Delegate", "T", "string").WithLocation(9, 26)); + } + + [Fact] + public void DelegateConstraint_Reference_ReferenceType() + { + var reference = CreateCompilation(@" +public class Test where T : class, System.Delegate +{ +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class Test2 +{ + public void M() where U : class, System.Delegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // delegate type + } +}", references: new[] { reference }).VerifyDiagnostics( + // (8,26): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("Test", "T", "int").WithLocation(8, 26), + // (9,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Delegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Delegate", "T", "string").WithLocation(9, 26)); + } + + [Fact] + public void DelegateConstraint_Reference_Constructor() + { + var reference = CreateCompilation(@" +public class Test where T : System.Delegate, new() +{ +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.Delegate, new() + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // delegate type + } +}", references: new[] { reference }).VerifyDiagnostics( + // (7,26): error CS0310: 'D1' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var a = new Test(); // delegate + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "D1").WithArguments("Test", "T", "D1").WithLocation(7, 26), + // (8,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.Delegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.Delegate", "T", "int").WithLocation(8, 26), + // (9,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.Delegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.Delegate", "T", "string").WithLocation(9, 26), + // (9,26): error CS0310: 'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(9, 26)); + } + + [Fact] + public void DelegateConstraint_Before_7_3() + { + var code = @" +public class Test where T : System.Delegate +{ +}"; + + CreateCompilation(code, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp7_2)).VerifyDiagnostics( + // (2,32): error CS8320: Feature 'delegate generic type constraints' is not available in C# 7.2. Please use language version 7.3 or greater. + // public class Test where T : System.Delegate + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_2, "System.Delegate").WithArguments("delegate generic type constraints", "7.3").WithLocation(2, 32)); + } + + [Fact] + public void DelegateConstraint_InheritanceChain() + { + CreateCompilation(@" +public delegate void D1(); +public class Test where U : System.Delegate, T +{ +} +public class Test2 +{ + public void M() + { + var a = new Test(); + + var b = new Test(); + var c = new Test(); + var d = new Test(); + var e = new Test(); + var f = new Test(); + + var g = new Test(); + var h = new Test(); + } +}").VerifyDiagnostics( + // (10,33): error CS0311: The type 'D1' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'D1' to 'Test2'. + // var a = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "D1").WithArguments("Test", "Test2", "U", "D1").WithLocation(10, 33), + // (14,52): error CS0311: The type 'System.Delegate' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'System.Delegate' to 'System.MulticastDelegate'. + // var d = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "System.Delegate").WithArguments("Test", "System.MulticastDelegate", "U", "System.Delegate").WithLocation(14, 52), + // (18,30): error CS0311: The type 'System.Delegate' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'System.Delegate' to 'D1'. + // var g = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "System.Delegate").WithArguments("Test", "D1", "U", "System.Delegate").WithLocation(18, 30)); + } + + [Fact] + public void DelegateConstraint_IsReflectedinSymbols_Alone() + { + var code = "public class Test where T : System.Delegate { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.Equal(SpecialType.System_Delegate, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void DelegateConstraint_IsReflectedinSymbols_ValueType() + { + var compilation = CreateCompilation("public class Test where T : struct, System.Delegate { }") + .VerifyDiagnostics( + // (1,40): error CS0450: 'Delegate': cannot specify both a constraint class and the 'class' or 'struct' constraint + // public class Test where T : struct, System.Delegate { } + Diagnostic(ErrorCode.ERR_RefValBoundWithClass, "System.Delegate").WithArguments("System.Delegate").WithLocation(1, 40) + ); + + var typeParameter = compilation.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + } + + [Fact] + public void DelegateConstraint_IsReflectedinSymbols_ReferenceType() + { + var code = "public class Test where T : class, System.Delegate { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_Delegate, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void DelegateConstraint_IsReflectedinSymbols_Constructor() + { + var code = "public class Test where T : System.Delegate, new() { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.True(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_Delegate, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void DelegateConstraint_EnforcedInInheritanceChain_Downwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : System.Delegate; +} +public delegate void D1(); +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + } +}").VerifyDiagnostics( + // (13,14): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'B.M()'. There is no boxing conversion from 'int' to 'System.Delegate'. + // this.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("B.M()", "System.Delegate", "T", "int").WithLocation(13, 14) + ); + } + + [Fact] + public void DelegateConstraint_EnforcedInInheritanceChain_Downwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : System.Delegate; +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + } +}", references: new[] { reference }).VerifyDiagnostics( + // (9,14): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'B.M()'. There is no boxing conversion from 'int' to 'System.Delegate'. + // this.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("B.M()", "System.Delegate", "T", "int").WithLocation(9, 14) + ); + } + + [Fact] + public void DelegateConstraint_EnforcedInInheritanceChain_Upwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +} +public class B : A +{ + public override void M() where T : System.Delegate { } +}").VerifyDiagnostics( + // (8,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : System.Delegate { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(8, 33)); + } + + [Fact] + public void DelegateConstraint_EnforcedInInheritanceChain_Upwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +}").EmitToImageReference(); + + CreateCompilation(@" +public class B : A +{ + public override void M() where T : System.Delegate { } +}", references: new[] { reference }).VerifyDiagnostics( + // (4,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : System.Delegate { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(4, 33)); + } + + [Fact] + public void DelegateConstraint_ResolveParentConstraints() + { + var comp = CreateCompilation(@" +public delegate void D1(); +public abstract class A +{ + public abstract void F() where U : System.Delegate, T; +} +public class B : A +{ + public override void F() { } +}"); + + Action validator = module => + { + var method = module.GlobalNamespace.GetTypeMember("B").GetMethod("F"); + var constraintTypeNames = method.TypeParameters.Single().ConstraintTypes().Select(type => type.ToTestDisplayString()); + + AssertEx.SetEqual(new[] { "System.Delegate", "D1" }, constraintTypeNames); + }; + + CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void DelegateConstraint_TypeNotAvailable() + { + CreateEmptyCompilation(@" +namespace System +{ + public class Object {} + public class Void {} +} +public class Test where T : System.Delegate +{ +}").VerifyDiagnostics( + // (7,39): error CS0234: The type or namespace name 'Delegate' does not exist in the namespace 'System' (are you missing an assembly reference?) + // public class Test where T : System.Delegate + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "Delegate").WithArguments("Delegate", "System").WithLocation(7, 39)); + } + + [Fact] + public void DelegateConstraint_BindingToMethods() + { + var code = @" +delegate void D1(int a, int b); +class TestClass +{ + public static void Impl(int a, int b) + { + System.Console.WriteLine($""Got {a} and {b}""); + } + public static void Main() + { + Test(Impl); + } + public static void Test(T obj) where T : System.Delegate + { + obj.DynamicInvoke(2, 3); + obj.DynamicInvoke(7, 9); + } +}"; + + CompileAndVerify(code, expectedOutput: @" +Got 2 and 3 +Got 7 and 9"); + } + + [Fact] + public void MulticastDelegateConstraint_Compilation_Alone() + { + CreateCompilation(@" +public class Test where T : System.MulticastDelegate +{ +} +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.MulticastDelegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // multicast delegate type + } +}").VerifyDiagnostics( + // (11,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.MulticastDelegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.MulticastDelegate", "T", "int").WithLocation(11, 26), + // (12,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.MulticastDelegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.MulticastDelegate", "T", "string").WithLocation(12, 26)); + } + + [Fact] + public void MulticastDelegateConstraint_Compilation_ReferenceType() + { + CreateCompilation(@" +public class Test where T : class, System.MulticastDelegate +{ +} +public delegate void D1(); +public class Test2 +{ + public void M() where U : class, System.MulticastDelegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // multicast delegate type + } +}").VerifyDiagnostics( + // (11,26): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("Test", "T", "int").WithLocation(11, 26), + // (12,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.MulticastDelegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.MulticastDelegate", "T", "string").WithLocation(12, 26)); + } + + [Fact] + public void MulticastDelegateConstraint_Compilation_ValueType() + { + CreateCompilation(@" +public class Test where T : struct, System.MulticastDelegate +{ +}").VerifyDiagnostics( + // (2,40): error CS0450: 'MulticastDelegate': cannot specify both a constraint class and the 'class' or 'struct' constraint + // public class Test where T : struct, System.MulticastDelegate + Diagnostic(ErrorCode.ERR_RefValBoundWithClass, "System.MulticastDelegate").WithArguments("System.MulticastDelegate").WithLocation(2, 40) + ); + } + + [Fact] + public void MulticastDelegateConstraint_Compilation_Constructor() + { + CreateCompilation(@" +public class Test where T : System.MulticastDelegate, new() +{ +} +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.MulticastDelegate, new() + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // multicast delegate type + } +}").VerifyDiagnostics( + // (10,26): error CS0310: 'D1' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var a = new Test(); // delegate + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "D1").WithArguments("Test", "T", "D1").WithLocation(10, 26), + // (11,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.MulticastDelegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.MulticastDelegate", "T", "int").WithLocation(11, 26), + // (12,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.MulticastDelegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.MulticastDelegate", "T", "string").WithLocation(12, 26), + // (12,26): error CS0310: 'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(12, 26)); + } + + [Fact] + public void MulticastDelegateConstraint_Reference_Alone() + { + var reference = CreateCompilation(@" +public class Test where T : System.MulticastDelegate +{ +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.MulticastDelegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // multicast delegate type + } +}", references: new[] { reference }).VerifyDiagnostics( + // (8,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.MulticastDelegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.MulticastDelegate", "T", "int").WithLocation(8, 26), + // (9,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.MulticastDelegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.MulticastDelegate", "T", "string").WithLocation(9, 26)); + } + + [Fact] + public void MulticastDelegateConstraint_Reference_ReferenceType() + { + var reference = CreateCompilation(@" +public class Test where T : class, System.MulticastDelegate +{ +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class Test2 +{ + public void M() where U : class, System.MulticastDelegate + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // multicast delegate type + } +}", references: new[] { reference }).VerifyDiagnostics( + // (8,26): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("Test", "T", "int").WithLocation(8, 26), + // (9,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.MulticastDelegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.MulticastDelegate", "T", "string").WithLocation(9, 26)); + } + + [Fact] + public void MulticastDelegateConstraint_Reference_Constructor() + { + var reference = CreateCompilation(@" +public class Test where T : System.MulticastDelegate, new() +{ +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class Test2 +{ + public void M() where U : System.MulticastDelegate, new() + { + var a = new Test(); // delegate + var b = new Test(); // value type + var c = new Test(); // reference type + var d = new Test(); // multicast delegate type + } +}", references: new[] { reference }).VerifyDiagnostics( + // (7,26): error CS0310: 'D1' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var a = new Test(); // delegate + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "D1").WithArguments("Test", "T", "D1").WithLocation(7, 26), + // (8,26): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no boxing conversion from 'int' to 'System.MulticastDelegate'. + // var b = new Test(); // value type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "int").WithArguments("Test", "System.MulticastDelegate", "T", "int").WithLocation(8, 26), + // (9,26): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test'. There is no implicit reference conversion from 'string' to 'System.MulticastDelegate'. + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "string").WithArguments("Test", "System.MulticastDelegate", "T", "string").WithLocation(9, 26), + // (9,26): error CS0310: 'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_NewConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(9, 26)); + } + + [Fact] + public void MulticastDelegateConstraint_Before_7_3() + { + var code = @" +public class Test where T : System.MulticastDelegate +{ +}"; + + CreateCompilation(code, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp7_2)).VerifyDiagnostics( + // (2,32): error CS8320: Feature 'delegate generic type constraints' is not available in C# 7.2. Please use language version 7.3 or greater. + // public class Test where T : System.MulticastDelegate + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_2, "System.MulticastDelegate").WithArguments("delegate generic type constraints", "7.3").WithLocation(2, 32)); + } + + [Fact] + public void MulticastDelegateConstraint_InheritanceChain() + { + CreateCompilation(@" +public delegate void D1(); +public class Test where U : System.MulticastDelegate, T +{ +} +public class Test2 +{ + public void M() + { + var a = new Test(); + + var b = new Test(); + var c = new Test(); + var d = new Test(); + var e = new Test(); + var f = new Test(); + + var g = new Test(); + var h = new Test(); + } +}").VerifyDiagnostics( + // (10,33): error CS0311: The type 'D1' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'D1' to 'Test2'. + // var a = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "D1").WithArguments("Test", "Test2", "U", "D1").WithLocation(10, 33), + // (15,52): error CS0311: The type 'System.Delegate' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'System.Delegate' to 'System.MulticastDelegate'. + // var e = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "System.Delegate").WithArguments("Test", "System.MulticastDelegate", "U", "System.Delegate").WithLocation(15, 52), + // (16,43): error CS0311: The type 'System.Delegate' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'System.Delegate' to 'System.MulticastDelegate'. + // var f = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "System.Delegate").WithArguments("Test", "System.MulticastDelegate", "U", "System.Delegate").WithLocation(16, 43), + // (18,30): error CS0311: The type 'System.MulticastDelegate' cannot be used as type parameter 'U' in the generic type or method 'Test'. There is no implicit reference conversion from 'System.MulticastDelegate' to 'D1'. + // var g = new Test(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "System.MulticastDelegate").WithArguments("Test", "D1", "U", "System.MulticastDelegate").WithLocation(18, 30)); + } + + [Fact] + public void MulticastDelegateConstraint_IsReflectedinSymbols_Alone() + { + var code = "public class Test where T : System.MulticastDelegate { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.Equal(SpecialType.System_MulticastDelegate, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void MulticastDelegateConstraint_IsReflectedinSymbols_ValueType() + { + var compilation = CreateCompilation("public class Test where T : struct, System.MulticastDelegate { }") + .VerifyDiagnostics( + // (1,40): error CS0450: 'MulticastDelegate': cannot specify both a constraint class and the 'class' or 'struct' constraint + // public class Test where T : struct, System.MulticastDelegate { } + Diagnostic(ErrorCode.ERR_RefValBoundWithClass, "System.MulticastDelegate").WithArguments("System.MulticastDelegate").WithLocation(1, 40) + ); + + var typeParameter = compilation.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + } + + [Fact] + public void MulticastDelegateConstraint_IsReflectedinSymbols_ReferenceType() + { + var code = "public class Test where T : class, System.MulticastDelegate { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_MulticastDelegate, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void MulticastDelegateConstraint_IsReflectedinSymbols_Constructor() + { + var code = "public class Test where T : System.MulticastDelegate, new() { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.True(typeParameter.HasConstructorConstraint); + Assert.Equal(SpecialType.System_MulticastDelegate, typeParameter.ConstraintTypes().Single().SpecialType); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void MulticastDelegateConstraint_EnforcedInInheritanceChain_Downwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : System.MulticastDelegate; +} +public delegate void D1(); +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + } +}").VerifyDiagnostics( + // (13,14): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'B.M()'. There is no boxing conversion from 'int' to 'System.MulticastDelegate'. + // this.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("B.M()", "System.MulticastDelegate", "T", "int").WithLocation(13, 14) + ); + } + + [Fact] + public void MulticastDelegateConstraint_EnforcedInInheritanceChain_Downwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : System.MulticastDelegate; +}").EmitToImageReference(); + + CreateCompilation(@" +public delegate void D1(); +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + } +}", references: new[] { reference }).VerifyDiagnostics( + // (9,14): error CS0315: The type 'int' cannot be used as type parameter 'T' in the generic type or method 'B.M()'. There is no boxing conversion from 'int' to 'System.MulticastDelegate'. + // this.M(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "M").WithArguments("B.M()", "System.MulticastDelegate", "T", "int").WithLocation(9, 14) + ); + } + + [Fact] + public void MulticastDelegateConstraint_EnforcedInInheritanceChain_Upwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +} +public class B : A +{ + public override void M() where T : System.MulticastDelegate { } +}").VerifyDiagnostics( + // (8,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : System.MulticastDelegate { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(8, 33)); + } + + [Fact] + public void MulticastDelegateConstraint_EnforcedInInheritanceChain_Upwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +}").EmitToImageReference(); + + CreateCompilation(@" +public class B : A +{ + public override void M() where T : System.MulticastDelegate { } +}", references: new[] { reference }).VerifyDiagnostics( + // (4,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : System.MulticastDelegate { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(4, 33)); + } + + [Fact] + public void MulticastDelegateConstraint_ResolveParentConstraints() + { + var comp = CreateCompilation(@" +public delegate void D1(); +public abstract class A +{ + public abstract void F() where U : System.MulticastDelegate, T; +} +public class B : A +{ + public override void F() { } +}"); + + Action validator = module => + { + var method = module.GlobalNamespace.GetTypeMember("B").GetMethod("F"); + var constraintTypeNames = method.TypeParameters.Single().ConstraintTypes().Select(type => type.ToTestDisplayString()); + + AssertEx.SetEqual(new[] { "System.MulticastDelegate", "D1" }, constraintTypeNames); + }; + + CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void MulticastDelegateConstraint_TypeNotAvailable() + { + CreateEmptyCompilation(@" +namespace System +{ + public class Object {} + public class Void {} +} +public class Test where T : System.MulticastDelegate +{ +}").VerifyDiagnostics( + // (7,39): error CS0234: The type or namespace name 'MulticastDelegate' does not exist in the namespace 'System' (are you missing an assembly reference?) + // public class Test where T : System.MulticastDelegate + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "MulticastDelegate").WithArguments("MulticastDelegate", "System").WithLocation(7, 39)); + } + + [Fact] + public void MulticastDelegateConstraint_BindingToMethods() + { + var code = @" +delegate void D1(int a, int b); +class TestClass +{ + public static void Impl(int a, int b) + { + System.Console.WriteLine($""Got {a} and {b}""); + } + public static void Main() + { + Test(Impl); + } + public static void Test(T obj) where T : System.MulticastDelegate + { + obj.DynamicInvoke(2, 3); + obj.DynamicInvoke(7, 9); + } +}"; + + CompileAndVerify(code, expectedOutput: @" +Got 2 and 3 +Got 7 and 9"); + } + + [Fact] + public void ConversionInInheritanceChain_MulticastDelegate() + { + var code = @" +class A where T : System.Delegate { } +class B : A where T : System.MulticastDelegate { }"; + + CreateCompilation(code).VerifyDiagnostics(); + + code = @" +class A where T : System.MulticastDelegate { } +class B : A where T : System.Delegate { }"; + + CreateCompilation(code).VerifyDiagnostics( + // (3,7): error CS0311: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'A'. There is no implicit reference conversion from 'T' to 'System.MulticastDelegate'. + // class B : A where T : System.Delegate { } + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "B").WithArguments("A", "System.MulticastDelegate", "T", "T").WithLocation(3, 7)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_Alone_Type() + { + CreateCompilation(@" +public class Test where T : unmanaged +{ +} +public struct GoodType { public int I; } +public struct BadType { public string S; } +public class Test2 +{ + public void M() where U : unmanaged + { + var a = new Test(); // unmanaged struct + var b = new Test(); // managed struct + var c = new Test(); // reference type + var d = new Test(); // value type + var e = new Test(); // generic type constrained to unmanaged + var f = new Test(); // unconstrained generic type + } +}").VerifyDiagnostics( + // (12,26): error CS8379: The type 'BadType' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // managed struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "BadType").WithArguments("Test", "T", "BadType").WithLocation(12, 26), + // (13,26): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(13, 26), + // (16,26): error CS8379: The type 'W' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test' + // var f = new Test(); // unconstrained generic type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "W").WithArguments("Test", "T", "W").WithLocation(16, 26)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_Alone_Method() + { + CreateCompilation(@" +public class Test +{ + public int M() where T : unmanaged => 0; +} +public struct GoodType { public int I; } +public struct BadType { public string S; } +public class Test2 +{ + public void M() where U : unmanaged + { + var a = new Test().M(); // unmanaged struct + var b = new Test().M(); // managed struct + var c = new Test().M(); // reference type + var d = new Test().M(); // value type + var e = new Test().M(); // generic type constrained to unmanaged + var f = new Test().M(); // unconstrained generic type + } +}").VerifyDiagnostics( + // (13,28): error CS8379: The type 'BadType' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M()' + // var b = new Test().M(); // managed struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M()", "T", "BadType").WithLocation(13, 28), + // (14,28): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M()' + // var c = new Test().M(); // reference type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M()", "T", "string").WithLocation(14, 28), + // (17,28): error CS8379: The type 'W' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M()' + // var f = new Test().M(); // unconstrained generic type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M()", "T", "W").WithLocation(17, 28) + ); + } + + [Fact] + public void UnmanagedConstraint_Compilation_Alone_Delegate() + { + CreateCompilation(@" +public delegate void D() where T : unmanaged; +public struct GoodType { public int I; } +public struct BadType { public string S; } +public abstract class Test2 where U : unmanaged +{ + public abstract D a(); // unmanaged struct + public abstract D b(); // managed struct + public abstract D c(); // reference type + public abstract D d(); // value type + public abstract D e(); // generic type constrained to unmanaged + public abstract D f(); // unconstrained generic type +}").VerifyDiagnostics( + // (8,32): error CS8379: The type 'BadType' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'D' + // public abstract D b(); // managed struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "b").WithArguments("D", "T", "BadType").WithLocation(8, 32), + // (9,31): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'D' + // public abstract D c(); // reference type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "c").WithArguments("D", "T", "string").WithLocation(9, 31), + // (12,26): error CS8379: The type 'W' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'D' + // public abstract D f(); // unconstrained generic type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "f").WithArguments("D", "T", "W").WithLocation(12, 26)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_Alone_LocalFunction() + { + CreateCompilation(@" +public abstract class Test2 where U : unmanaged +{ + public void M() + { + void local() where T : unmanaged { } + + local(); + } +}").VerifyDiagnostics( + // (6,20): error CS8380: Using unmanaged constraint on local functions type parameters is not supported. + // void local() where T : unmanaged { } + Diagnostic(ErrorCode.ERR_UnmanagedConstraintWithLocalFunctions, "T").WithLocation(6, 20)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_ReferenceType() + { + var c = CreateCompilation("public class Test where T : class, unmanaged {}"); + + c.VerifyDiagnostics( + // (1,39): error CS8380: The 'unmanaged' constraint must come before any other constraints + // public class Test where T : class, unmanaged {} + Diagnostic(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, "unmanaged").WithLocation(1, 39)); + + var typeParameter = c.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasUnmanagedTypeConstraint); + Assert.False(typeParameter.HasValueTypeConstraint); + Assert.True(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + } + + [Fact] + public void UnmanagedConstraint_Compilation_ValueType() + { + var c = CreateCompilation("public class Test where T : struct, unmanaged {}"); + + c.VerifyDiagnostics( + // (1,40): error CS8380: The 'unmanaged' constraint must come before any other constraints + // public class Test where T : struct, unmanaged {} + Diagnostic(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, "unmanaged").WithLocation(1, 40)); + + var typeParameter = c.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.False(typeParameter.HasUnmanagedTypeConstraint); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + } + + [Fact] + public void UnmanagedConstraint_Compilation_Constructor() + { + CreateCompilation("public class Test where T : unmanaged, new() {}").VerifyDiagnostics( + // (1,43): error CS8379: The 'new()' constraint cannot be used with the 'unmanaged' constraint + // public class Test where T : unmanaged, new() {} + Diagnostic(ErrorCode.ERR_NewBoundWithUnmanaged, "new").WithLocation(1, 43)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_AnotherType_Before() + { + CreateCompilation("public class Test where T : unmanaged, System.Exception { }").VerifyDiagnostics( + // (1,43): error CS8380: 'Exception': cannot specify both a constraint class and the 'unmanaged' constraint + // public class Test where T : unmanaged, System.Exception { } + Diagnostic(ErrorCode.ERR_UnmanagedBoundWithClass, "System.Exception").WithArguments("System.Exception").WithLocation(1, 43) + ); + } + + [Fact] + public void UnmanagedConstraint_Compilation_AnotherType_Before1() + { + CreateCompilation("public class Test where T : unmanaged, System.Enum { }").VerifyDiagnostics(); + + CreateCompilation("public class Test where T : unmanaged, System.Delegate { }").VerifyDiagnostics( + // (1,43): error CS8380: 'Delegate': cannot specify both a constraint class and the 'unmanaged' constraint + // public class Test where T : unmanaged, System.Delegate { } + Diagnostic(ErrorCode.ERR_UnmanagedBoundWithClass, "System.Delegate").WithArguments("System.Delegate").WithLocation(1, 43) + ); + } + + [Fact] + public void UnmanagedConstraint_Compilation_AnotherType_After() + { + CreateCompilation("public class Test where T : System.Exception, unmanaged { }").VerifyDiagnostics( + // (1,50): error CS8380: The 'unmanaged' constraint must come before any other constraints + // public class Test where T : System.Exception, unmanaged { } + Diagnostic(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, "unmanaged").WithLocation(1, 50)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_AnotherParameter_After() + { + CreateCompilation("public class Test where T : U, unmanaged { }").VerifyDiagnostics( + // (1,38): error CS8380: The 'unmanaged' constraint must come before any other constraints + // public class Test where T : U, unmanaged { } + Diagnostic(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, "unmanaged").WithLocation(1, 38)); + } + + [Fact] + public void UnmanagedConstraint_Compilation_AnotherParameter_Before() + { + CreateCompilation("public class Test where T : unmanaged, U { }").VerifyDiagnostics(); + CreateCompilation("public class Test where U: class where T : unmanaged, U, System.IDisposable { }").VerifyDiagnostics(); + } + + [Fact] + public void UnmanagedConstraint_UnmanagedEnumNotAvailable() + { + CreateEmptyCompilation(@" +namespace System +{ + public class Object {} + public class Void {} + public class ValueType {} +} +public class Test where T : unmanaged +{ +}").VerifyDiagnostics( + // (8,32): error CS0518: Predefined type 'System.Runtime.InteropServices.UnmanagedType' is not defined or imported + // public class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.Runtime.InteropServices.UnmanagedType").WithLocation(8, 32)); + } + + [Fact] + public void UnmanagedConstraint_ValueTypeNotAvailable() + { + CreateEmptyCompilation(@" +namespace System +{ + public class Object {} + public class Void {} + public class Enum {} + public class Int32 {} + namespace Runtime + { + namespace InteropServices + { + public enum UnmanagedType {} + } + } +} +public class Test where T : unmanaged +{ +}").VerifyDiagnostics( + // (16,32): error CS0518: Predefined type 'System.ValueType' is not defined or imported + // public class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "unmanaged").WithArguments("System.ValueType").WithLocation(16, 32)); + } + + [Fact] + public void UnmanagedConstraint_Reference_Alone_Type() + { + var reference = CreateCompilation(@" +public class Test where T : unmanaged +{ +}").EmitToImageReference(); + + var code = @" +public struct GoodType { public int I; } +public struct BadType { public string S; } +public class Test2 +{ + public void M() where U : unmanaged + { + var a = new Test(); // unmanaged struct + var b = new Test(); // managed struct + var c = new Test(); // reference type + var d = new Test(); // value type + var e = new Test(); // generic type constrained to unmanaged + var f = new Test(); // unconstrained generic type + } +}"; + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (9,26): error CS8379: The type 'BadType' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test' + // var b = new Test(); // managed struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "BadType").WithArguments("Test", "T", "BadType").WithLocation(9, 26), + // (10,26): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test' + // var c = new Test(); // reference type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "string").WithArguments("Test", "T", "string").WithLocation(10, 26), + // (13,26): error CS8379: The type 'W' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test' + // var f = new Test(); // unconstrained generic type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "W").WithArguments("Test", "T", "W").WithLocation(13, 26)); + } + + [Fact] + public void UnmanagedConstraint_Reference_Alone_Method() + { + var reference = CreateCompilation(@" +public class Test +{ + public int M() where T : unmanaged => 0; +}").EmitToImageReference(); + + var code = @" +public struct GoodType { public int I; } +public struct BadType { public string S; } +public class Test2 +{ + public void M() where U : unmanaged + { + var a = new Test().M(); // unmanaged struct + var b = new Test().M(); // managed struct + var c = new Test().M(); // reference type + var d = new Test().M(); // value type + var e = new Test().M(); // generic type constrained to unmanaged + var f = new Test().M(); // unconstrained generic type + } +}"; + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (9,28): error CS8379: The type 'BadType' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M()' + // var b = new Test().M(); // managed struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M()", "T", "BadType").WithLocation(9, 28), + // (10,28): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M()' + // var c = new Test().M(); // reference type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M()", "T", "string").WithLocation(10, 28), + // (13,28): error CS8379: The type 'W' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M()' + // var f = new Test().M(); // unconstrained generic type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M()", "T", "W").WithLocation(13, 28) + ); + } + + [Fact] + public void UnmanagedConstraint_Reference_Alone_Delegate() + { + var reference = CreateCompilation(@" +public delegate void D() where T : unmanaged; +").EmitToImageReference(); + + var code = @" +public struct GoodType { public int I; } +public struct BadType { public string S; } +public abstract class Test2 where U : unmanaged +{ + public abstract D a(); // unmanaged struct + public abstract D b(); // managed struct + public abstract D c(); // reference type + public abstract D d(); // value type + public abstract D e(); // generic type constrained to unmanaged + public abstract D f(); // unconstrained generic type +}"; + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // (7,32): error CS8379: The type 'BadType' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'D' + // public abstract D b(); // managed struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "b").WithArguments("D", "T", "BadType").WithLocation(7, 32), + // (8,31): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'D' + // public abstract D c(); // reference type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "c").WithArguments("D", "T", "string").WithLocation(8, 31), + // (11,26): error CS8379: The type 'W' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'D' + // public abstract D f(); // unconstrained generic type + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "f").WithArguments("D", "T", "W").WithLocation(11, 26)); + } + + [Fact] + public void UnmanagedConstraint_Before_7_3() + { + var code = @" +public class Test where T : unmanaged +{ +}"; + + CreateCompilation(code, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp7_2)).VerifyDiagnostics( + // (2,32): error CS8320: Feature 'unmanaged generic type constraints' is not available in C# 7.2. Please use language version 7.3 or greater. + // public class Test where T : unmanaged + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_2, "unmanaged").WithArguments("unmanaged generic type constraints", "7.3").WithLocation(2, 32)); + } + + [Fact] + public void UnmanagedConstraint_IsReflectedinSymbols_Alone_Type() + { + var code = "public class Test where T : unmanaged { }"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void UnmanagedConstraint_IsReflectedinSymbols_Alone_Method() + { + var code = @" +public class Test +{ + public void M() where T : unmanaged {} +}"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("Test").GetMethod("M").TypeParameters.Single(); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void UnmanagedConstraint_IsReflectedinSymbols_Alone_Delegate() + { + var code = "public delegate void D() where T : unmanaged;"; + + Action validator = module => + { + var typeParameter = module.GlobalNamespace.GetTypeMember("D").TypeParameters.Single(); + Assert.True(typeParameter.HasUnmanagedTypeConstraint); + Assert.True(typeParameter.HasValueTypeConstraint); + Assert.False(typeParameter.HasReferenceTypeConstraint); + Assert.False(typeParameter.HasConstructorConstraint); + Assert.Empty(typeParameter.ConstraintTypes()); + }; + + CompileAndVerify(code, sourceSymbolValidator: validator, symbolValidator: validator); + } + + [Fact] + public void UnmanagedConstraint_EnforcedInInheritanceChain_Downwards_Source() + { + CreateCompilation(@" +struct Test +{ + public string RefMember { get; set; } +} +public abstract class A +{ + public abstract void M() where T : unmanaged; +} +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + this.M(); + } +}").VerifyDiagnostics( + // (17,14): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'B.M()' + // this.M(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("B.M()", "T", "string").WithLocation(17, 14), + // (18,14): error CS8379: The type 'Test' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'B.M()' + // this.M(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("B.M()", "T", "Test").WithLocation(18, 14) + ); + } + + [Fact] + public void UnmanagedConstraint_EnforcedInInheritanceChain_Downwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M() where T : unmanaged; +}").EmitToImageReference(); + + CreateCompilation(@" +struct Test +{ + public string RefMember { get; set; } +} +public class B : A +{ + public override void M() { } + + public void Test() + { + this.M(); + this.M(); + this.M(); + } +}", references: new[] { reference }).VerifyDiagnostics( + // (13,14): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'B.M()' + // this.M(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("B.M()", "T", "string").WithLocation(13, 14), + // (14,14): error CS8379: The type 'Test' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'B.M()' + // this.M(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("B.M()", "T", "Test").WithLocation(14, 14) + ); + } + + [Fact] + public void UnmanagedConstraint_EnforcedInInheritanceChain_Upwards_Source() + { + CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +} +public class B : A +{ + public override void M() where T : unmanaged { } +}").VerifyDiagnostics( + // (8,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : unmanaged { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(8, 33)); + } + + [Fact] + public void UnmanagedConstraint_StructMismatchInImplements() + { + CreateCompilation(@" +public interface I1 where T : unmanaged +{ + void Test(G x) where G : unmanaged; +} + +public class C2 : I1 where T : struct +{ + public void Test(G x) where G : struct + { + I1 i = this; + i.Test(default(System.ArraySegment)); + } +} +").VerifyDiagnostics( + // (7,14): error CS8379: The type 'T' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'I1' + // public class C2 : I1 where T : struct + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "C2").WithArguments("I1", "T", "T").WithLocation(7, 14), + // (9,17): error CS0425: The constraints for type parameter 'G' of method 'C2.Test(G)' must match the constraints for type parameter 'G' of interface method 'I1.Test(G)'. Consider using an explicit interface implementation instead. + // public void Test(G x) where G : struct + Diagnostic(ErrorCode.ERR_ImplBadConstraints, "Test").WithArguments("G", "C2.Test(G)", "G", "I1.Test(G)").WithLocation(9, 17), + // (11,12): error CS8379: The type 'T' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'I1' + // I1 i = this; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "T").WithArguments("I1", "T", "T").WithLocation(11, 12), + // (12,11): error CS8379: The type 'ArraySegment' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'G' in the generic type or method 'I1.Test(G)' + // i.Test(default(System.ArraySegment)); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "Test").WithArguments("I1.Test(G)", "G", "System.ArraySegment").WithLocation(12, 11) + ); + } + + [Fact] + public void UnmanagedConstraint_TypeMismatchInImplements() + { + CreateCompilation(@" +public interface I1 where T : unmanaged, System.IDisposable +{ + void Test(G x) where G : unmanaged, System.Enum; +} + +public class C2 : I1 where T : unmanaged +{ + public void Test(G x) where G : unmanaged + { + I1 i = this; + i.Test(default(System.AttributeTargets)); // <-- this one is OK + i.Test(0); + } +} +").VerifyDiagnostics( + // (7,14): error CS0314: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'I1'. There is no boxing conversion or type parameter conversion from 'T' to 'System.IDisposable'. + // public class C2 : I1 where T : unmanaged + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "C2").WithArguments("I1", "System.IDisposable", "T", "T").WithLocation(7, 14), + // (9,17): error CS0425: The constraints for type parameter 'G' of method 'C2.Test(G)' must match the constraints for type parameter 'G' of interface method 'I1.Test(G)'. Consider using an explicit interface implementation instead. + // public void Test(G x) where G : unmanaged + Diagnostic(ErrorCode.ERR_ImplBadConstraints, "Test").WithArguments("G", "C2.Test(G)", "G", "I1.Test(G)").WithLocation(9, 17), + // (11,12): error CS0314: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'I1'. There is no boxing conversion or type parameter conversion from 'T' to 'System.IDisposable'. + // I1 i = this; + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "T").WithArguments("I1", "System.IDisposable", "T", "T").WithLocation(11, 12), + // (13,11): error CS0315: The type 'int' cannot be used as type parameter 'G' in the generic type or method 'I1.Test(G)'. There is no boxing conversion from 'int' to 'System.Enum'. + // i.Test(0); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "Test").WithArguments("I1.Test(G)", "System.Enum", "G", "int").WithLocation(13, 11) + ); + } + + [Fact] + public void UnmanagedConstraint_TypeMismatchInImplementsMeta() + { + var reference = CreateCompilation(@" +public interface I1 where T : unmanaged, System.IDisposable +{ + void Test(G x) where G : unmanaged, System.Enum; +} +").EmitToImageReference(); + + CreateCompilation(@" +public class C2 : I1 where T : unmanaged +{ + public void Test(G x) where G : unmanaged + { + I1 i = this; + i.Test(default(System.AttributeTargets)); // <-- this one is OK + i.Test(0); + } +}", references: new[] { reference }).VerifyDiagnostics( + // (2,14): error CS0314: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'I1'. There is no boxing conversion or type parameter conversion from 'T' to 'System.IDisposable'. + // public class C2 : I1 where T : unmanaged + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "C2").WithArguments("I1", "System.IDisposable", "T", "T").WithLocation(2, 14), + // (4,17): error CS0425: The constraints for type parameter 'G' of method 'C2.Test(G)' must match the constraints for type parameter 'G' of interface method 'I1.Test(G)'. Consider using an explicit interface implementation instead. + // public void Test(G x) where G : unmanaged + Diagnostic(ErrorCode.ERR_ImplBadConstraints, "Test").WithArguments("G", "C2.Test(G)", "G", "I1.Test(G)").WithLocation(4, 17), + // (6,12): error CS0314: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'I1'. There is no boxing conversion or type parameter conversion from 'T' to 'System.IDisposable'. + // I1 i = this; + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar, "T").WithArguments("I1", "System.IDisposable", "T", "T").WithLocation(6, 12), + // (8,11): error CS0315: The type 'int' cannot be used as type parameter 'G' in the generic type or method 'I1.Test(G)'. There is no boxing conversion from 'int' to 'System.Enum'. + // i.Test(0); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "Test").WithArguments("I1.Test(G)", "System.Enum", "G", "int").WithLocation(8, 11) + ); + } + + [Fact] + public void UnmanagedConstraint_TypeMismatchInImplementsMeta2() + { + var reference = CreateCompilation(@" + public interface I1 + { + void Test(ref G x) where G : unmanaged, System.IDisposable; + } +").EmitToImageReference(); + + var reference1 = CreateCompilation(@" +public class C1 : I1 +{ + void I1.Test(ref G x) + { + x.Dispose(); + } +}", references: new[] { reference }).EmitToImageReference(); ; + + CompileAndVerify(@" +struct S : System.IDisposable +{ + public int a; + + public void Dispose() + { + a += 123; + } +} + +class Test +{ + static void Main() + { + S local = default; + I1 i = new C1(); + i.Test(ref local); + System.Console.WriteLine(local.a); + } +}", + + // NOTE: must pass verification (IDisposable constraint is copied over to the implementing method) + options: TestOptions.UnsafeReleaseExe, references: new[] { reference, reference1 }, verify: Verification.Passes, expectedOutput: "123"); + } + + [Fact] + public void UnmanagedConstraint_EnforcedInInheritanceChain_Upwards_Reference() + { + var reference = CreateCompilation(@" +public abstract class A +{ + public abstract void M(); +}").EmitToImageReference(); + + CreateCompilation(@" +public class B : A +{ + public override void M() where T : unmanaged { } +}", references: new[] { reference }).VerifyDiagnostics( + // (4,33): error CS0460: Constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly + // public override void M() where T : unmanaged { } + Diagnostic(ErrorCode.ERR_OverrideWithConstraints, "where").WithLocation(4, 33)); + } + + [Fact] + public void UnmanagedConstraints_PointerOperations_Invalid() + { + CreateCompilation(@" +class Test +{ + void M(T arg) where T : unmanaged + { + } + void N() + { + M(""test""); + } +}").VerifyDiagnostics( + // (9,9): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M(T)' + // M("test"); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M(T)", "T", "string").WithLocation(9, 9)); + } + + [Theory] + [InlineData("(sbyte)1", "System.SByte", 1)] + [InlineData("(byte)1", "System.Byte", 1)] + [InlineData("(short)1", "System.Int16", 2)] + [InlineData("(ushort)1", "System.UInt16", 2)] + [InlineData("(int)1", "System.Int32", 4)] + [InlineData("(uint)1", "System.UInt32", 4)] + [InlineData("(long)1", "System.Int64", 8)] + [InlineData("(ulong)1", "System.UInt64", 8)] + [InlineData("'a'", "System.Char", 2)] + [InlineData("(float)1", "System.Single", 4)] + [InlineData("(double)1", "System.Double", 8)] + [InlineData("(decimal)1", "System.Decimal", 16)] + [InlineData("false", "System.Boolean", 1)] + [InlineData("E.A", "E", 4)] + [InlineData("new S { a = 1, b = 2, c = 3 }", "S", 12)] + public void UnmanagedConstraints_PointerOperations_SimpleTypes(string arg, string type, int size) + { + CompileAndVerify(@" +enum E +{ + A +} +struct S +{ + public int a; + public int b; + public int c; +} +unsafe class Test +{ + static T* M(T arg) where T : unmanaged + { + T* ptr = &arg; + System.Console.WriteLine(ptr->GetType()); // method access + System.Console.WriteLine(sizeof(T)); // sizeof operator + + T* ar = stackalloc T [10]; + return ar; + } + static void Main() + { + M(" + arg + @"); + } +}", + options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: string.Join(Environment.NewLine, type, size)).VerifyIL("Test.M", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: conv.u + IL_0003: constrained. ""T"" + IL_0009: callvirt ""System.Type object.GetType()"" + IL_000e: call ""void System.Console.WriteLine(object)"" + IL_0013: sizeof ""T"" + IL_0019: call ""void System.Console.WriteLine(int)"" + IL_001e: ldc.i4.s 10 + IL_0020: conv.u + IL_0021: sizeof ""T"" + IL_0027: mul.ovf.un + IL_0028: localloc + IL_002a: ret +}"); + } + + [Fact] + public void UnmanagedConstraints_InterfaceMethod() + { + CompileAndVerify(@" +struct S : System.IDisposable +{ + public int a; + + public void Dispose() + { + a += 123; + } +} +unsafe class Test +{ + static void M(ref T arg) where T : unmanaged, System.IDisposable + { + arg.Dispose(); + + fixed(T* ptr = &arg) + { + ptr->Dispose(); + } + } + + static void Main() + { + S local = default; + M(ref local); + System.Console.WriteLine(local.a); + } +}", + options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails, expectedOutput: "246").VerifyIL("Test.M", @" +{ + // Code size 31 (0x1f) + .maxstack 1 + .locals init (pinned T& V_0) + IL_0000: ldarg.0 + IL_0001: constrained. ""T"" + IL_0007: callvirt ""void System.IDisposable.Dispose()"" + IL_000c: ldarg.0 + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: conv.u + IL_0010: constrained. ""T"" + IL_0016: callvirt ""void System.IDisposable.Dispose()"" + IL_001b: ldc.i4.0 + IL_001c: conv.u + IL_001d: stloc.0 + IL_001e: ret +}"); + } + + [Fact] + public void UnmanagedConstraints_CtorAndValueTypeAreEmitted() + { + CompileAndVerify(@" +using System.Linq; +class Program +{ + public static void M() where T: unmanaged + { + } + + static void Main(string[] args) + { + var typeParam = typeof(Program).GetMethod(""M"").GetGenericArguments().First(); + System.Console.WriteLine(typeParam.GenericParameterAttributes); + } +}", + options: TestOptions.UnsafeReleaseExe, verify: Verification.Passes, expectedOutput: "NotNullableValueTypeConstraint, DefaultConstructorConstraint"); + } + + [Fact] + public void UnmanagedConstraints_NestedStructs_Flat() + { + CompileAndVerify(@" +struct TestData +{ + public int A; + public TestData(int a) + { + A = a; + } +} +unsafe class Test +{ + public static void Main() + { + N(); + } + static void N() where T : unmanaged + { + System.Console.WriteLine(sizeof(T)); + } +}", options: TestOptions.UnsafeReleaseExe, verify: Verification.Passes, expectedOutput: "4"); + } + + [Fact] + public void UnmanagedConstraints_NestedStructs_Nested() + { + CompileAndVerify(@" +struct InnerTestData +{ + public int B; + public InnerTestData(int b) + { + B = b; + } +} +struct TestData +{ + public int A; + public InnerTestData B; + public TestData(int a, int b) + { + A = a; + B = new InnerTestData(b); + } +} +unsafe class Test +{ + public static void Main() + { + N(); + } + static void N() where T : unmanaged + { + System.Console.WriteLine(sizeof(T)); + } +}", options: TestOptions.UnsafeReleaseExe, verify: Verification.Passes, expectedOutput: "8"); + } + + [Fact] + public void UnmanagedConstraints_NestedStructs_Error() + { + CreateCompilation(@" +struct InnerTestData +{ + public string B; + public InnerTestData(string b) + { + B = b; + } +} +struct TestData +{ + public int A; + public InnerTestData B; + public TestData(int a, string b) + { + A = a; + B = new InnerTestData(b); + } +} +class Test +{ + public static void Main() + { + N(); + } + static void N() where T : unmanaged + { + } +}").VerifyDiagnostics( + // (24,9): error CS8379: The type 'TestData' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.N()' + // N(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "N").WithArguments("Test.N()", "T", "TestData").WithLocation(24, 9)); + } + + [Fact] + public void UnmanagedConstraints_ExistingUnmanagedKeywordType_InScope() + { + CompileAndVerify(@" +class unmanaged +{ + public void Print() + { + System.Console.WriteLine(""success""); + } +} +class Test +{ + public static void Main() + { + M(new unmanaged()); + } + static void M(T arg) where T : unmanaged + { + arg.Print(); + } +}", expectedOutput: "success"); + } + + [Fact] + public void UnmanagedConstraints_ExistingUnmanagedKeywordType_OutOfScope() + { + CreateCompilation(@" +namespace hidden +{ + class unmanaged + { + public void Print() + { + System.Console.WriteLine(""success""); + } + } +} +class Test +{ + public static void Main() + { + M(""test""); + } + static void M(T arg) where T : unmanaged + { + arg.Print(); + } +}").VerifyDiagnostics( + // (16,9): error CS8379: The type 'string' cannot be a reference type, or contain reference type fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'Test.M(T)' + // M("test"); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("Test.M(T)", "T", "string").WithLocation(16, 9), + // (20,13): error CS1061: 'T' does not contain a definition for 'Print' and no extension method 'Print' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?) + // arg.Print(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Print").WithArguments("T", "Print").WithLocation(20, 13)); + } + + [Fact] + public void UnmanagedConstraints_UnmanagedIsValidForStructConstraint_Methods() + { + CompileAndVerify(@" +class Program +{ + static void A(T arg) where T : struct + { + System.Console.WriteLine(arg); + } + static void B(T arg) where T : unmanaged + { + A(arg); + } + static void Main() + { + B(5); + } +}", expectedOutput: "5"); + } + + [Fact] + public void UnmanagedConstraints_UnmanagedIsValidForStructConstraint_Types() + { + CompileAndVerify(@" +class A where T : struct +{ + public void M(T arg) + { + System.Console.WriteLine(arg); + } +} +class B : A where T : unmanaged +{ +} +class Program +{ + static void Main() + { + new B().M(5); + } +}", expectedOutput: "5"); + } + + [Fact] + public void UnmanagedConstraints_UnmanagedIsValidForStructConstraint_Interfaces() + { + CompileAndVerify(@" +interface A where T : struct +{ + void M(T arg); +} +class B : A where T : unmanaged +{ + public void M(T arg) + { + System.Console.WriteLine(arg); + } +} +class Program +{ + static void Main() + { + new B().M(5); + } +}", expectedOutput: "5"); + } + + [Fact] + public void UnmanagedConstraints_PointerTypeSubstitution() + { + var compilation = CreateCompilation(@" +unsafe public class Test +{ + public T* M() where T : unmanaged => throw null; + + public void N() + { + var result = M(); + } +}", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree, ignoreAccessibility: true); + + var value = ((VariableDeclaratorSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.VariableDeclarator)).Initializer.Value; + Assert.Equal("M()", value.ToFullString()); + + var symbol = (MethodSymbol)model.GetSymbolInfo(value).Symbol; + Assert.Equal("System.Int32*", symbol.ReturnType.ToTestDisplayString()); + } + + [Fact] + public void UnmanagedConstraints_CannotConstraintToTypeParameterConstrainedByUnmanaged() + { + CreateCompilation(@" +class Test where U : unmanaged +{ + void M() where T : U + { + } +}").VerifyDiagnostics( + // (4,12): error CS8379: Type parameter 'U' has the 'unmanaged' constraint so 'U' cannot be used as a constraint for 'T' + // void M() where T : U + Diagnostic(ErrorCode.ERR_ConWithUnmanagedCon, "T").WithArguments("T", "U").WithLocation(4, 12)); + } + + [Fact] + public void UnmanagedConstraints_UnmanagedAsTypeConstraintName() + { + CreateCompilation(@" +class Test where unmanaged : System.IDisposable +{ + void M(T arg) where T : unmanaged + { + arg.Dispose(); + arg.NonExistentMethod(); + } +}").VerifyDiagnostics( + // (7,13): error CS1061: 'T' does not contain a definition for 'NonExistentMethod' and no extension method 'NonExistentMethod' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?) + // arg.NonExistentMethod(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "NonExistentMethod").WithArguments("T", "NonExistentMethod").WithLocation(7, 13)); + } + + [Fact] + public void UnmanagedConstraints_CircularReferenceToUnmanagedTypeWillBindSuccessfully() + { + CreateCompilation(@" +public unsafe class C where U : unmanaged +{ + public void M1() where T : T* { } + public void M2() where T : U* { } +}", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,35): error CS0706: Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // public void M2() where T : U* { } + Diagnostic(ErrorCode.ERR_BadConstraintType, "U*").WithLocation(5, 35), + // (4,35): error CS0706: Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // public void M1() where T : T* { } + Diagnostic(ErrorCode.ERR_BadConstraintType, "T*").WithLocation(4, 35)); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs index c0e2023b46339..ac8e5476ceafe 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs @@ -642,6 +642,11 @@ public static void Main() public void MissingIDisposable() { var source = @" +namespace System +{ + public class Object { } + public class Void { } +} class C { void M() @@ -651,30 +656,12 @@ void M() }"; CreateEmptyCompilation(source).VerifyDiagnostics( - // Related to the using statement: - - // (6,30): warning CS0642: Possible mistaken empty statement + // (11,20): error CS0815: Cannot assign to an implicitly-typed variable // using (var v = null) ; - Diagnostic(ErrorCode.WRN_PossibleMistakenNullStatement, ";"), - - // Cascading from the lack of mscorlib: - - // (2,7): error CS0518: Predefined type 'System.Object' is not defined or imported - // class C - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C").WithArguments("System.Object"), - // (4,5): error CS0518: Predefined type 'System.Void' is not defined or imported - // void M() - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "void").WithArguments("System.Void"), - // (6,16): error CS0518: Predefined type 'System.Object' is not defined or imported - // using (var v = null) ; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "var").WithArguments("System.Object"), - // (6,20): error CS0815: Cannot assign to an implicitly-typed variable + Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "v = null").WithArguments("").WithLocation(11, 20), + // (11,30): warning CS0642: Possible mistaken empty statement // using (var v = null) ; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "v = null").WithArguments(""), - // (2,7): error CS1729: 'object' does not contain a constructor that takes 0 arguments - // class C - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "C").WithArguments("object", "0") - ); + Diagnostic(ErrorCode.WRN_PossibleMistakenNullStatement, ";").WithLocation(11, 30)); } diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 3d8c257ced976..e24ecdfbcd7e1 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -6348,5 +6348,196 @@ Structure X SymbolDisplayPartKind.Space, SymbolDisplayPartKind.StructName); } + + [Fact] + public void EnumConstraint_Type() + { + TestSymbolDescription( + "class X where T : System.Enum { }", + global => global.GetTypeMember("X"), + SymbolDisplayFormat.TestFormat.WithGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints), + "X where T : System.Enum", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void EnumConstraint() + { + TestSymbolDescription( + "class X where T : System.Enum { }", + global => global.GetTypeMember("X").TypeParameters.Single().ConstraintTypes().Single(), + SymbolDisplayFormat.TestFormat, + "System.Enum", + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void DelegateConstraint_Type() + { + TestSymbolDescription( + "class X where T : System.Delegate { }", + global => global.GetTypeMember("X"), + SymbolDisplayFormat.TestFormat.WithGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints), + "X where T : System.Delegate", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void DelegateConstraint() + { + TestSymbolDescription( + "class X where T : System.Delegate { }", + global => global.GetTypeMember("X").TypeParameters.Single().ConstraintTypes().Single(), + SymbolDisplayFormat.TestFormat, + "System.Delegate", + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void MulticastDelegateConstraint_Type() + { + TestSymbolDescription( + "class X where T : System.MulticastDelegate { }", + global => global.GetTypeMember("X"), + SymbolDisplayFormat.TestFormat.WithGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints), + "X where T : System.MulticastDelegate", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void MulticastDelegateConstraint() + { + TestSymbolDescription( + "class X where T : System.MulticastDelegate { }", + global => global.GetTypeMember("X").TypeParameters.Single().ConstraintTypes().Single(), + SymbolDisplayFormat.TestFormat, + "System.MulticastDelegate", + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.ClassName); + } + + [Fact] + public void UnmanagedConstraint_Type() + { + TestSymbolDescription( + "class X where T : unmanaged { }", + global => global.GetTypeMember("X"), + SymbolDisplayFormat.TestFormat.AddGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeConstraints), + "X where T : unmanaged", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void UnmanagedConstraint_Method() + { + TestSymbolDescription(@" +class X +{ + void M() where T : unmanaged, System.IDisposable { } +}", + global => global.GetTypeMember("X").GetMethod("M"), + SymbolDisplayFormat.TestFormat.AddGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeConstraints), + "void X.M() where T : unmanaged, System.IDisposable", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.MethodName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.InterfaceName); + } + + [Fact] + public void UnmanagedConstraint_Delegate() + { + TestSymbolDescription( + "delegate void D() where T : unmanaged;", + global => global.GetTypeMember("D"), + SymbolDisplayFormat.TestFormat.AddGenericsOptions(SymbolDisplayGenericsOptions.IncludeTypeConstraints), + "D where T : unmanaged", + SymbolDisplayPartKind.DelegateName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.TypeParameterName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 2260876198811..10784e65eb872 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -562,6 +562,7 @@ public void AllWellKnownTypes() case WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute: case WellKnownType.System_Span_T: case WellKnownType.System_ReadOnlySpan_T: + case WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute: // Not yet in the platform. case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation: // Not always available. @@ -872,6 +873,7 @@ public void AllWellKnownTypeMembers() case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningMultipleFiles: case WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor: case WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor: + case WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor: // Not always available. continue; } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index bbe833a9a7a83..ec98d53fb3685 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -12314,9 +12314,8 @@ public void CS0702ERR_SpecialTypeAsBound() @"using System; interface IA where T : object { } interface IB where T : System.Object { } -interface IC where T : ValueType where U : Enum { } -interface ID where T : Array { } -interface IE where T : Delegate where U : MulticastDelegate { }"; +interface IC where T : ValueType { } +interface ID where T : Array { }"; CreateCompilation(source).VerifyDiagnostics( // (2,27): error CS0702: Constraint cannot be special class 'object' Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "object").WithArguments("object").WithLocation(2, 27), @@ -12324,14 +12323,8 @@ interface IE where T : Delegate where U : MulticastDelegate { }"; Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "System.Object").WithArguments("object").WithLocation(3, 27), // (4,30): error CS0702: Constraint cannot be special class 'System.ValueType' Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "ValueType").WithArguments("System.ValueType").WithLocation(4, 30), - // (4,50): error CS0702: Constraint cannot be special class 'System.Enum' - Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "Enum").WithArguments("System.Enum").WithLocation(4, 50), // (5,27): error CS0702: Constraint cannot be special class 'System.Array' - Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "Array").WithArguments("System.Array").WithLocation(5, 27), - // (6,30): error CS0702: Constraint cannot be special class 'System.Delegate' - Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "Delegate").WithArguments("System.Delegate").WithLocation(6, 30), - // (6,49): error CS0702: Constraint cannot be special class 'System.MulticastDelegate' - Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "MulticastDelegate").WithArguments("System.MulticastDelegate").WithLocation(6, 49)); + Diagnostic(ErrorCode.ERR_SpecialTypeAsBound, "Array").WithArguments("System.Array").WithLocation(5, 27)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index 55467a0cd4e5b..f50e2b6131453 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -1052,24 +1052,26 @@ void M() }"; CreateCompilation(source).VerifyDiagnostics( // (2,15): error CS0706: Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // where U : T* Diagnostic(ErrorCode.ERR_BadConstraintType, "T*").WithLocation(2, 15), // (3,15): error CS0706: Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // where V : T[] Diagnostic(ErrorCode.ERR_BadConstraintType, "T[]").WithLocation(3, 15), // (9,19): error CS0706: Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // where U : T* Diagnostic(ErrorCode.ERR_BadConstraintType, "T*").WithLocation(9, 19), // (10,19): error CS0706: Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter. + // where V : T[]; Diagnostic(ErrorCode.ERR_BadConstraintType, "T[]").WithLocation(10, 19), // CONSIDER: Dev10 doesn't report these cascading errors. // (2,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context - Diagnostic(ErrorCode.ERR_UnsafeNeeded, "T*"), - // (2,15): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T') - Diagnostic(ErrorCode.ERR_ManagedAddr, "T*").WithArguments("T"), + // where U : T* + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "T*").WithLocation(2, 15), // (9,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context - Diagnostic(ErrorCode.ERR_UnsafeNeeded, "T*"), - // (9,19): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('T') - Diagnostic(ErrorCode.ERR_ManagedAddr, "T*").WithArguments("T")); + // where U : T* + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "T*").WithLocation(9, 19)); } [Fact] diff --git a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs index 01478078c5a50..5d1a7f24ae2f0 100644 --- a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs +++ b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.IO; using System.Reflection.Metadata; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.PooledObjects; @@ -714,6 +713,9 @@ private ImmutableArray> DecodeModifiersOrThrow( case AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile: isAllowed = IsAcceptedVolatileModifierType(type); break; + case AllowedRequiredModifierType.System_Runtime_InteropServices_UnmanagedType: + isAllowed = IsAcceptedUnmanagedTypeModifierType(type); + break; } if (isAllowed) @@ -875,6 +877,58 @@ internal ImmutableArray> DecodeLocalSignatureOrThrow(ref B } } + internal TypeSymbol DecodeGenericParameterConstraint(EntityHandle token, out bool isUnmanagedConstraint) + { + isUnmanagedConstraint = false; + + switch (token.Kind) + { + case HandleKind.TypeSpecification: + { + try + { + var memoryReader = this.Module.GetTypeSpecificationSignatureReaderOrThrow((TypeSpecificationHandle)token); + var modifiers = DecodeModifiersOrThrow(ref memoryReader, AllowedRequiredModifierType.System_Runtime_InteropServices_UnmanagedType, out var typeCode, out var modReqFound); + var type = DecodeTypeOrThrow(ref memoryReader, typeCode, out _); + + if (modReqFound) + { + if (type.SpecialType == SpecialType.System_ValueType) + { + isUnmanagedConstraint = true; + } + else + { + return GetUnsupportedMetadataTypeSymbol(); + } + } + else if (!modifiers.IsDefaultOrEmpty) + { + // Optional modifiers (also, other required parameters) not allowed on generic parameters + // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528856 + return GetUnsupportedMetadataTypeSymbol(); + } + + return type; + } + catch (BadImageFormatException mrEx) + { + return GetUnsupportedMetadataTypeSymbol(mrEx); + } + catch (UnsupportedSignatureContent) + { + return GetUnsupportedMetadataTypeSymbol(); + } + } + case HandleKind.TypeReference: + return GetTypeOfTypeRef((TypeReferenceHandle)token, out _); + case HandleKind.TypeDefinition: + return GetTypeOfTypeDef((TypeDefinitionHandle)token); + default: + return GetUnsupportedMetadataTypeSymbol(); + } + } + /// If the encoded local variable type is invalid. /// An exception from metadata reader. internal LocalInfo DecodeLocalVariableOrThrow(ref BlobReader signatureReader) @@ -2393,6 +2447,7 @@ private enum AllowedRequiredModifierType None, System_Runtime_CompilerServices_Volatile, System_Runtime_InteropServices_InAttribute, + System_Runtime_InteropServices_UnmanagedType, } } } diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index 62fcfe8bfd844..b59a41e1b08c5 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -944,6 +944,11 @@ internal bool HasIsReadOnlyAttribute(EntityHandle token) return FindTargetAttribute(token, AttributeDescription.IsReadOnlyAttribute).HasValue; } + internal bool HasIsUnmanagedAttribute(EntityHandle token) + { + return FindTargetAttribute(token, AttributeDescription.IsUnmanagedAttribute).HasValue; + } + internal bool HasExtensionAttribute(EntityHandle token, bool ignoreCase) { return FindTargetAttribute(token, ignoreCase ? AttributeDescription.CaseInsensitiveExtensionAttribute : AttributeDescription.CaseSensitiveExtensionAttribute).HasValue; diff --git a/src/Compilers/Core/Portable/MetadataReader/SymbolFactory.cs b/src/Compilers/Core/Portable/MetadataReader/SymbolFactory.cs index b3dcff2d71bd9..75087772f0f86 100644 --- a/src/Compilers/Core/Portable/MetadataReader/SymbolFactory.cs +++ b/src/Compilers/Core/Portable/MetadataReader/SymbolFactory.cs @@ -42,6 +42,8 @@ internal abstract TypeSymbol GetMDArrayTypeSymbol(ModuleSymbol moduleSymbol, int internal abstract bool IsAcceptedVolatileModifierType(ModuleSymbol moduleSymbol, TypeSymbol type); internal abstract bool IsAcceptedInAttributeModifierType(TypeSymbol type); + internal abstract bool IsAcceptedUnmanagedTypeModifierType(TypeSymbol type); + internal abstract Cci.PrimitiveTypeCode GetPrimitiveTypeCode(ModuleSymbol moduleSymbol, TypeSymbol type); } } diff --git a/src/Compilers/Core/Portable/MetadataReader/TypeNameDecoder.cs b/src/Compilers/Core/Portable/MetadataReader/TypeNameDecoder.cs index d1b3825b99452..a206ac3b34c3d 100644 --- a/src/Compilers/Core/Portable/MetadataReader/TypeNameDecoder.cs +++ b/src/Compilers/Core/Portable/MetadataReader/TypeNameDecoder.cs @@ -98,6 +98,11 @@ protected bool IsAcceptedInAttributeModifierType(TypeSymbol type) return _factory.IsAcceptedInAttributeModifierType(type); } + protected bool IsAcceptedUnmanagedTypeModifierType(TypeSymbol type) + { + return _factory.IsAcceptedUnmanagedTypeModifierType(type); + } + protected Microsoft.Cci.PrimitiveTypeCode GetPrimitiveTypeCode(TypeSymbol type) { return _factory.GetPrimitiveTypeCode(this.moduleSymbol, type); diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 5dc6bd1ec7f78..bd8b1c9a54284 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -5,6 +5,7 @@ Microsoft.CodeAnalysis.Emit.EmitOptions.EmitOptions(bool metadataOnly, Microsoft Microsoft.CodeAnalysis.Emit.EmitOptions.PdbChecksumAlgorithm.get -> System.Security.Cryptography.HashAlgorithmName Microsoft.CodeAnalysis.Emit.EmitOptions.WithPdbChecksumAlgorithm(System.Security.Cryptography.HashAlgorithmName name) -> Microsoft.CodeAnalysis.Emit.EmitOptions Microsoft.CodeAnalysis.INamedTypeSymbol.IsSerializable.get -> bool +Microsoft.CodeAnalysis.ITypeParameterSymbol.HasUnmanagedTypeConstraint.get -> bool Microsoft.CodeAnalysis.MetadataImportOptions Microsoft.CodeAnalysis.MetadataImportOptions.All = 2 -> Microsoft.CodeAnalysis.MetadataImportOptions Microsoft.CodeAnalysis.MetadataImportOptions.Internal = 1 -> Microsoft.CodeAnalysis.MetadataImportOptions diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index ea72272358e4c..2e8ae248834d1 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -257,6 +257,7 @@ static AttributeDescription() private static readonly byte[][] s_signaturesOfInAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfOutAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfIsReadOnlyAttribute = { s_signature_HasThis_Void }; + private static readonly byte[][] s_signaturesOfIsUnmanagedAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfFixedBufferAttribute = { s_signature_HasThis_Void_Type_Int32 }; private static readonly byte[][] s_signaturesOfSuppressUnmanagedCodeSecurityAttribute = { s_signature_HasThis_Void }; private static readonly byte[][] s_signaturesOfPrincipalPermissionAttribute = { s_signature_HasThis_Void_SecurityAction }; @@ -453,6 +454,7 @@ static AttributeDescription() internal static readonly AttributeDescription InAttribute = new AttributeDescription("System.Runtime.InteropServices", "InAttribute", s_signaturesOfInAttribute); internal static readonly AttributeDescription OutAttribute = new AttributeDescription("System.Runtime.InteropServices", "OutAttribute", s_signaturesOfOutAttribute); internal static readonly AttributeDescription IsReadOnlyAttribute = new AttributeDescription("System.Runtime.CompilerServices", "IsReadOnlyAttribute", s_signaturesOfIsReadOnlyAttribute); + internal static readonly AttributeDescription IsUnmanagedAttribute = new AttributeDescription("System.Runtime.CompilerServices", "IsUnmanagedAttribute", s_signaturesOfIsUnmanagedAttribute); internal static readonly AttributeDescription CoClassAttribute = new AttributeDescription("System.Runtime.InteropServices", "CoClassAttribute", s_signaturesOfCoClassAttribute); internal static readonly AttributeDescription GuidAttribute = new AttributeDescription("System.Runtime.InteropServices", "GuidAttribute", s_signaturesOfGuidAttribute); internal static readonly AttributeDescription CLSCompliantAttribute = new AttributeDescription("System", "CLSCompliantAttribute", s_signaturesOfCLSCompliantAttribute); diff --git a/src/Compilers/Core/Portable/Symbols/ITypeParameterSymbol.cs b/src/Compilers/Core/Portable/Symbols/ITypeParameterSymbol.cs index 5a56c4ef83798..7225315d141e7 100644 --- a/src/Compilers/Core/Portable/Symbols/ITypeParameterSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/ITypeParameterSymbol.cs @@ -46,10 +46,15 @@ public interface ITypeParameterSymbol : ITypeSymbol bool HasReferenceTypeConstraint { get; } /// - /// True if the value type constraint (struct)was specified for the type parameter. + /// True if the value type constraint (struct) was specified for the type parameter. /// bool HasValueTypeConstraint { get; } + /// + /// True if the value type constraint (unmanaged) was specified for the type parameter. + /// + bool HasUnmanagedTypeConstraint { get; } + /// /// True if the parameterless constructor constraint (new()) was specified for the type parameter. /// diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 6e56401a52078..acb424ffaa580 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -431,6 +431,8 @@ internal enum WellKnownMember System_ReadOnlySpan_T__get_Item, System_ReadOnlySpan_T__get_Length, + System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor, + Count } } diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 167c33627f7e3..eed93b0f775ea 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -2968,6 +2968,14 @@ static WellKnownMembers() 0, // Arity 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor + (byte)(MemberFlags.Constructor), // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, + }; string[] allNames = new string[(int)WellKnownMember.Count] @@ -3338,6 +3346,7 @@ static WellKnownMembers() ".ctor", // System_ReadOnlySpan__ctor "get_Item", // System_ReadOnlySpan__get_Item "get_Length", // System_ReadOnlySpan__get_Length + ".ctor", // System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 05c42a71fff9d..3876f36b9078a 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -271,6 +271,8 @@ internal enum WellKnownType System_ObsoleteAttribute, System_Span_T, System_ReadOnlySpan_T, + System_Runtime_InteropServices_UnmanagedType, + System_Runtime_CompilerServices_IsUnmanagedAttribute, NextAvailable, } @@ -537,6 +539,8 @@ internal static class WellKnownTypes "System.ObsoleteAttribute", "System.Span`1", "System.ReadOnlySpan`1", + "System.Runtime.InteropServices.UnmanagedType", + "System.Runtime.CompilerServices.IsUnmanagedAttribute", }; private readonly static Dictionary s_nameToTypeIdMap = new Dictionary((int)Count); diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/SymbolFactory.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/SymbolFactory.vb index 2099086cd0f02..f00dca652a2d6 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/SymbolFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/SymbolFactory.vb @@ -54,6 +54,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Return False End Function + Friend Overrides Function IsAcceptedUnmanagedTypeModifierType(type As TypeSymbol) As Boolean + ' VB doesn't deal with unmanaged generic type constraints + Return False + End Function + Friend Overrides Function GetSZArrayTypeSymbol(moduleSymbol As PEModuleSymbol, elementType As TypeSymbol, customModifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol))) As TypeSymbol If TypeOf elementType Is UnsupportedMetadataTypeSymbol Then Return elementType diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb index f353cf64ee8a4..da2988db6e29a 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb @@ -314,6 +314,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public MustOverride ReadOnly Property HasValueTypeConstraint As Boolean Implements ITypeParameterSymbol.HasValueTypeConstraint + Private ReadOnly Property HasUnmanagedTypeConstraint As Boolean Implements ITypeParameterSymbol.HasUnmanagedTypeConstraint + Get + Return False + End Get + End Property + Public MustOverride ReadOnly Property Variance As VarianceKind Implements ITypeParameterSymbol.Variance ''' diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb index 83cb053bdea34..c973e61567213 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb @@ -5719,6 +5719,109 @@ BC40008: 'Class2' is obsolete. ) End Sub + + Public Sub EnumConstraint_FromCSharp() + Dim reference = CreateCSharpCompilation(" +public class Test where T : System.Enum +{ +}", parseOptions:=New CSharp.CSharpParseOptions(CSharp.LanguageVersion.CSharp7_3)).EmitToImageReference() + + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib45AndVBRuntime( + + +Public Enum E1 + A +End Enum +Public Class Test2 + Public Sub M() + Dim a = new Test(Of E1)() ' enum + Dim b = new Test(Of Integer)() ' value type + Dim c = new Test(Of string)() ' reference type + Dim d = new Test(Of System.Enum)() ' Enum type + End Sub +End Class + +, {reference}) + + AssertTheseDiagnostics(compilation, + +BC32044: Type argument 'Integer' does not inherit from or implement the constraint type '[Enum]'. + Dim b = new Test(Of Integer)() ' value type + ~~~~~~~ +BC32044: Type argument 'String' does not inherit from or implement the constraint type '[Enum]'. + Dim c = new Test(Of string)() ' reference type + ~~~~~~ +) + End Sub + + + Public Sub DelegateConstraint_FromCSharp() + Dim reference = CreateCSharpCompilation(" +public class Test where T : System.Delegate +{ +}", parseOptions:=New CSharp.CSharpParseOptions(CSharp.LanguageVersion.CSharp7_3)).EmitToImageReference() + + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib45AndVBRuntime( + + +Delegate Sub D1() + +Public Class Test2 + Public Sub M() + Dim a = new Test(Of D1)() ' delegate + Dim b = new Test(Of Integer)() ' value type + Dim c = new Test(Of string)() ' reference type + Dim d = new Test(Of System.Delegate)() ' Delegate type + End Sub +End Class + +, {reference}) + + AssertTheseDiagnostics(compilation, + +BC32044: Type argument 'Integer' does not inherit from or implement the constraint type '[Delegate]'. + Dim b = new Test(Of Integer)() ' value type + ~~~~~~~ +BC32044: Type argument 'String' does not inherit from or implement the constraint type '[Delegate]'. + Dim c = new Test(Of string)() ' reference type + ~~~~~~ +) + End Sub + + + Public Sub MulticastDelegateConstraint_FromCSharp() + Dim reference = CreateCSharpCompilation(" +public class Test where T : System.MulticastDelegate +{ +}", parseOptions:=New CSharp.CSharpParseOptions(CSharp.LanguageVersion.CSharp7_3)).EmitToImageReference() + + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib45AndVBRuntime( + + +Delegate Sub D1() + +Public Class Test2 + Public Sub M() + Dim a = new Test(Of D1)() ' delegate + Dim b = new Test(Of Integer)() ' value type + Dim c = new Test(Of string)() ' reference type + Dim d = new Test(Of System.MulticastDelegate)() ' MulticastDelegate type + End Sub +End Class + +, {reference}) + + AssertTheseDiagnostics(compilation, + +BC32044: Type argument 'Integer' does not inherit from or implement the constraint type 'MulticastDelegate'. + Dim b = new Test(Of Integer)() ' value type + ~~~~~~~ +BC32044: Type argument 'String' does not inherit from or implement the constraint type 'MulticastDelegate'. + Dim c = new Test(Of string)() ' reference type + ~~~~~~ +) + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb index 0c7314bddcddd..3dad316294b92 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb @@ -9,7 +9,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Roslyn.Test.Utilities - +Imports Microsoft.CodeAnalysis.CSharp Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.Metadata.PE @@ -103,6 +103,110 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.Metadata.PE Assert.Equal("System.Runtime.CompilerServices.IsConst", m7Mod.Modifier.ToTestDisplayString()) End Sub + + Public Sub UnmanagedConstraint_RejectedSymbol_OnClass() + Dim reference = CreateCSharpCompilation(" +public class TestRef where T : unmanaged +{ +}", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = + + +Class Test + Shared Sub Main() + Dim x = New TestRef(Of String)() + End Sub +End Class + + + + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) + + AssertTheseDiagnostics(compilation, +BC30649: '' is an unsupported type. + Dim x = New TestRef(Of String)() + ~~~~~~ +BC32044: Type argument 'String' does not inherit from or implement the constraint type '?'. + Dim x = New TestRef(Of String)() + ~~~~~~ +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. + Dim x = New TestRef(Of String)() + ~~~~~~ + ) + + Dim badTypeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(badTypeParameter.HasValueTypeConstraint) + End Sub + + + Public Sub UnmanagedConstraint_RejectedSymbol_OnMethod() + Dim reference = CreateCSharpCompilation(" +public class TestRef +{ + public void M() where T : unmanaged + { + } +}", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = + + +Class Test + Shared Sub Main() + Dim x = New TestRef() + x.M(Of String)() + End Sub +End Class + + + + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) + + AssertTheseDiagnostics(compilation, +BC30649: '' is an unsupported type. + x.M(Of String)() + ~~~~~~~~~~~~~~~~ + ) + + Dim badTypeParameter = compilation.GetTypeByMetadataName("TestRef").GetMethod("M").TypeParameters.Single() + Assert.True(badTypeParameter.HasValueTypeConstraint) + End Sub + + + Public Sub UnmanagedConstraint_RejectedSymbol_OnDelegate() + Dim reference = CreateCSharpCompilation(" +public delegate T D() where T : unmanaged; +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = + + +Class Test + Shared Sub Main(del As D(Of String)) + End Sub +End Class + + + + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) + + AssertTheseDiagnostics(compilation, +BC30649: '' is an unsupported type. + Shared Sub Main(del As D(Of String)) + ~~~ +BC32044: Type argument 'String' does not inherit from or implement the constraint type '?'. + Shared Sub Main(del As D(Of String)) + ~~~ +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. + Shared Sub Main(del As D(Of String)) + ~~~ + ) + + Dim badTypeParameter = compilation.GetTypeByMetadataName("D`1").TypeParameters.Single() + Assert.True(badTypeParameter.HasValueTypeConstraint) + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 324b61bb1365e..b96b2e9b5f117 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -511,7 +511,8 @@ End Namespace Continue For Case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation, WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute, - WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute + WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, + WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute ' Not always available. Continue For End Select @@ -550,7 +551,8 @@ End Namespace Continue For Case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation, WellKnownType.System_Runtime_CompilerServices_IsReadOnlyAttribute, - WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute + WellKnownType.System_Runtime_CompilerServices_IsByRefLikeAttribute, + WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute ' Not always available. Continue For End Select @@ -597,7 +599,8 @@ End Namespace Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningMultipleFiles, WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor, - WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor + WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor, + WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor ' Not always available. Continue For End Select @@ -686,7 +689,8 @@ End Namespace Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningMultipleFiles, WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor, - WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor + WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor, + WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor ' Not always available. Continue For End Select diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs index 7e6dd3ce52556..f74788f626fc5 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs @@ -2388,5 +2388,121 @@ static void Main(string[] args) }", Keyword("var"), Class("List")); } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_InsideMethod() + { + // Asserts no Keyword("unmanaged") because it is an identifier. + await TestInMethodAsync(@" +var unmanaged = 0; +unmanaged++;", + Keyword("var"), + Local("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_Keyword() + { + await TestAsync( + "class X where T : unmanaged { }", + TypeParameter("T"), + Keyword("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +class X where T : unmanaged { }", + TypeParameter("T"), + Interface("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +class X where T : unmanaged { }", + TypeParameter("T"), + Keyword("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_Keyword() + { + await TestAsync(@" +class X +{ + void M() where T : unmanaged { } +}", + TypeParameter("T"), + Keyword("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +class X +{ + void M() where T : unmanaged { } +}", + TypeParameter("T"), + Interface("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +class X +{ + void M() where T : unmanaged { } +}", + TypeParameter("T"), + Keyword("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_Keyword() + { + await TestAsync( + "delegate void D() where T : unmanaged;", + TypeParameter("T"), + Keyword("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +delegate void D() where T : unmanaged;", + TypeParameter("T"), + Interface("unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +delegate void D() where T : unmanaged;", + TypeParameter("T"), + Keyword("unmanaged")); + } } } diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index 3be1b53219ec2..c1bc72f061d45 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -4004,5 +4004,267 @@ await TestAsync( Comment(">>>>>>> End"), Punctuation.CloseCurly); } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_InsideMethod() + { + await TestInMethodAsync(@" +var unmanaged = 0; +unmanaged++;", + Keyword("var"), + Local("unmanaged"), + Operators.Equals, + Number("0"), + Punctuation.Semicolon, + Identifier("unmanaged"), + Operators.PlusPlus, + Punctuation.Semicolon); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_Keyword() + { + await TestAsync( + "class X where T : unmanaged { }", + Keyword("class"), + Class("X"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +class X where T : unmanaged { }", + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +class X where T : unmanaged { }", + Keyword("namespace"), + Identifier("OtherScope"), + Punctuation.OpenCurly, + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_Keyword() + { + await TestAsync(@" +class X +{ + void M() where T : unmanaged { } +}", + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("void"), + Method("M"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +class X +{ + void M() where T : unmanaged { } +}", + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("void"), + Method("M"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +class X +{ + void M() where T : unmanaged { } +}", + Keyword("namespace"), + Identifier("OtherScope"), + Punctuation.OpenCurly, + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("void"), + Method("M"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_Keyword() + { + await TestAsync( + "delegate void D() where T : unmanaged;", + Keyword("delegate"), + Keyword("void"), + Delegate("D"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.Semicolon); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +delegate void D() where T : unmanaged;", + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Keyword("delegate"), + Keyword("void"), + Delegate("D"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.Semicolon); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +delegate void D() where T : unmanaged;", + Keyword("namespace"), + Identifier("OtherScope"), + Punctuation.OpenCurly, + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly, + Keyword("delegate"), + Keyword("void"), + Delegate("D"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + Identifier("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.Semicolon); + } } } diff --git a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs index 886d3dbc9d82e..02b7e937d2f70 100644 --- a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs @@ -930,5 +930,267 @@ await TestInMethodAsync( Constant("Number"), Punctuation.Semicolon); } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_InsideMethod() + { + await TestInMethodAsync(@" +var unmanaged = 0; +unmanaged++;", + Keyword("var"), + Local("unmanaged"), + Operators.Equals, + Number("0"), + Punctuation.Semicolon, + Local("unmanaged"), + Operators.PlusPlus, + Punctuation.Semicolon); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_Keyword() + { + await TestAsync( + "class X where T : unmanaged { }", + Keyword("class"), + Class("X"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +class X where T : unmanaged { }", + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Type_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +class X where T : unmanaged { }", + Keyword("namespace"), + Identifier("OtherScope"), + Punctuation.OpenCurly, + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_Keyword() + { + await TestAsync(@" +class X +{ + void M() where T : unmanaged { } +}", + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("void"), + Method("M"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +class X +{ + void M() where T : unmanaged { } +}", + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("void"), + Method("M"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Method_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +class X +{ + void M() where T : unmanaged { } +}", + Keyword("namespace"), + Identifier("OtherScope"), + Punctuation.OpenCurly, + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly, + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("void"), + Method("M"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_Keyword() + { + await TestAsync( + "delegate void D() where T : unmanaged;", + Keyword("delegate"), + Keyword("void"), + Delegate("D"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.Semicolon); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_ExistingInterface() + { + await TestAsync(@" +interface unmanaged {} +delegate void D() where T : unmanaged;", + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Keyword("delegate"), + Keyword("void"), + Delegate("D"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Interface("unmanaged"), + Punctuation.Semicolon); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Classification)] + public async Task TestUnmanagedConstraint_Delegate_ExistingInterfaceButOutOfScope() + { + await TestAsync(@" +namespace OtherScope +{ + interface unmanaged {} +} +delegate void D() where T : unmanaged;", + Keyword("namespace"), + Identifier("OtherScope"), + Punctuation.OpenCurly, + Keyword("interface"), + Interface("unmanaged"), + Punctuation.OpenCurly, + Punctuation.CloseCurly, + Punctuation.CloseCurly, + Keyword("delegate"), + Keyword("void"), + Delegate("D"), + Punctuation.OpenAngle, + TypeParameter("T"), + Punctuation.CloseAngle, + Punctuation.OpenParen, + Punctuation.CloseParen, + Keyword("where"), + TypeParameter("T"), + Punctuation.Colon, + Keyword("unmanaged"), + Punctuation.Semicolon); + } } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 44b49ffc03372..73b9e2b0bc000 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -9251,5 +9251,32 @@ public async Task DoNotCrashInExtensionMethoWithExpressionBodiedMember() "; await VerifyItemExistsAsync(markup, "o"); } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task EnumConstraint() + { + var markup = +@"public class X where T : System.$$ +"; + await VerifyItemExistsAsync(markup, "Enum"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task DelegateConstraint() + { + var markup = +@"public class X where T : System.$$ +"; + await VerifyItemExistsAsync(markup, "Delegate"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task MulticastDelegateConstraint() + { + var markup = +@"public class X where T : System.$$ +"; + await VerifyItemExistsAsync(markup, "MulticastDelegate"); + } } } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs index 05a067cde54f3..2adf3e8dfff08 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs @@ -4095,42 +4095,47 @@ internal static void Test(global::Outer.Inner inner) new TestParameters(Options.Regular)); } + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("new()")] + [InlineData("unmanaged")] [WorkItem(542529, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542529")] - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] - public async Task TestTypeParameterConstraints1() + [Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + public async Task TestTypeParameterConstraints(string constraint) { await TestInRegularAndScriptAsync( -@"using System; +$@"using System; -class A where T : class -{ -} +class A where T : {constraint} +{{ +}} class Program -{ - static void Goo(A x) where T : class - { +{{ + static void Goo(A x) where T : {constraint} + {{ [|Bar|](x); - } -}", -@"using System; + }} +}}", +$@"using System; -class A where T : class -{ -} +class A where T : {constraint} +{{ +}} class Program -{ - static void Goo(A x) where T : class - { +{{ + static void Goo(A x) where T : {constraint} + {{ Bar(x); - } + }} - private static void Bar(A x) where T : class - { + private static void Bar(A x) where T : {constraint} + {{ throw new NotImplementedException(); - } -}"); + }} +}}"); } [WorkItem(542622, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542622")] @@ -4165,42 +4170,47 @@ private static void Bar(Func> p) }"); } + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("new()")] + [InlineData("unmanaged")] [WorkItem(542626, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542626")] - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] - public async Task TestMethodConstraints1() + [Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + public async Task TestMethodConstraints(string constraint) { await TestInRegularAndScriptAsync( -@"using System; +$@"using System; -class A where T : class -{ -} +class A where T : {constraint} +{{ +}} class Program -{ - static void Goo(A x) where T : class - { +{{ + static void Goo(A x) where T : {constraint} + {{ [|Bar|](x); - } -}", -@"using System; + }} +}}", +$@"using System; -class A where T : class -{ -} +class A where T : {constraint} +{{ +}} class Program -{ - static void Goo(A x) where T : class - { +{{ + static void Goo(A x) where T : {constraint} + {{ Bar(x); - } + }} - private static void Bar(A x) where T : class - { + private static void Bar(A x) where T : {constraint} + {{ throw new NotImplementedException(); - } -}"); + }} +}}"); } [WorkItem(542627, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542627")] diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs index 1145816fded1e..384cef1abd9e1 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs @@ -564,6 +564,18 @@ void M() var y = [|var|] } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsSpellcheck)] + public async Task TestUnmanagedConstraint() + { + await TestInRegularAndScriptAsync( +@"class C where T : [|umanaged|] +{ +}", +@"class C where T : unmanaged +{ }"); } } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs index 81385aad7d088..54c81348bbf26 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs @@ -257,7 +257,46 @@ class Program LanguageVersion.Latest, new CSharpParseOptions(LanguageVersion.CSharp7_2)); } -#endregion C# 7.3 + + [Fact] + public async Task UpgradeProjectFromCSharp7_2ToLatest_EnumConstraint() + { + await TestLanguageVersionUpgradedAsync( +@"public class X where T : [|System.Enum|] +{ +} +", + LanguageVersion.CSharp7_3, + new CSharpParseOptions(LanguageVersion.CSharp7_2), + index: 1); + } + + [Fact] + public async Task UpgradeProjectFromCSharp7_2ToLatest_DelegateConstraint() + { + await TestLanguageVersionUpgradedAsync( +@"public class X where T : [|System.Delegate|] +{ +} +", + LanguageVersion.CSharp7_3, + new CSharpParseOptions(LanguageVersion.CSharp7_2), + index: 1); + } + + [Fact] + public async Task UpgradeProjectFromCSharp7_2ToLatest_MulticastDelegateConstraint() + { + await TestLanguageVersionUpgradedAsync( +@"public class X where T : [|System.MulticastDelegate|] +{ +} +", + LanguageVersion.CSharp7_3, + new CSharpParseOptions(LanguageVersion.CSharp7_2), + index: 1); + } + #endregion C# 7.3 [Fact] public async Task UpgradeAllProjectsToDefault() @@ -433,5 +472,92 @@ void A() string.Format(CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0, "7.0") }); } + + [Fact] + public async Task UpgradeProjectWithUnmanagedConstraintTo7_3_Type() + { + await TestLanguageVersionUpgradedAsync( +@" +class Test where T : [|unmanaged|] +{ +}", + LanguageVersion.CSharp7_3, + new CSharpParseOptions(LanguageVersion.CSharp7), + index: 1); + } + + [Fact] + public async Task UpgradeProjectWithUnmanagedConstraintTo7_3_Type_AlreadyDefined() + { + await TestExactActionSetOfferedAsync( +@" + + +interface unmanaged { } +class Test<T> where T : [|unmanaged|] +{ +} + + +", + expectedActionSet: Enumerable.Empty()); + } + + [Fact] + public async Task UpgradeProjectWithUnmanagedConstraintTo7_3_Method() + { + await TestLanguageVersionUpgradedAsync( +@" +class Test +{ + public void M() where T : [|unmanaged|] { } +}", + LanguageVersion.CSharp7_3, + new CSharpParseOptions(LanguageVersion.CSharp7), + index: 1); + } + + [Fact] + public async Task UpgradeProjectWithUnmanagedConstraintTo7_3_Method_AlreadyDefined() + { + await TestExactActionSetOfferedAsync( +@" + + +interface unmanaged { } +class Test +{ + public void M<T>() where T : [|unmanaged|] { } +} + + +", + expectedActionSet: Enumerable.Empty()); + } + + [Fact] + public async Task UpgradeProjectWithUnmanagedConstraintTo7_3_Delegate() + { + await TestLanguageVersionUpgradedAsync( +@"delegate void D() where T : [|unmanaged|];", + LanguageVersion.CSharp7_3, + new CSharpParseOptions(LanguageVersion.CSharp7), + index: 1); + } + + [Fact] + public async Task UpgradeProjectWithUnmanagedConstraintTo7_3_Delegate_AlreadyDefined() + { + await TestExactActionSetOfferedAsync( +@" + + +interface unmanaged { } +delegate void D<T>() where T : [| unmanaged |]; + + +", + expectedActionSet: Enumerable.Empty()); + } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index e4f42c16fce4d..da6f98cb04703 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -8678,7 +8678,7 @@ public void TypeTypeParameterAttributeUpdate() #region Type Parameter Constraints [Fact] - public void TypeConstraintInsert() + public void TypeConstraintInsert_Class() { var src1 = "class C { }"; var src2 = "class C where T : class { }"; @@ -8693,7 +8693,22 @@ public void TypeConstraintInsert() } [Fact] - public void TypeConstraintInsert2() + public void TypeConstraintInsert_Unmanaged() + { + var src1 = "class C { }"; + var src2 = "class C where T : unmanaged { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [where T : unmanaged]@11"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "where T : unmanaged", FeaturesResources.type_constraint)); + } + + [Fact] + public void TypeConstraintInsert_DoubleStatement_New() { var src1 = "class C where T : class { }"; var src2 = "class C where S : new() where T : class { }"; @@ -8708,7 +8723,22 @@ public void TypeConstraintInsert2() } [Fact] - public void TypeConstraintDelete1() + public void TypeConstraintInsert_DoubleStatement_Unmanaged() + { + var src1 = "class C where T : class { }"; + var src2 = "class C where S : unmanaged where T : class { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [where S : unmanaged]@13"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "where S : unmanaged", FeaturesResources.type_constraint)); + } + + [Fact] + public void TypeConstraintDelete_Class() { var src1 = "class C where T : class { }"; var src2 = "class C { }"; @@ -8723,7 +8753,22 @@ public void TypeConstraintDelete1() } [Fact] - public void TypeConstraintDelete2() + public void TypeConstraintDelete_Unmanaged() + { + var src1 = "class C where T : unmanaged { }"; + var src2 = "class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Delete [where T : unmanaged]@13"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_constraint)); + } + + [Fact] + public void TypeConstraintDelete_DoubleStatement_New() { var src1 = "class C where S : new() where T : class { }"; var src2 = "class C where T : class { }"; @@ -8738,7 +8783,22 @@ public void TypeConstraintDelete2() } [Fact] - public void TypeConstraintReorder() + public void TypeConstraintDelete_DoubleStatement_Unmanaged() + { + var src1 = "class C where S : unmanaged where T : class { }"; + var src2 = "class C where T : class { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Delete [where S : unmanaged]@13"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_constraint)); + } + + [Fact] + public void TypeConstraintReorder_Class() { var src1 = "class C where S : struct where T : class { }"; var src2 = "class C where T : class where S : struct { }"; @@ -8751,6 +8811,20 @@ public void TypeConstraintReorder() edits.VerifyRudeDiagnostics(); } + [Fact] + public void TypeConstraintReorder_Unmanaged() + { + var src1 = "class C where S : struct where T : unmanaged { }"; + var src2 = "class C where T : unmanaged where S : struct { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Reorder [where T : unmanaged]@30 -> @13"); + + edits.VerifyRudeDiagnostics(); + } + [Fact] public void TypeConstraintUpdateAndReorder() { diff --git a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs index 524caf9ffdbea..6ac66ada76711 100644 --- a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs @@ -1177,6 +1177,38 @@ await TestExtractInterfaceCommandCSharpAsync(markup, expectedSuccess: true, expe @"interface ITestClass { ref readonly int this[int p1] { get; } +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ExtractInterface)] + public async Task TestUnmanagedConstraint_Type() + { + var markup = @" +class $$TestClass where T : unmanaged +{ + public void M(T arg) => throw null; +}"; + + await TestExtractInterfaceCommandCSharpAsync(markup, expectedSuccess: true, expectedInterfaceCode: +@"interface ITestClass where T : unmanaged +{ + void M(T arg); +}"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ExtractInterface)] + public async Task TestUnmanagedConstraint_Method() + { + var markup = @" +class $$TestClass +{ + public void M() where T : unmanaged => throw null; +}"; + + await TestExtractInterfaceCommandCSharpAsync(markup, expectedSuccess: true, expectedInterfaceCode: +@"interface ITestClass +{ + void M() where T : unmanaged; }"); } } diff --git a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs b/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs index dfda238a0aac1..1e5a1c54397fc 100644 --- a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs @@ -1681,6 +1681,30 @@ public class [|Test|] : TestParent public class Test : TestParent { public override ref readonly int this[int p] => throw new System.NotImplementedException(); +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestUnmanagedConstraint() + { + await TestInRegularAndScriptAsync( +@"public abstract class ParentTest +{ + public abstract void M() where T : unmanaged; +} +public class [|Test|] : ParentTest +{ +}", +@"public abstract class ParentTest +{ + public abstract void M() where T : unmanaged; +} +public class Test : ParentTest +{ + public override void M() + { + throw new System.NotImplementedException(); + } }"); } } diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs index c8323436ed4c4..af5ef87815c62 100644 --- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs @@ -6941,6 +6941,30 @@ public class Test : [|ITest|] public class Test : ITest { public ref readonly int this[int p] => throw new System.NotImplementedException(); +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestUnmanagedConstraint() + { + await TestInRegularAndScriptAsync( +@"public interface ITest +{ + void M() where T : unmanaged; +} +public class Test : [|ITest|] +{ +}", +@"public interface ITest +{ + void M() where T : unmanaged; +} +public class Test : ITest +{ + public void M() where T : unmanaged + { + throw new System.NotImplementedException(); + } }"); } } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index b9cb93c753bf6..f7e5985d7c137 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -5329,5 +5329,72 @@ void method1() ", MainDescription($"({ FeaturesResources.parameter }) ? b")); } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task EnumConstraint() + { + await TestInMethodAsync( +@" +class X where T : System.Enum +{ + private $$T x; +}", + MainDescription($"T {FeaturesResources.in_} X where T : Enum")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task DelegateConstraint() + { + await TestInMethodAsync( +@" +class X where T : System.Delegate +{ + private $$T x; +}", + MainDescription($"T {FeaturesResources.in_} X where T : Delegate")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task MulticastDelegateConstraint() + { + await TestInMethodAsync( +@" +class X where T : System.MulticastDelegate +{ + private $$T x; +}", + MainDescription($"T {FeaturesResources.in_} X where T : MulticastDelegate")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task UnmanagedConstraint_Type() + { + await TestAsync( +@" +class $$X where T : unmanaged +{ +}", + MainDescription("class X where T : unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task UnmanagedConstraint_Method() + { + await TestAsync( +@" +class X +{ + void $$M() where T : unmanaged { } +}", + MainDescription("void X.M() where T : unmanaged")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task UnmanagedConstraint_Delegate() + { + await TestAsync( + "delegate void $$D() where T : unmanaged;", + MainDescription("delegate void D() where T : unmanaged")); + } } } diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs index a2e74367d7f45..0b57e64ae24c5 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs @@ -624,6 +624,33 @@ void Bar() await TestAsync(markup, expectedOrderedItems); } + [Fact, Trait(Traits.Feature, Traits.Features.SignatureHelp)] + public async Task TestUnmanagedConstraint() + { + var markup = @" + +class C +{ + /// + /// summary headline + /// + /// T documentation + void M(T arg) where T : unmanaged + { + } + + void Bar() + { + [|M<$$|]> + } +}"; + + await TestAsync(markup, new List + { + new SignatureHelpTestItem("void C.M(T arg) where T : unmanaged", "summary headline", "T documentation", currentParameterIndex: 0) + }); + } + #endregion #region "Trigger tests" diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UnmanagedKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UnmanagedKeywordRecommenderTests.cs new file mode 100644 index 0000000000000..641e101e2f567 --- /dev/null +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UnmanagedKeywordRecommenderTests.cs @@ -0,0 +1,168 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations +{ + public class UnmanagedKeywordRecommenderTests : RecommenderTests + { + private readonly UnmanagedKeywordRecommender _recommender = new UnmanagedKeywordRecommender(); + + public UnmanagedKeywordRecommenderTests() + { + this.keywordText = "unmanaged"; + this.RecommendKeywordsAsync = (position, context) => _recommender.RecommendKeywordsAsync(position, context, CancellationToken.None); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAtRoot_Interactive() + { + await VerifyAbsenceAsync(SourceCodeKind.Script, +@"$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotInUsingAlias() + { + await VerifyAbsenceAsync( +@"using Goo = $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterName_Type() + { + await VerifyAbsenceAsync( +@"class Test $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterWhereClause_Type() + { + await VerifyAbsenceAsync( +@"class Test where $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterWhereClauseType_Type() + { + await VerifyAbsenceAsync( +@"class Test where T $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterWhereClauseColon_Type() + { + await VerifyKeywordAsync( +@"class Test where T : $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterTypeConstraint_Type() + { + await VerifyAbsenceAsync( +@"class Test where T : I $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterTypeConstraintComma_Type() + { + await VerifyKeywordAsync( +@"class Test where T : I, $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterName_Method() + { + await VerifyAbsenceAsync( +@"class Test { + void M $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterWhereClause_Method() + { + await VerifyAbsenceAsync( +@"class Test { + void M where $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterWhereClauseType_Method() + { + await VerifyAbsenceAsync( +@"class Test { + void M where T $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterWhereClauseColon_Method() + { + await VerifyKeywordAsync( +@"class Test { + void M where T : $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterTypeConstraint_Method() + { + await VerifyAbsenceAsync( +@"class Test { + void M where T : I $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterTypeConstraintComma_Method() + { + await VerifyKeywordAsync( +@"class Test { + void M where T : I, $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterName_Delegate() + { + await VerifyAbsenceAsync( +@"delegate void D $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterWhereClause_Delegate() + { + await VerifyAbsenceAsync( +@"delegate void D() where $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterWhereClauseType_Delegate() + { + await VerifyAbsenceAsync( +@"delegate void D() where T $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterWhereClauseColon_Delegate() + { + await VerifyKeywordAsync( +@"delegate void D() where T : $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterTypeConstraint_Delegate() + { + await VerifyAbsenceAsync( +@"delegate void D() where T : I $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterTypeConstraintComma_Delegate() + { + await VerifyKeywordAsync( +@"delegate void D() where T : I, $$"); + } + } +} diff --git a/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs b/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs index bf59b1d16c946..100e1c3cf8bf6 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs @@ -216,14 +216,11 @@ private static TestWorkspace CreateWorkspace( bool includeXmlDocComments, string sourceWithSymbolReference, string languageVersion) { + string languageVersionAttribute = languageVersion is null ? "" : $@" LanguageVersion=""{languageVersion}"""; + var xmlString = string.Concat(@" - + {1} ", metadataLanguage, SecurityElement.Escape(source), - includeXmlDocComments.ToString())); + includeXmlDocComments.ToString(), + languageVersionAttribute)); } if (sourceWithSymbolReference != null) diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index b64a7d37881f6..223270e0944e6 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -1399,5 +1399,115 @@ Function Method1() As Uri End Interface"; await GenerateAndVerifySourceAsync(source, symbolName, LanguageNames.VisualBasic, expectedVB, includeXmlDocComments: true); } + + [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + public async Task TestUnmanagedCSharpConstraint_Type() + { + var metadata = @"using System; +public class TestType where T : unmanaged +{ +}"; + var sourceWithSymbolReference = @" +class C +{ + void M() + { + var obj = new [|TestType|]<int>(); + } +}"; + var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +public class [|TestType|] where T : unmanaged +{{ + public TestType(); +}}"; + + using (var context = TestContext.Create( + LanguageNames.CSharp, + SpecializedCollections.SingletonEnumerable(metadata), + includeXmlDocComments: false, + languageVersion: "CSharp7_3", + sourceWithSymbolReference: sourceWithSymbolReference)) + { + var navigationSymbol = await context.GetNavigationSymbolAsync(); + var metadataAsSourceFile = await context.GenerateSourceAsync(navigationSymbol); + context.VerifyResult(metadataAsSourceFile, expected); + } + } + + [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + public async Task TestUnmanagedCSharpConstraint_Method() + { + var metadata = @"using System; +public class TestType +{ + public void M() where T : unmanaged + { + } +}"; + var sourceWithSymbolReference = @" +class C +{ + void M() + { + var obj = new TestType().[|M|]<int>(); + } +}"; + var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +public class TestType +{{ + public TestType(); + + public void [|M|]() where T : unmanaged; +}}"; + + using (var context = TestContext.Create( + LanguageNames.CSharp, + SpecializedCollections.SingletonEnumerable(metadata), + includeXmlDocComments: false, + languageVersion: "CSharp7_3", + sourceWithSymbolReference: sourceWithSymbolReference)) + { + var navigationSymbol = await context.GetNavigationSymbolAsync(); + var metadataAsSourceFile = await context.GenerateSourceAsync(navigationSymbol); + context.VerifyResult(metadataAsSourceFile, expected); + } + } + + [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + public async Task TestUnmanagedCSharpConstraint_Delegate() + { + var metadata = @"using System; +public delegate void D() where T : unmanaged;"; + var sourceWithSymbolReference = @" +class C +{ + void M([|D|]<int> lambda) + { + } +}"; + var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +public delegate void [|D|]() where T : unmanaged;"; + + using (var context = TestContext.Create( + LanguageNames.CSharp, + SpecializedCollections.SingletonEnumerable(metadata), + includeXmlDocComments: false, + languageVersion: "CSharp7_3", + sourceWithSymbolReference: sourceWithSymbolReference)) + { + var navigationSymbol = await context.GetNavigationSymbolAsync(); + var metadataAsSourceFile = await context.GenerateSourceAsync(navigationSymbol); + context.VerifyResult(metadataAsSourceFile, expected); + } + } } } diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs index 12194c9701bd2..948efeaa1e4b3 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs @@ -1503,5 +1503,48 @@ void M() } } + [WpfFact] + [Trait(Traits.Feature, Traits.Features.RenameTracking)] + public async Task RenameTracking_UnmanagedConstraint_Keyword() + { + var code = @" +class C<T> where T : $$unmanaged +{ +}"; + using (var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp)) + { + await state.AssertNoTag(); + } + } + + [WpfFact] + [Trait(Traits.Feature, Traits.Features.RenameTracking)] + public async Task RenameTracking_UnmanagedConstraint_Type() + { + var code = @" +interface unmanaged +{ +} +class C<T> where T : $$unmanaged +{ +}"; + using (var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp)) + { + state.EditorOperations.InsertText("my"); + + await state.AssertTag("unmanaged", "myunmanaged", invokeAction: true); + + // Make sure the rename completed + var expectedCode = @" +interface myunmanaged +{ +} +class C where T : myunmanaged +{ +}"; + Assert.Equal(expectedCode, state.HostDocument.TextBuffer.CurrentSnapshot.GetText()); + await state.AssertNoTag(); + } + } } } diff --git a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb index d0c0670850b3e..165b462d21b74 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTests.vb @@ -813,6 +813,41 @@ class C Test(workspace) End Sub + + Public Sub TestCSharpGoToUnmanaged_Keyword() + Dim workspace = + + + + class C<T> where T : un$$managed + { + } + + + + + Test(workspace, expectedResult:=False) + End Sub + + + Public Sub TestCSharpGoToUnmanaged_Type() + Dim workspace = + + + + interface [|unmanaged|] + { + } + class C<T> where T : un$$managed + { + } + + + + + Test(workspace) + End Sub + #End Region #Region "CSharp TupleTests" diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs index 88e6516e9ee7d..2961e4ffb3aa3 100644 --- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs +++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs @@ -58,6 +58,8 @@ public override string ToString() { case "=": return "Operators.Equals"; + case "++": + return "Operators.PlusPlus"; } goto default; diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs index c7a514e1eff37..50fc84128c438 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EETypeParameterSymbol.cs @@ -60,6 +60,11 @@ public override bool HasValueTypeConstraint get { return _sourceTypeParameter.HasValueTypeConstraint; } } + public override bool HasUnmanagedTypeConstraint + { + get { return _sourceTypeParameter.HasUnmanagedTypeConstraint; } + } + public override ImmutableArray Locations { get { throw ExceptionUtilities.Unreachable; } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs index fad03dd5e587a..2f32b6ba9e0bb 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SimpleTypeParameterSymbol.cs @@ -53,6 +53,11 @@ public override bool HasValueTypeConstraint get { return false; } } + public override bool HasUnmanagedTypeConstraint + { + get { return false; } + } + public override VarianceKind Variance { get { return VarianceKind.None; } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs index 908bec90f65af..ce748080329f8 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs @@ -140,6 +140,7 @@ private static ImmutableArray> GetKeywo new ULongKeywordRecommender(), new UncheckedKeywordRecommender(), new UndefKeywordRecommender(), + new UnmanagedKeywordRecommender(), new UnsafeKeywordRecommender(), new UShortKeywordRecommender(), new UsingKeywordRecommender(), diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs new file mode 100644 index 0000000000000..40ca34676169c --- /dev/null +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Completion.Providers; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders +{ + internal class UnmanagedKeywordRecommender : IKeywordRecommender + { + public Task> RecommendKeywordsAsync(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) + { + if (context.SyntaxTree.IsTypeParameterConstraintContext(position, context.LeftToken, cancellationToken)) + { + return Task.FromResult(SpecializedCollections.SingletonEnumerable(new RecommendedKeyword("unmanaged"))); + } + + return Task.FromResult>(null); + } + } +} diff --git a/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs index 918c99e3c1b4c..52fa63c540f22 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs @@ -247,6 +247,11 @@ private IList GetSelectedDisplayParts( parts.Add(Keyword(SyntaxKind.ClassKeyword)); needComma = true; } + else if (typeParam.HasUnmanagedTypeConstraint) + { + parts.Add(new SymbolDisplayPart(SymbolDisplayPartKind.Keyword, null, "unmanaged")); + needComma = true; + } else if (typeParam.HasValueTypeConstraint) { parts.Add(Keyword(SyntaxKind.StructKeyword)); diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs index 9b9f5b8cf71a8..17bb143675c00 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs @@ -289,7 +289,8 @@ protected ImmutableArray CreateMethodTypeParameters(Cancel typeParameters.Add(CodeGenerationSymbolFactory.CreateTypeParameter( parameter.GetAttributes(), parameter.Variance, parameter.Name, ImmutableArray.Create(), - parameter.HasConstructorConstraint, parameter.HasReferenceTypeConstraint, parameter.HasValueTypeConstraint, parameter.Ordinal)); + parameter.HasConstructorConstraint, parameter.HasReferenceTypeConstraint, parameter.HasValueTypeConstraint, + parameter.HasUnmanagedTypeConstraint, parameter.Ordinal)); } return typeParameters.ToImmutableAndFree(); diff --git a/src/Features/Core/Portable/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs b/src/Features/Core/Portable/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs index 78e3d706893be..6f837df37e864 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs @@ -72,7 +72,8 @@ private ITypeParameterSymbol MassageTypeParameter( constraintTypes: constraints.AsImmutable(), hasConstructorConstraint: typeParameter.HasConstructorConstraint, hasReferenceConstraint: typeParameter.HasReferenceTypeConstraint, - hasValueConstraint: typeParameter.HasValueTypeConstraint); + hasValueConstraint: typeParameter.HasValueTypeConstraint, + hasUnmanagedConstraint: typeParameter.HasUnmanagedTypeConstraint); } private List MergeClassTypes(List classTypes, CancellationToken cancellationToken) diff --git a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs index 63be4b318a1dc..9542e14e09b25 100644 --- a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs +++ b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs @@ -14,6 +14,8 @@ internal static class ClassificationHelpers private const string FromKeyword = "from"; private const string ValueKeyword = "value"; private const string VarKeyword = "var"; + private const string UnmanagedKeyword = "unmanaged"; + private const string DynamicKeyword = "dynamic"; private const string AwaitKeyword = "await"; /// @@ -327,6 +329,11 @@ token.Parent is IdentifierNameSyntax && token.Parent.Parent is VariableDeclarationSyntax && !(token.Parent.Parent.Parent is FieldDeclarationSyntax) && !(token.Parent.Parent.Parent is EventFieldDeclarationSyntax); + + case UnmanagedKeyword: + return token.Parent is IdentifierNameSyntax + && token.Parent.Parent is TypeConstraintSyntax + && token.Parent.Parent.Parent is TypeParameterConstraintClauseSyntax; } } @@ -368,15 +375,15 @@ internal static ClassifiedSpan AdjustStaleClassification(SourceText rawText, Cla var token = SyntaxFactory.ParseToken(text); if (token.Span.Length == span.Length) { - // var and dynamic are not contextual keywords. They are always identifiers + // var, dynamic, and unmanaged are not contextual keywords. They are always identifiers // (that we classify as keywords). Because we are just parsing a token we don't // know if we're in the right context for them to be identifiers or keywords. // So, we base on decision on what they were before. i.e. if we had a keyword - // before, then assume it stays a keyword if we see 'var' or 'dynamic. + // before, then assume it stays a keyword if we see 'var', 'dynamic', or 'unmanaged'. + var tokenString = token.ToString(); var isKeyword = SyntaxFacts.IsKeywordKind(token.Kind()) || (wasKeyword && SyntaxFacts.GetContextualKeywordKind(text) != SyntaxKind.None) - || (wasKeyword && token.ToString() == "var") - || (wasKeyword && token.ToString() == "dynamic"); + || (wasKeyword && (tokenString == VarKeyword || tokenString == DynamicKeyword || tokenString == UnmanagedKeyword)); var isIdentifier = token.Kind() == SyntaxKind.IdentifierToken; diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 3ef068712df75..a39d86a882d7b 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -135,7 +135,7 @@ private bool TryClassifySymbol( var alias = semanticModel.GetAliasInfo(name, cancellationToken); if (alias == null || alias.Name != "var") { - if (!IsSymbolCalledVar(symbol)) + if (!IsSymbolWithName(symbol, "var")) { // We bound to a symbol. If we bound to a symbol called "var" then we want to // classify this appropriately as a type. Otherwise, we want to classify this as @@ -146,6 +146,22 @@ private bool TryClassifySymbol( } } + if (name.IsUnmanaged && name.Parent.IsKind(SyntaxKind.TypeConstraint)) + { + var alias = semanticModel.GetAliasInfo(name, cancellationToken); + if (alias == null || alias.Name != "unmanaged") + { + if (!IsSymbolWithName(symbol, "unmanaged")) + { + // We bound to a symbol. If we bound to a symbol called "unmanaged" then we want to + // classify this appropriately as a type. Otherwise, we want to classify this as + // a keyword. + classifiedSpan = new ClassifiedSpan(name.Span, ClassificationTypeNames.Keyword); + return true; + } + } + } + // Use .Equals since we can't rely on object identity for constructed types. SyntaxToken token; switch (symbol) @@ -339,14 +355,19 @@ private bool TryClassifyNameOfIdentifier( return false; } - private bool IsSymbolCalledVar(ISymbol symbol) + private bool IsSymbolWithName(ISymbol symbol, string name) { + if (symbol is null || symbol.Name != name) + { + return false; + } + if (symbol is INamedTypeSymbol namedType) { - return namedType.Arity == 0 && symbol.Name == "var"; + return namedType.Arity == 0; } - return symbol != null && symbol.Name == "var"; + return true; } } } diff --git a/src/Workspaces/CSharp/Portable/Extensions/ITypeParameterSymbolExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ITypeParameterSymbolExtensions.cs index c02d81f87cc21..82f6396a7c0ea 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ITypeParameterSymbolExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ITypeParameterSymbolExtensions.cs @@ -42,6 +42,10 @@ private static void AddConstraintClauses( { constraints.Add(SyntaxFactory.ClassOrStructConstraint(SyntaxKind.ClassConstraint)); } + else if (typeParameter.HasUnmanagedTypeConstraint) + { + constraints.Add(SyntaxFactory.TypeConstraint(SyntaxFactory.IdentifierName("unmanaged"))); + } else if (typeParameter.HasValueTypeConstraint) { constraints.Add(SyntaxFactory.ClassOrStructConstraint(SyntaxKind.StructConstraint)); diff --git a/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationSymbolFactory.cs b/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationSymbolFactory.cs index c913cbfee4c68..39a130d0780c7 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationSymbolFactory.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/CodeGenerationSymbolFactory.cs @@ -252,7 +252,7 @@ public static ITypeParameterSymbol CreateTypeParameterSymbol(string name, int or attributes: default, varianceKind: VarianceKind.None, name: name, constraintTypes: ImmutableArray.Create(), hasConstructorConstraint: false, hasReferenceConstraint: false, hasValueConstraint: false, - ordinal: ordinal); + hasUnmanagedConstraint: false, ordinal: ordinal); } /// @@ -264,9 +264,10 @@ public static ITypeParameterSymbol CreateTypeParameter( ImmutableArray constraintTypes, bool hasConstructorConstraint = false, bool hasReferenceConstraint = false, + bool hasUnmanagedConstraint = false, bool hasValueConstraint = false, int ordinal = 0) { - return new CodeGenerationTypeParameterSymbol(null, attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, ordinal); + return new CodeGenerationTypeParameterSymbol(null, attributes, varianceKind, name, constraintTypes, hasConstructorConstraint, hasReferenceConstraint, hasValueConstraint, hasUnmanagedConstraint, ordinal); } /// diff --git a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationTypeParameterSymbol.cs b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationTypeParameterSymbol.cs index 93c4857e883a1..9ede82e472325 100644 --- a/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationTypeParameterSymbol.cs +++ b/src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationTypeParameterSymbol.cs @@ -13,6 +13,7 @@ internal class CodeGenerationTypeParameterSymbol : CodeGenerationTypeSymbol, ITy public bool HasConstructorConstraint { get; } public bool HasReferenceTypeConstraint { get; } public bool HasValueTypeConstraint { get; } + public bool HasUnmanagedTypeConstraint { get; } public int Ordinal { get; } public CodeGenerationTypeParameterSymbol( @@ -24,6 +25,7 @@ public CodeGenerationTypeParameterSymbol( bool hasConstructorConstraint, bool hasReferenceConstraint, bool hasValueConstraint, + bool hasUnmanagedConstraint, int ordinal) : base(containingType, attributes, Accessibility.NotApplicable, default, name, SpecialType.None) { @@ -33,6 +35,7 @@ public CodeGenerationTypeParameterSymbol( this.HasConstructorConstraint = hasConstructorConstraint; this.HasReferenceTypeConstraint = hasReferenceConstraint; this.HasValueTypeConstraint = hasValueConstraint; + this.HasUnmanagedTypeConstraint = hasUnmanagedConstraint; } protected override CodeGenerationSymbol Clone() @@ -40,7 +43,7 @@ protected override CodeGenerationSymbol Clone() return new CodeGenerationTypeParameterSymbol( this.ContainingType, this.GetAttributes(), this.Variance, this.Name, this.ConstraintTypes, this.HasConstructorConstraint, this.HasReferenceTypeConstraint, - this.HasValueTypeConstraint, this.Ordinal); + this.HasValueTypeConstraint, this.HasUnmanagedTypeConstraint, this.Ordinal); } public new ITypeParameterSymbol OriginalDefinition => this; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs index 92b96cc7fa351..b83c77f2a4266 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs @@ -131,6 +131,7 @@ private static ImmutableArray RenameTypeParameters( typeParameter.HasConstructorConstraint, typeParameter.HasReferenceTypeConstraint, typeParameter.HasValueTypeConstraint, + typeParameter.HasUnmanagedTypeConstraint, typeParameter.Ordinal); newTypeParameters.Add(newTypeParameter); diff --git a/src/Workspaces/CoreTest/FindReferencesTests.cs b/src/Workspaces/CoreTest/FindReferencesTests.cs index 74d5f62d8efe1..02f69357369b4 100644 --- a/src/Workspaces/CoreTest/FindReferencesTests.cs +++ b/src/Workspaces/CoreTest/FindReferencesTests.cs @@ -320,6 +320,26 @@ public override void SomeMethod() { } Assert.Equal(refsFromVirtualSorted, refsFromOverrideSorted); } + [Fact] + public async Task FindRefereceToUnmanagedConstraint_Type() + { + var text = @" +interface unmanaged // Line 1 +{ +} +abstract class C where T : unmanaged // Line 4 +{ +}"; + var solution = GetSingleDocumentSolution(text); + var project = solution.Projects.First(); + var comp = await project.GetCompilationAsync(); + + var constraint = comp.GetTypeByMetadataName("C`1").TypeParameters.Single().ConstraintTypes.Single(); + var result = (await SymbolFinder.FindReferencesAsync(constraint, solution)).Single(); + + Verify(result, new HashSet { 1, 4 }); + } + private static void Verify(ReferencedSymbol reference, HashSet expectedMatchedLines) { void verifier(Location location)