Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge "generic attributes" to main #55577

Merged
merged 16 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4c3ffcb
Merge pull request #51424 from jcouv/merge-master
jcouv Feb 24, 2021
04bbcd1
Merge branch 'main' of github.com:dotnet/roslyn into features/generic…
RikkiGibson Jul 7, 2021
5380f17
Enable generic attributes (#26337)
AviAvni Jul 12, 2021
62c477a
Merge branch 'features/generic-attributes' of github.com:dotnet/rosly…
RikkiGibson Jul 12, 2021
bca6952
Fix error code
RikkiGibson Jul 13, 2021
9aa300e
Merge pull request #54784 from dotnet/merges/main-to-features/generic…
RikkiGibson Jul 13, 2021
aad722b
Merge branch 'main' of github.com:dotnet/roslyn into merges/main-to-f…
RikkiGibson Jul 19, 2021
2c4059c
Merge pull request #54953 from dotnet/merges/main-to-features/generic…
RikkiGibson Jul 19, 2021
f260ccb
Give 'typeof'-like errors in attribute type arguments (#54956)
RikkiGibson Jul 21, 2021
5831d86
Adjust AllowMultiple behavior with generic attributes (#54963)
RikkiGibson Jul 22, 2021
c1b967c
Merge branch 'main' of github.com:dotnet/roslyn into merges/main-to-f…
RikkiGibson Jul 23, 2021
4dfa549
Merge pull request #55100 from dotnet/merges/main-to-features/generic…
RikkiGibson Jul 24, 2021
2bf65c5
More generic attributes tests (#55202)
RikkiGibson Aug 11, 2021
b62b1c8
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
RikkiGibson Aug 11, 2021
e88ab2b
Merge pull request #55548 from dotnet/merges/main-to-features/generic…
RikkiGibson Aug 11, 2021
73f45c3
Move "generic attributes" to preview LangVersion (#55556)
RikkiGibson Aug 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/contributing/Compiler Test Plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This document provides guidance for thinking about language interactions and tes
- properties (including get/set/init accessors)
- events (including add/remove accessors)
- Parameter modifiers (ref, out, in, params)
- Attributes (including security attribute)
- Attributes (including generic attributes and security attributes)
- Generics (type arguments, variance, constraints including `class`, `struct`, `new()`, `unmanaged`, `notnull`, types and interfaces with nullability)
- Default and constant values
- Partial classes
Expand Down
26 changes: 26 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1338,10 +1338,36 @@ private BoundExpression BindTypeOf(TypeOfExpressionSyntax node, BindingDiagnosti
hasError = true;
}

if (!hasError)
{
CheckDisallowedAttributeDependentType(typeWithAnnotations, isError: false, node.Location, diagnostics);
}

BoundTypeExpression boundType = new BoundTypeExpression(typeSyntax, alias, typeWithAnnotations, type.IsErrorType());
return new BoundTypeOfOperator(node, boundType, null, this.GetWellKnownType(WellKnownType.System_Type, diagnostics, node), hasError);
}

/// <summary>Called when an "attribute-dependent" type such as 'dynamic', 'string?', etc. is not permitted.</summary>
private void CheckDisallowedAttributeDependentType(TypeWithAnnotations typeArgument, bool isError, Location errorLocation, BindingDiagnosticBag diagnostics)
{
var diagnosticId = isError ? ErrorCode.ERR_AttrDependentTypeNotAllowed : ErrorCode.WRN_AttrDependentTypeNotAllowed;
typeArgument.VisitType(type: null, static (typeWithAnnotations, arg, _) =>
{
var (topLevelType, diagnosticId, errorLocation, diagnostics) = arg;
var type = typeWithAnnotations.Type;
if (type.IsDynamic()
|| (typeWithAnnotations.NullableAnnotation.IsAnnotated() && !type.IsValueType)
|| type.IsNativeIntegerType
|| (type.IsTupleType && !type.TupleElementNames.IsDefault))
{
diagnostics.Add(diagnosticId, errorLocation, topLevelType.ToDisplayString(SymbolDisplayFormat.CSharpShortErrorMessageFormat));
return true;
}

return false;
}, typePredicate: null, arg: (typeArgument, diagnosticId, errorLocation, diagnostics));
}

private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, BindingDiagnosticBag diagnostics)
{
ExpressionSyntax typeSyntax = node.Type;
Expand Down
16 changes: 2 additions & 14 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,6 @@ private void LookupAttributeType(
var attributeTypeWithoutSuffixViabilityUseSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(useSiteInfo);
bool resultWithoutSuffixIsViable = IsSingleViableAttributeType(result, out symbolWithoutSuffix, ref attributeTypeWithoutSuffixViabilityUseSiteInfo);

// Generic types are not allowed.
Debug.Assert(arity == 0 || !result.IsMultiViable);

// Result with 'Attribute' suffix added.
LookupResult resultWithSuffix = null;
Symbol symbolWithSuffix = null;
Expand All @@ -535,9 +532,6 @@ private void LookupAttributeType(
resultWithSuffix = LookupResult.GetInstance();
this.LookupSymbolsOrMembersInternal(resultWithSuffix, qualifierOpt, name + "Attribute", arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
resultWithSuffixIsViable = IsSingleViableAttributeType(resultWithSuffix, out symbolWithSuffix, ref attributeTypeWithSuffixViabilityUseSiteInfo);

// Generic types are not allowed.
Debug.Assert(arity == 0 || !result.IsMultiViable);
}

if (resultWithoutSuffixIsViable && resultWithSuffixIsViable)
Expand Down Expand Up @@ -704,13 +698,7 @@ private bool CheckAttributeTypeViability(Symbol symbol, bool diagnose, ref Diagn
if (symbol.Kind == SymbolKind.NamedType)
{
var namedType = (NamedTypeSymbol)symbol;
if (namedType.IsGenericType)
{
// Attribute classes cannot be generic.
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_AttributeCantBeGeneric, symbol) : null;
return false;
}
else if (namedType.IsAbstract)
if (namedType.IsAbstract)
{
// Attribute class cannot be abstract.
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_AbstractAttributeClass, symbol) : null;
Expand Down Expand Up @@ -1693,7 +1681,7 @@ private static bool WrongArity(Symbol symbol, int arity, bool diagnose, LookupOp
NamedTypeSymbol namedType = (NamedTypeSymbol)symbol;
// non-declared types only appear as using aliases (aliases are arity 0)
Debug.Assert(object.ReferenceEquals(namedType.ConstructedFrom, namedType));
if (namedType.Arity != arity || options.IsAttributeTypeLookup() && arity != 0)
if (namedType.Arity != arity)
{
if (namedType.Arity == 0)
{
Expand Down
30 changes: 19 additions & 11 deletions src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,28 +1173,36 @@ private TypeWithAnnotations BindGenericSimpleNamespaceOrTypeOrAliasSymbol(
}
else
{
var boundTypeArguments = BindTypeArguments(typeArguments, diagnostics, basesBeingResolved);
if (unconstructedType.IsGenericType
&& options.IsAttributeTypeLookup())
{
foreach (var typeArgument in boundTypeArguments)
{
var type = typeArgument.Type;
if (type.IsUnboundGenericType() || type.ContainsTypeParameter())
{
diagnostics.Add(ErrorCode.ERR_AttrTypeArgCannotBeTypeVar, node.Location, type.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat));
}
else
{
CheckDisallowedAttributeDependentType(typeArgument, isError: true, node.Location, diagnostics);
}
}
}

// It's not an unbound type expression, so we must have type arguments, and we have a
// generic type of the correct arity in hand (possibly an error type). Bind the type
// arguments and construct the final result.
resultType = ConstructNamedType(
unconstructedType,
node,
typeArguments,
BindTypeArguments(typeArguments, diagnostics, basesBeingResolved),
boundTypeArguments,
basesBeingResolved,
diagnostics);
}

if (options.IsAttributeTypeLookup())
{
// Generic type cannot be an attribute type.
// Parser error has already been reported, just wrap the result type with error type symbol.
Debug.Assert(unconstructedType.IsErrorType());
Debug.Assert(resultType.IsErrorType());
resultType = new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(resultType), resultType,
LookupResultKind.NotAnAttributeType, errorInfo: null);
}

return TypeWithAnnotations.Create(AreNullableAnnotationsEnabled(node.TypeArgumentList.GreaterThanToken), resultType);
}

Expand Down
25 changes: 17 additions & 8 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1269,9 +1269,6 @@
<data name="ERR_TypeVarCantBeNull" xml:space="preserve">
<value>Cannot convert null to type parameter '{0}' because it could be a non-nullable value type. Consider using 'default({0})' instead.</value>
</data>
<data name="ERR_AttributeCantBeGeneric" xml:space="preserve">
<value>Cannot apply attribute class '{0}' because it is generic</value>
</data>
<data name="ERR_DuplicateBound" xml:space="preserve">
<value>Duplicate constraint '{0}' for type parameter '{1}'</value>
</data>
Expand Down Expand Up @@ -1308,6 +1305,18 @@
<data name="ERR_AttrArgWithTypeVars" xml:space="preserve">
<value>'{0}': an attribute argument cannot use type parameters</value>
</data>
<data name="ERR_AttrTypeArgCannotBeTypeVar" xml:space="preserve">
<value>'{0}': an attribute type argument cannot use type parameters</value>
</data>
<data name="WRN_AttrDependentTypeNotAllowed" xml:space="preserve">
<value>Type '{0}' cannot be used in this context because it cannot be represented in metadata.</value>
</data>
<data name="WRN_AttrDependentTypeNotAllowed_Title" xml:space="preserve">
<value>Type cannot be used in this context because it cannot be represented in metadata.</value>
</data>
<data name="ERR_AttrDependentTypeNotAllowed" xml:space="preserve">
<value>Type '{0}' cannot be used in this context because it cannot be represented in metadata.</value>
</data>
<data name="ERR_NewTyvarWithArgs" xml:space="preserve">
<value>'{0}': cannot provide arguments when creating an instance of a variable type</value>
</data>
Expand Down Expand Up @@ -2030,9 +2039,6 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_UnifyingInterfaceInstantiations" xml:space="preserve">
<value>'{0}' cannot implement both '{1}' and '{2}' because they may unify for some type parameter substitutions</value>
</data>
<data name="ERR_GenericDerivingFromAttribute" xml:space="preserve">
<value>A generic type cannot derive from '{0}' because it is an attribute class</value>
</data>
<data name="ERR_TyVarNotFoundInConstraint" xml:space="preserve">
<value>'{1}' does not define type parameter '{0}'</value>
</data>
Expand Down Expand Up @@ -2834,7 +2840,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep
<value>The managed coclass wrapper class '{0}' for interface '{1}' cannot be found (are you missing an assembly reference?)</value>
</data>
<data name="ERR_AmbiguousAttribute" xml:space="preserve">
<value>'{0}' is ambiguous between '{1}' and '{2}'; use either '@{0}' or '{0}Attribute'</value>
<value>'{0}' is ambiguous between '{1}' and '{2}'. Either use '@{0}' or explicitly include the 'Attribute' suffix.</value>
</data>
<data name="ERR_BadArgExtraRef" xml:space="preserve">
<value>Argument {0} may not be passed with the '{1}' keyword</value>
Expand Down Expand Up @@ -6842,4 +6848,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_NewlinesAreNotAllowedInsideANonVerbatimInterpolatedString" xml:space="preserve">
<value>Newlines are not allowed inside a non-verbatim interpolated string</value>
</data>
</root>
<data name="IDS_FeatureGenericAttributes" xml:space="preserve">
<value>generic attributes</value>
</data>
</root>
7 changes: 5 additions & 2 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ internal enum ErrorCode
ERR_NewBoundMustBeLast = 401,
WRN_MainCantBeGeneric = 402,
ERR_TypeVarCantBeNull = 403,
ERR_AttributeCantBeGeneric = 404,
// ERR_AttributeCantBeGeneric = 404,
ERR_DuplicateBound = 405,
ERR_ClassBoundNotFirst = 406,
ERR_BadRetType = 407,
Expand Down Expand Up @@ -484,7 +484,7 @@ internal enum ErrorCode
WRN_TypeParameterSameAsOuterTypeParameter = 693,
ERR_TypeVariableSameAsParent = 694,
ERR_UnifyingInterfaceInstantiations = 695,
ERR_GenericDerivingFromAttribute = 698,
// ERR_GenericDerivingFromAttribute = 698,
ERR_TyVarNotFoundInConstraint = 699,
ERR_BadBoundType = 701,
ERR_SpecialTypeAsBound = 702,
Expand Down Expand Up @@ -1991,6 +1991,9 @@ internal enum ErrorCode
WRN_CallerArgumentExpressionAttributeSelfReferential = 8965,
WRN_CallerArgumentExpressionParamForUnconsumedLocation = 8966,
ERR_NewlinesAreNotAllowedInsideANonVerbatimInterpolatedString = 8967,
ERR_AttrTypeArgCannotBeTypeVar = 8968,
WRN_AttrDependentTypeNotAllowed = 8969,
ERR_AttrDependentTypeNotAllowed = 8970,

#endregion

Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ internal static int GetWarningLevel(ErrorCode code)
switch (code)
{
case ErrorCode.WRN_PartialMethodTypeDifference:
case ErrorCode.WRN_AttrDependentTypeNotAllowed:
// Warning level 6 is exclusively for warnings introduced in the compiler
// shipped with dotnet 6 (C# 10) and that can be reported for pre-existing code.
return 6;
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ internal enum MessageID
IDS_FeatureFileScopedNamespace = MessageBase + 12809,
IDS_FeatureParameterlessStructConstructors = MessageBase + 12810,
IDS_FeatureStructFieldInitializers = MessageBase + 12811,
IDS_FeatureGenericAttributes = MessageBase + 12812,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -343,6 +344,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
{
// C# preview features.
case MessageID.IDS_FeatureStaticAbstractMembersInInterfaces: // semantic check
case MessageID.IDS_FeatureGenericAttributes: // semantic check
return LanguageVersion.Preview;

// C# 10.0 features.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ internal bool IsSecurityAttribute(CSharpCompilation compilation)
{
if (this.AttributeClass is object)
{
string className = this.AttributeClass.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat);
string className = this.AttributeClass.ToDisplayString(SymbolDisplayFormat.TestFormat);

if (!this.CommonConstructorArguments.Any() & !this.CommonNamedArguments.Any())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ protected override void CheckBase(BindingDiagnosticBag diagnostics)
// you need to know all bases before you can ask this question... (asking this causes a cycle)
if (this.IsGenericType && !baseContainsErrorTypes && this.DeclaringCompilation.IsAttributeType(localBase))
{
// A generic type cannot derive from '{0}' because it is an attribute class
diagnostics.Add(ErrorCode.ERR_GenericDerivingFromAttribute, baseLocation, localBase);
MessageID.IDS_FeatureGenericAttributes.CheckFeatureAvailability(diagnostics, this.DeclaringCompilation, baseLocation);
}

// Check constraints on the first declaration with explicit bases.
Expand Down
10 changes: 9 additions & 1 deletion src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,14 @@ internal bool LoadAndValidateAttributes(
var attributeTypesBuilder = new NamedTypeSymbol[totalAttributesCount];

Binder.BindAttributeTypes(binders, attributesToBind, this, attributeTypesBuilder, diagnostics);
for (var i = 0; i < totalAttributesCount; i++)
{
if (attributeTypesBuilder[i].IsGenericType)
{
MessageID.IDS_FeatureGenericAttributes.CheckFeatureAvailability(diagnostics, attributesToBind[i]);
}
}

ImmutableArray<NamedTypeSymbol> boundAttributeTypes = attributeTypesBuilder.AsImmutableOrNull();

this.EarlyDecodeWellKnownAttributeTypes(boundAttributeTypes, attributesToBind);
Expand Down Expand Up @@ -674,7 +682,7 @@ private bool ValidateAttributeUsage(
AttributeUsageInfo attributeUsageInfo = attributeType.GetAttributeUsageInfo();

// Given attribute can't be specified more than once if AllowMultiple is false.
if (!uniqueAttributeTypes.Add(attributeType) && !attributeUsageInfo.AllowMultiple)
if (!uniqueAttributeTypes.Add(attributeType.OriginalDefinition) && !attributeUsageInfo.AllowMultiple)
{
diagnostics.Add(ErrorCode.ERR_DuplicateAttribute, node.Name.Location, node.GetErrorDisplayName());
return false;
Expand Down
Loading