未验证 提交 651d3f01 编写于 作者: A AlekseyTs 提交者: GitHub

Treat unconstrained type parameters declared in `#nullable disable` context as...

Treat unconstrained type parameters declared in `#nullable disable` context as having an oblivious nullability in case they are substituted with a reference type. (#34797)

As apposed to possibly nullable or not nullable reference type.
Fixes #29980.
Fixes #34844.
上级 b3049770
......@@ -52,6 +52,8 @@ Trivial/optimized cases:
NullableAttribute(1) should be placed on a type parameter definition that has a `class!` constraint.
NullableAttribute(2) should be placed on a type parameter definition that has a `class?` constraint.
NullableAttribute(2) should be placed on a type parameter definition that has no type constraints, `class`, `struct` and `unmanaged` constraints and
is declared in a context where nullable type annotations are allowed, that is eqivalent to having an `object?` constraint.
Other forms of NullableAttribute are not emitted on type parameter definitions and are not specially recognized on them.
The `NullableAttribute` type declaration is synthesized by the compiler if it is not included in the compilation, but is needed to produce the output.
......@@ -267,7 +269,10 @@ A `class?` constraint is allowed, which, like class, requires the type argument
An explicit `object` (or `System.Object`) constraint is allowed, which requires the type to be non-nullable when it is a reference type.
However, an explicit `object?` constraint is not allowed.
An unconstrained type parameter is essentially equivalent to one constrained by `object?`.
An unconstrained (here it means - no type constraints, and no `class`, `struct`, or `unmanaged` constraints) type parameter is essentially
equivalent to one constrained by `object?` when it is declared in a context where nullable annotations are enabled. If annotations are disabled,
the type parameter is essentially equivalent to one constrained by `object~`. The context is determined at the identifier that declares the type
parameter within a type parameter list.
[4/25/18](https://github.com/dotnet/csharplang/blob/master/meetings/2018/LDM-2018-04-25.md)
Note, the `object`/`System.Object` constraint is represented in metadata as any other type constraint, the type is System.Object.
......
......@@ -21,6 +21,7 @@ internal partial class Binder
internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstraintClauses(
Symbol containingSymbol,
ImmutableArray<TypeParameterSymbol> typeParameters,
TypeParameterListSyntax typeParameterList,
SyntaxList<TypeParameterConstraintClauseSyntax> clauses,
DiagnosticBag diagnostics)
{
......@@ -58,7 +59,7 @@ internal partial class Binder
Debug.Assert(ordinal >= 0);
Debug.Assert(ordinal < n);
var constraintClause = this.BindTypeParameterConstraints(name, clause, diagnostics);
var constraintClause = this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, diagnostics);
if (results[ordinal] == null)
{
results[ordinal] = constraintClause;
......@@ -81,12 +82,12 @@ internal partial class Binder
}
}
// Add empty values for type parameters without constraint clauses.
// Add default values for type parameters without constraint clauses.
for (int i = 0; i < n; i++)
{
if (results[i] == null)
{
results[i] = TypeParameterConstraintClause.Empty;
results[i] = GetDefaultTypeParameterConstraintClause(typeParameterList.Parameters[i]);
}
}
......@@ -96,14 +97,14 @@ internal partial class Binder
/// <summary>
/// Bind and return a single type parameter constraint clause.
/// </summary>
private TypeParameterConstraintClause BindTypeParameterConstraints(
string name, TypeParameterConstraintClauseSyntax constraintClauseSyntax, DiagnosticBag diagnostics)
private TypeParameterConstraintClause BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, DiagnosticBag diagnostics)
{
var constraints = TypeParameterConstraintKind.None;
var constraintTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance();
var syntaxBuilder = ArrayBuilder<TypeConstraintSyntax>.GetInstance();
SeparatedSyntaxList<TypeParameterConstraintSyntax> constraintsSyntax = constraintClauseSyntax.Constraints;
Debug.Assert(!InExecutableBinder); // Cannot eagerly report diagnostics handled by LazyMissingNonNullTypesContextDiagnosticInfo
bool hasTypeLikeConstraint = false;
for (int i = 0, n = constraintsSyntax.Count; i < n; i++)
{
......@@ -111,6 +112,8 @@ internal partial class Binder
switch (syntax.Kind())
{
case SyntaxKind.ClassConstraint:
hasTypeLikeConstraint = true;
if (i != 0)
{
diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation());
......@@ -135,6 +138,8 @@ internal partial class Binder
continue;
case SyntaxKind.StructConstraint:
hasTypeLikeConstraint = true;
if (i != 0)
{
diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation());
......@@ -161,6 +166,8 @@ internal partial class Binder
continue;
case SyntaxKind.TypeConstraint:
{
hasTypeLikeConstraint = true;
var typeConstraintSyntax = (TypeConstraintSyntax)syntax;
var typeSyntax = typeConstraintSyntax.Type;
var typeSyntaxKind = typeSyntax.Kind();
......@@ -207,9 +214,31 @@ internal partial class Binder
}
}
if (!hasTypeLikeConstraint && !IsNullableEnabled(typeParameterSyntax.Identifier))
{
constraints |= TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType;
}
return TypeParameterConstraintClause.Create(constraints, constraintTypes.ToImmutableAndFree(), syntaxBuilder.ToImmutableAndFree());
}
internal ImmutableArray<TypeParameterConstraintClause> GetDefaultTypeParameterConstraintClauses(TypeParameterListSyntax typeParameterList)
{
var builder = ArrayBuilder<TypeParameterConstraintClause>.GetInstance(typeParameterList.Parameters.Count);
foreach (TypeParameterSyntax typeParameterSyntax in typeParameterList.Parameters)
{
builder.Add(GetDefaultTypeParameterConstraintClause(typeParameterSyntax));
}
return builder.ToImmutableAndFree();
}
private TypeParameterConstraintClause GetDefaultTypeParameterConstraintClause(TypeParameterSyntax typeParameterSyntax)
{
return IsNullableEnabled(typeParameterSyntax.Identifier) ? TypeParameterConstraintClause.Empty : TypeParameterConstraintClause.ObliviousNullabilityIfReferenceType;
}
/// <summary>
/// Returns true if the constraint is valid. Otherwise
/// returns false and generates a diagnostic.
......
......@@ -73,6 +73,8 @@ public override bool HasReferenceTypeConstraint
get { return false; }
}
internal override bool? IsNotNullableIfReferenceType => null;
public override bool HasValueTypeConstraint
{
get { return false; }
......
......@@ -352,21 +352,29 @@ internal static class ConstraintsHelper
this Symbol containingSymbol,
Binder binder,
ImmutableArray<TypeParameterSymbol> typeParameters,
TypeParameterListSyntax typeParameterList,
SyntaxList<TypeParameterConstraintClauseSyntax> constraintClauses,
Location location,
DiagnosticBag diagnostics)
{
if (typeParameters.Length == 0 || constraintClauses.Count == 0)
if (typeParameters.Length == 0)
{
return ImmutableArray<TypeParameterConstraintClause>.Empty;
}
if (constraintClauses.Count == 0)
{
ImmutableArray<TypeParameterConstraintClause> defaultClauses = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);
return defaultClauses.ContainsOnlyEmptyConstraintClauses() ? ImmutableArray<TypeParameterConstraintClause>.Empty : defaultClauses;
}
// Wrap binder from factory in a generic constraints specific binder
// to avoid checking constraints when binding type names.
Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause));
binder = binder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks);
return binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, constraintClauses, diagnostics);
return binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, diagnostics);
}
/// <summary>
......@@ -394,32 +402,37 @@ internal static class ConstraintsHelper
TypeParameterConstraintClause constraintClause,
DiagnosticBag diagnostics)
{
Symbol containingSymbol = typeParameter.ContainingSymbol;
var constraintTypeBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
var constraintTypes = constraintClause.ConstraintTypes;
int n = constraintTypes.Length;
for (int i = 0; i < n; i++)
if (!constraintClause.TypeConstraintsSyntax.IsDefault)
{
var constraintType = constraintTypes[i];
var syntax = constraintClause.TypeConstraintsSyntax[i];
// 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 (Binder.IsValidConstraint(typeParameter.Name, syntax, constraintType, constraintClause.Constraints, constraintTypeBuilder, diagnostics))
Symbol containingSymbol = typeParameter.ContainingSymbol;
var constraintTypeBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
int n = constraintTypes.Length;
for (int i = 0; i < n; i++)
{
containingSymbol.CheckConstraintTypeVisibility(syntax.Location, constraintType, diagnostics);
constraintTypeBuilder.Add(constraintType);
var constraintType = constraintTypes[i];
var syntax = constraintClause.TypeConstraintsSyntax[i];
// 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 (Binder.IsValidConstraint(typeParameter.Name, syntax, constraintType, constraintClause.Constraints, constraintTypeBuilder, diagnostics))
{
containingSymbol.CheckConstraintTypeVisibility(syntax.Location, constraintType, diagnostics);
constraintTypeBuilder.Add(constraintType);
}
}
}
if (constraintTypeBuilder.Count < n)
{
constraintTypes = constraintTypeBuilder.ToImmutable();
}
if (constraintTypeBuilder.Count < n)
{
constraintTypes = constraintTypeBuilder.ToImmutable();
}
constraintTypeBuilder.Free();
constraintTypeBuilder.Free();
}
// Verify constraints on any other partial declarations.
foreach (var otherClause in constraintClause.OtherPartialDeclarations)
......
......@@ -71,6 +71,8 @@ public override bool HasReferenceTypeConstraint
}
}
internal override bool? IsNotNullableIfReferenceType => null;
public override bool HasValueTypeConstraint
{
get
......
......@@ -536,14 +536,15 @@ private static bool HaveSameConstraints(Symbol member1, TypeMap typeMap1, Symbol
return HaveSameConstraints(typeParameters1, typeMap1, typeParameters2, typeMap2);
}
public static bool HaveSameConstraints(ImmutableArray<TypeParameterSymbol> typeParameters1, TypeMap typeMap1, ImmutableArray<TypeParameterSymbol> typeParameters2, TypeMap typeMap2)
public static bool HaveSameConstraints(ImmutableArray<TypeParameterSymbol> typeParameters1, TypeMap typeMap1, ImmutableArray<TypeParameterSymbol> typeParameters2, TypeMap typeMap2, bool includingNullability = false)
{
Debug.Assert(typeParameters1.Length == typeParameters2.Length);
int arity = typeParameters1.Length;
for (int i = 0; i < arity; i++)
{
if (!HaveSameConstraints(typeParameters1[i], typeMap1, typeParameters2[i], typeMap2))
if (!HaveSameConstraints(typeParameters1[i], typeMap1, typeParameters2[i], typeMap2) ||
(includingNullability && !HaveSameNullabilityInConstraints(typeParameters1[i], typeMap1, typeParameters2[i], typeMap2, unknownNullabilityMatchesAny: false)))
{
return false;
}
......@@ -595,20 +596,23 @@ private static bool HaveSameTypeConstraints(TypeParameterSymbol typeParameter1,
AreConstraintTypesSubset(substitutedTypes2, substitutedTypes1, typeParameter1);
}
public static bool HaveSameNullabilityInConstraints(TypeParameterSymbol typeParameter1, TypeMap typeMap1, TypeParameterSymbol typeParameter2, TypeMap typeMap2)
public static bool HaveSameNullabilityInConstraints(TypeParameterSymbol typeParameter1, TypeMap typeMap1, TypeParameterSymbol typeParameter2, TypeMap typeMap2, bool unknownNullabilityMatchesAny = true)
{
if (!typeParameter1.IsValueType)
{
bool? isNotNullableIfReferenceType1 = typeParameter1.IsNotNullableIfReferenceType;
bool? isNotNullableIfReferenceType2 = typeParameter2.IsNotNullableIfReferenceType;
if (isNotNullableIfReferenceType1.HasValue && isNotNullableIfReferenceType2.HasValue &&
isNotNullableIfReferenceType1.GetValueOrDefault() != isNotNullableIfReferenceType2.GetValueOrDefault())
if (isNotNullableIfReferenceType1 != isNotNullableIfReferenceType2 &&
!(unknownNullabilityMatchesAny && (!isNotNullableIfReferenceType1.HasValue || !isNotNullableIfReferenceType2.HasValue)))
{
return false;
}
}
return HaveSameTypeConstraints(typeParameter1, typeMap1, typeParameter2, typeMap2, TypeSymbol.EqualsAllIgnoreOptionsPlusNullableWithUnknownMatchesAnyComparer);
return HaveSameTypeConstraints(typeParameter1, typeMap1, typeParameter2, typeMap2,
unknownNullabilityMatchesAny ?
TypeSymbol.EqualsAllIgnoreOptionsPlusNullableWithUnknownMatchesAnyComparer :
TypeSymbol.EqualsAllIgnoreOptionsPlusNullableComparer);
}
/// <summary>
......
......@@ -157,18 +157,8 @@ private ImmutableArray<TypeWithAnnotations> GetDeclaredConstraintTypes()
}
var moduleSymbol = containingType.ContainingPEModule;
var metadataReader = moduleSymbol.Module.MetadataReader;
GenericParameterConstraintHandleCollection constraints;
try
{
constraints = metadataReader.GetGenericParameter(_handle).GetConstraints();
}
catch (BadImageFormatException)
{
constraints = default(GenericParameterConstraintHandleCollection);
Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo);
}
PEModule peModule = moduleSymbol.Module;
GenericParameterConstraintHandleCollection constraints = GetConstraintHandleCollection(peModule);
bool hasUnmanagedModreqPattern = false;
......@@ -186,6 +176,7 @@ private ImmutableArray<TypeWithAnnotations> GetDeclaredConstraintTypes()
tokenDecoder = new MetadataDecoder(moduleSymbol, containingType);
}
var metadataReader = peModule.MetadataReader;
foreach (var constraintHandle in constraints)
{
var constraint = metadataReader.GetGenericParameterConstraint(constraintHandle);
......@@ -230,7 +221,7 @@ private ImmutableArray<TypeWithAnnotations> GetDeclaredConstraintTypes()
// - 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))
hasUnmanagedModreqPattern != peModule.HasIsUnmanagedAttribute(_handle))
{
// we do not recognize these combinations as "unmanaged"
hasUnmanagedModreqPattern = false;
......@@ -244,6 +235,23 @@ private ImmutableArray<TypeWithAnnotations> GetDeclaredConstraintTypes()
return _lazyDeclaredConstraintTypes;
}
private GenericParameterConstraintHandleCollection GetConstraintHandleCollection(PEModule module)
{
GenericParameterConstraintHandleCollection constraints;
try
{
constraints = module.MetadataReader.GetGenericParameter(_handle).GetConstraints();
}
catch (BadImageFormatException)
{
constraints = default(GenericParameterConstraintHandleCollection);
Interlocked.CompareExchange(ref _lazyConstraintsUseSiteErrorInfo, new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this), CSDiagnosticInfo.EmptyErrorInfo);
}
return constraints;
}
public override ImmutableArray<Location> Locations
{
get
......@@ -301,6 +309,30 @@ public override bool HasReferenceTypeConstraint
}
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
if ((_flags & (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.ReferenceTypeConstraint)) == 0)
{
PEModule module = ((PEModuleSymbol)this.ContainingModule).Module;
GenericParameterConstraintHandleCollection constraints = GetConstraintHandleCollection(module);
if (constraints.Count == 0)
{
if (module.HasNullableAttribute(_handle, out byte transformFlag, out _) && transformFlag == NullableAnnotationExtensions.AnnotatedAttributeValue)
{
return false;
}
return null;
}
}
return CalculateIsNotNullableIfReferenceType();
}
}
public override bool HasValueTypeConstraint
{
get
......
......@@ -82,6 +82,14 @@ internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsLis
return this.RetargetingTranslator.Retarget(_underlyingTypeParameter.GetConstraintTypes(inProgress, early));
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
return _underlyingTypeParameter.IsNotNullableIfReferenceType;
}
}
internal override ImmutableArray<NamedTypeSymbol> GetInterfaces(ConsList<TypeParameterSymbol> inProgress)
{
return this.RetargetingTranslator.Retarget(_underlyingTypeParameter.GetInterfaces(inProgress));
......
......@@ -131,6 +131,8 @@ public override bool HasReferenceTypeConstraint
get { return false; }
}
internal override bool? IsNotNullableIfReferenceType => null;
public override bool HasUnmanagedTypeConstraint
{
get { return false; }
......
......@@ -130,6 +130,8 @@ public override bool HasReferenceTypeConstraint
get { return false; }
}
internal override bool? IsNotNullableIfReferenceType => null;
public override bool HasUnmanagedTypeConstraint
{
get { return false; }
......
......@@ -464,6 +464,7 @@ public override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterCo
var constraints = this.MakeTypeParameterConstraintsEarly(
_binder,
TypeParameters,
_syntax.TypeParameterList,
_syntax.ConstraintClauses,
_syntax.Identifier.GetLocation(),
diagnostics);
......
......@@ -273,24 +273,49 @@ private ImmutableArray<TypeParameterConstraintClause> MakeTypeParameterConstrain
int arity = typeParameters.Length;
if (arity > 0)
{
bool skipPartialDeclarationsWithoutConstraintClauses = false;
foreach (var decl in declaration.Declarations)
{
if (GetConstraintClauses((CSharpSyntaxNode)decl.SyntaxReference.GetSyntax(), out _).Count != 0)
{
skipPartialDeclarationsWithoutConstraintClauses = true;
break;
}
}
foreach (var decl in declaration.Declarations)
{
var syntaxRef = decl.SyntaxReference;
var constraintClauses = GetConstraintClauses((CSharpSyntaxNode)syntaxRef.GetSyntax());
if (constraintClauses.Count == 0)
var constraintClauses = GetConstraintClauses((CSharpSyntaxNode)syntaxRef.GetSyntax(), out TypeParameterListSyntax typeParameterList);
if (skipPartialDeclarationsWithoutConstraintClauses && constraintClauses.Count == 0)
{
continue;
}
var binderFactory = this.DeclaringCompilation.GetBinderFactory(syntaxRef.SyntaxTree);
var binder = binderFactory.GetBinder(constraintClauses[0]);
Binder binder;
ImmutableArray<TypeParameterConstraintClause> constraints;
// Wrap binder from factory in a generic constraints specific binder
// to avoid checking constraints when binding type names.
Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause));
binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks);
if (constraintClauses.Count == 0)
{
binder = binderFactory.GetBinder(typeParameterList.Parameters[0]);
constraints = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);
}
else
{
binder = binderFactory.GetBinder(constraintClauses[0]);
// Wrap binder from factory in a generic constraints specific binder
// to avoid checking constraints when binding type names.
Debug.Assert(!binder.Flags.Includes(BinderFlags.GenericConstraintsClause));
binder = binder.WithContainingMemberOrLambda(this).WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks);
constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, typeParameterList, constraintClauses, diagnostics);
}
var constraints = binder.BindTypeParameterConstraintClauses(this, typeParameters, constraintClauses, diagnostics);
Debug.Assert(constraints.Length == arity);
if (results.Length == 0)
......@@ -304,6 +329,11 @@ private ImmutableArray<TypeParameterConstraintClause> MakeTypeParameterConstrain
results = results.ZipAsArray(constraints, (x, y) => x.AddPartialDeclaration(y));
}
}
if (results.ContainsOnlyEmptyConstraintClauses())
{
results = ImmutableArray<TypeParameterConstraintClause>.Empty;
}
}
return results;
......@@ -334,19 +364,30 @@ private ImmutableArray<TypeParameterConstraintClause> MakeTypeParameterConstrain
}
}
return ConstraintsHelper.MakeTypeParameterConstraintsLate(typeParameters, constraintClauses, diagnostics);
ImmutableArray<TypeParameterConstraintClause> results = ConstraintsHelper.MakeTypeParameterConstraintsLate(typeParameters, constraintClauses, diagnostics);
if (results.ContainsOnlyEmptyConstraintClauses())
{
results = ImmutableArray<TypeParameterConstraintClause>.Empty;
}
return results;
}
private static SyntaxList<TypeParameterConstraintClauseSyntax> GetConstraintClauses(CSharpSyntaxNode node)
private static SyntaxList<TypeParameterConstraintClauseSyntax> GetConstraintClauses(CSharpSyntaxNode node, out TypeParameterListSyntax typeParameterList)
{
switch (node.Kind())
{
case SyntaxKind.ClassDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.InterfaceDeclaration:
return ((TypeDeclarationSyntax)node).ConstraintClauses;
var typeDeclaration = (TypeDeclarationSyntax)node;
typeParameterList = typeDeclaration.TypeParameterList;
return typeDeclaration.ConstraintClauses;
case SyntaxKind.DelegateDeclaration:
return ((DelegateDeclarationSyntax)node).ConstraintClauses;
var delegateDeclaration = (DelegateDeclarationSyntax)node;
typeParameterList = delegateDeclaration.TypeParameterList;
return delegateDeclaration.ConstraintClauses;
default:
throw ExceptionUtilities.UnexpectedValue(node.Kind());
}
......
......@@ -523,6 +523,7 @@ public override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterCo
var constraints = this.MakeTypeParameterConstraintsEarly(
withTypeParametersBinder,
TypeParameters,
syntax.TypeParameterList,
syntax.ConstraintClauses,
syntax.Identifier.GetLocation(),
diagnostics);
......@@ -1180,7 +1181,7 @@ private static bool HaveSameConstraints(SourceOrdinaryMethodSymbol part1, Source
var typeMap1 = new TypeMap(typeParameters1, indexedTypeParameters, allowAlpha: true);
var typeMap2 = new TypeMap(typeParameters2, indexedTypeParameters, allowAlpha: true);
return MemberSignatureComparer.HaveSameConstraints(typeParameters1, typeMap1, typeParameters2, typeMap2);
return MemberSignatureComparer.HaveSameConstraints(typeParameters1, typeMap1, typeParameters2, typeMap2, includingNullability: true);
}
internal override bool CallsAreOmitted(SyntaxTree syntaxTree)
......
......@@ -306,7 +306,9 @@ private bool ModifyCompilationForAttributeEmbedding()
private void CheckNullableAnnotationsInConstraints(DiagnosticBag diagnostics)
{
if ((this.HasReferenceTypeConstraint && this.ReferenceTypeConstraintIsNullable != null) ||
this.ConstraintTypesNoUseSiteDiagnostics.Any(c => c.NeedsNullableAttribute()))
this.ConstraintTypesNoUseSiteDiagnostics.Any(c => c.NeedsNullableAttribute()) ||
(!this.HasReferenceTypeConstraint && !this.HasValueTypeConstraint &&
this.ConstraintTypesNoUseSiteDiagnostics.IsEmpty && this.IsNotNullableIfReferenceType == false))
{
DeclaringCompilation.EnsureNullableAttributeExists(diagnostics, this.GetNonNullSyntaxNode().Location, ModifyCompilationForAttributeEmbedding());
}
......@@ -361,7 +363,26 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsUnmanagedAttribute(this));
}
if (this.HasReferenceTypeConstraint && this.ReferenceTypeConstraintIsNullable != null)
byte nullableAttributeValue = NullableAnnotationExtensions.ObliviousAttributeValue;
if (this.HasReferenceTypeConstraint)
{
switch (this.ReferenceTypeConstraintIsNullable)
{
case true:
nullableAttributeValue = NullableAnnotationExtensions.AnnotatedAttributeValue;
break;
case false:
nullableAttributeValue = NullableAnnotationExtensions.NotAnnotatedAttributeValue;
break;
}
}
else if (!this.HasValueTypeConstraint && this.ConstraintTypesNoUseSiteDiagnostics.IsEmpty && this.IsNotNullableIfReferenceType == false)
{
nullableAttributeValue = NullableAnnotationExtensions.AnnotatedAttributeValue;
}
if (nullableAttributeValue != NullableAnnotationExtensions.ObliviousAttributeValue)
{
NamedTypeSymbol byteType = DeclaringCompilation.GetSpecialType(SpecialType.System_Byte);
Debug.Assert((object)byteType != null);
......@@ -370,9 +391,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r
ref attributes,
moduleBuilder.SynthesizeNullableAttribute(WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte,
ImmutableArray.Create(new TypedConstant(byteType, TypedConstantKind.Primitive,
(this.ReferenceTypeConstraintIsNullable == true ?
NullableAnnotationExtensions.AnnotatedAttributeValue :
NullableAnnotationExtensions.NotAnnotatedAttributeValue)))));
nullableAttributeValue))));
}
}
......@@ -477,6 +496,19 @@ public override bool HasReferenceTypeConstraint
}
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
if ((this.GetDeclaredConstraints() & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0)
{
return null;
}
return CalculateIsNotNullableIfReferenceType();
}
}
public override bool HasUnmanagedTypeConstraint
{
get
......@@ -579,6 +611,19 @@ public override bool HasReferenceTypeConstraint
}
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
if ((this.GetDeclaredConstraints() & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) != 0)
{
return null;
}
return CalculateIsNotNullableIfReferenceType();
}
}
public override bool HasUnmanagedTypeConstraint
{
get
......@@ -801,6 +846,14 @@ public override bool HasReferenceTypeConstraint
}
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
return this.OverriddenTypeParameter?.IsNotNullableIfReferenceType;
}
}
public override bool HasUnmanagedTypeConstraint
{
get
......
......@@ -20,6 +20,16 @@ internal enum TypeParameterConstraintKind
Unmanaged = 0x08,
NullableReferenceType = ReferenceType | 0x10,
NotNullableReferenceType = ReferenceType | 0x20,
/// <summary>
/// Type parameter has no type constraints, including `struct`, `class`, `unmanaged` and is declared in a context
/// where nullable annotations are disabled.
/// Cannot be combined with <see cref="ReferenceType"/>, <see cref="ValueType"/> or <see cref="Unmanaged"/>.
/// Note, presence of this flag suppresses generation of Nullable attribute on the corresponding type parameter.
/// This imitates the shape of metadata produced by pre-nullable compilers. Metadata import is adjusted accordingly
/// to distinguish between the two situations.
/// </summary>
ObliviousNullabilityIfReferenceType = 0x40,
}
/// <summary>
......@@ -34,16 +44,30 @@ internal sealed class TypeParameterConstraintClause
typeConstraintsSyntax: default,
otherPartialDeclarations: ImmutableArray<TypeParameterConstraintClause>.Empty);
internal static readonly TypeParameterConstraintClause ObliviousNullabilityIfReferenceType = new TypeParameterConstraintClause(
TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType,
ImmutableArray<TypeWithAnnotations>.Empty,
typeConstraintsSyntax: default,
otherPartialDeclarations: ImmutableArray<TypeParameterConstraintClause>.Empty);
internal static TypeParameterConstraintClause Create(
TypeParameterConstraintKind constraints,
ImmutableArray<TypeWithAnnotations> constraintTypes,
ImmutableArray<TypeConstraintSyntax> typeConstraintsSyntax = default)
{
Debug.Assert(!constraintTypes.IsDefault);
if (constraints == TypeParameterConstraintKind.None && constraintTypes.IsEmpty)
if (constraintTypes.IsEmpty)
{
Debug.Assert(typeConstraintsSyntax.IsDefault);
return Empty;
switch (constraints)
{
case TypeParameterConstraintKind.None:
Debug.Assert(typeConstraintsSyntax.IsDefault);
return Empty;
case TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType:
Debug.Assert(typeConstraintsSyntax.IsDefault);
return ObliviousNullabilityIfReferenceType;
}
}
return new TypeParameterConstraintClause(constraints, constraintTypes, typeConstraintsSyntax, otherPartialDeclarations: ImmutableArray<TypeParameterConstraintClause>.Empty);
}
......@@ -66,6 +90,9 @@ internal sealed class TypeParameterConstraintClause
ExceptionUtilities.UnexpectedValue(constraints); // This call asserts.
break;
}
Debug.Assert((constraints & TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType) == 0 ||
(constraints & ~(TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType | TypeParameterConstraintKind.Constructor)) == 0);
#endif
this.Constraints = constraints;
this.ConstraintTypes = constraintTypes;
......@@ -87,9 +114,9 @@ internal sealed class TypeParameterConstraintClause
/// </summary>
internal readonly ImmutableArray<TypeParameterConstraintClause> OtherPartialDeclarations;
internal bool IsEmpty => Constraints == TypeParameterConstraintKind.None && ConstraintTypes.IsEmpty;
internal bool IsEmpty => Constraints == TypeParameterConstraintKind.None && ConstraintTypes.IsEmpty && OtherPartialDeclarations.ContainsOnlyEmptyConstraintClauses();
internal bool IsEarly => !TypeConstraintsSyntax.IsDefault;
internal bool IsEarly => !TypeConstraintsSyntax.IsDefault || !OtherPartialDeclarations.IsEmpty;
internal TypeParameterConstraintClause AddPartialDeclaration(TypeParameterConstraintClause other)
{
......@@ -103,5 +130,10 @@ internal static bool IsEarly(this ImmutableArray<TypeParameterConstraintClause>
{
return constraintClauses.Any(clause => clause.IsEarly);
}
internal static bool ContainsOnlyEmptyConstraintClauses(this ImmutableArray<TypeParameterConstraintClause> constraintClauses)
{
return constraintClauses.All(clause => clause.IsEmpty);
}
}
}
......@@ -103,6 +103,19 @@ internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsLis
return constraintTypes.ToImmutableAndFree().WhereAsArray(type => type.SpecialType != SpecialType.System_Object || !type.NullableAnnotation.IsAnnotated());
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
if (_underlyingTypeParameter.ConstraintTypesNoUseSiteDiagnostics.IsEmpty)
{
return _underlyingTypeParameter.IsNotNullableIfReferenceType;
}
return CalculateIsNotNullableIfReferenceType();
}
}
internal override ImmutableArray<NamedTypeSymbol> GetInterfaces(ConsList<TypeParameterSymbol> inProgress)
{
return _map.SubstituteNamedTypes(_underlyingTypeParameter.GetInterfaces(inProgress));
......
......@@ -474,7 +474,7 @@ internal bool IsReferenceTypeFromConstraintTypes(ImmutableArray<TypeWithAnnotati
if (constraintType.TypeKind == TypeKind.TypeParameter)
{
bool? isNotNullableIfReferenceType = ((TypeParameterSymbol)constraintType.Type).GetIsNotNullableIfReferenceType();
bool? isNotNullableIfReferenceType = ((TypeParameterSymbol)constraintType.Type).IsNotNullableIfReferenceType;
if (isNotNullableIfReferenceType == false)
{
......@@ -534,7 +534,7 @@ internal bool GetIsReferenceType(ConsList<TypeParameterSymbol> inProgress)
public sealed override bool IsReferenceType => GetIsReferenceType(ConsList<TypeParameterSymbol>.Empty);
internal bool? GetIsNotNullableIfReferenceType()
protected bool? CalculateIsNotNullableIfReferenceType()
{
bool? fromReferenceTypeConstraint = false;
......@@ -568,7 +568,7 @@ internal bool GetIsReferenceType(ConsList<TypeParameterSymbol> inProgress)
}
// https://github.com/dotnet/roslyn/issues/26198 Should this API be exposed through ITypeParameterSymbol?
internal bool? IsNotNullableIfReferenceType => GetIsNotNullableIfReferenceType();
internal abstract bool? IsNotNullableIfReferenceType { get; }
internal bool GetIsValueType(ConsList<TypeParameterSymbol> inProgress)
{
......
......@@ -128,6 +128,9 @@ private InterfaceInfo GetInterfaceInfo()
internal static readonly EqualityComparer<TypeSymbol> EqualsAllIgnoreOptionsPlusNullableWithUnknownMatchesAnyComparer =
new TypeSymbolComparer(TypeCompareKind.AllIgnoreOptions & ~(TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));
internal static readonly EqualityComparer<TypeSymbol> EqualsAllIgnoreOptionsPlusNullableComparer =
new TypeSymbolComparer(TypeCompareKind.AllIgnoreOptions & ~(TypeCompareKind.AllNullableIgnoreOptions));
internal static readonly EqualityComparer<TypeSymbol> EqualsCLRSignatureComparer = new TypeSymbolComparer(TypeCompareKind.CLRSignatureCompareOptions);
/// <summary>
/// The original definition of this symbol. If this symbol is constructed from another
......
......@@ -42,6 +42,7 @@ public static bool CanBeConst(this TypeSymbol typeSymbol)
}
/// <summary>
/// Assuming that nullable annotations are enabled:
/// T => true
/// T where T : struct => false
/// T where T : class => false
......@@ -62,6 +63,7 @@ public static bool IsTypeParameterDisallowingAnnotation(this TypeSymbol type)
}
/// <summary>
/// Assuming that nullable annotations are enabled:
/// T => true
/// T where T : struct => false
/// T where T : class => false
......
......@@ -1198,6 +1198,9 @@ class B : A<object?>
}";
var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule));
comp.VerifyEmitDiagnostics(
// (1,9): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// class A<T>
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 9),
// (4,7): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// class B : A<object?>
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "B").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(4, 7));
......@@ -1215,6 +1218,9 @@ class C : I<(object X, object? Y)>
}";
var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule));
comp.VerifyEmitDiagnostics(
// (1,13): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// interface I<T>
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 13),
// (4,7): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// class C : I<(object X, object? Y)>
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "C").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(4, 7));
......@@ -1411,9 +1417,15 @@ static void G()
}";
var comp = CreateCompilation(new[] { source }, parseOptions: TestOptions.Regular8, options: WithNonNullTypesTrue(TestOptions.ReleaseModule));
comp.VerifyEmitDiagnostics(
// (1,17): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// delegate void D<T>(T t);
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 17),
// (1,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// delegate void D<T>(T t);
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T t").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(1, 20),
// (4,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// static void F<T>(D<T> d)
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "T").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(4, 19),
// (4,22): error CS0518: Predefined type 'System.Runtime.CompilerServices.NullableAttribute' is not defined or imported
// static void F<T>(D<T> d)
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "D<T> d").WithArguments("System.Runtime.CompilerServices.NullableAttribute").WithLocation(4, 22),
......
......@@ -60,6 +60,19 @@ public override bool HasReferenceTypeConstraint
get { return _sourceTypeParameter.ReferenceTypeConstraintIsNullable; }
}
internal override bool? IsNotNullableIfReferenceType
{
get
{
if (_sourceTypeParameter.ConstraintTypesNoUseSiteDiagnostics.IsEmpty)
{
return _sourceTypeParameter.IsNotNullableIfReferenceType;
}
return CalculateIsNotNullableIfReferenceType();
}
}
public override bool HasValueTypeConstraint
{
get { return _sourceTypeParameter.HasValueTypeConstraint; }
......
......@@ -53,6 +53,8 @@ public override bool HasReferenceTypeConstraint
get { return false; }
}
internal override bool? IsNotNullableIfReferenceType => null;
public override bool HasValueTypeConstraint
{
get { return false; }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册