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

Use lightweight type constraint binding for IsValueType #48270

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 0 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,6 @@ internal bool AreNullableAnnotationsEnabled(SyntaxTree syntaxTree, int position)
internal bool AreNullableAnnotationsEnabled(SyntaxToken token)
{
RoslynDebug.Assert(token.SyntaxTree is object);
if ((Flags & BinderFlags.IgnoreNullableContext) != 0)
{
return false;
}
return AreNullableAnnotationsEnabled(token.SyntaxTree, token.SpanStart);
}

Expand Down
6 changes: 4 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/BinderFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable disable

using System;
using Microsoft.CodeAnalysis.CSharp.Symbols;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand Down Expand Up @@ -113,9 +114,10 @@ internal enum BinderFlags : uint
InEEMethodBinder = 1 << 30,

/// <summary>
/// Assume '#nullable disable' context.
/// Skip binding type arguments (we use <see cref="PlaceholderTypeArgumentSymbol"/> instead).
/// For example, currently used when type constraints are bound in some scenarios.
/// </summary>
IgnoreNullableContext = 1u << 31,
SuppressTypeArgumentBinding = 1u << 31,

// Groups

Expand Down
9 changes: 4 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
ImmutableArray<TypeParameterSymbol> typeParameters,
TypeParameterListSyntax typeParameterList,
SyntaxList<TypeParameterConstraintClauseSyntax> clauses,
bool canIgnoreNullableContext,
ref IReadOnlyDictionary<TypeParameterSymbol, bool> isValueTypeOverride,
DiagnosticBag diagnostics,
bool isForOverride = false)
Expand Down Expand Up @@ -67,7 +66,7 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
Debug.Assert(ordinal < n);

(TypeParameterConstraintClause constraintClause, ArrayBuilder<TypeConstraintSyntax>? typeConstraintNodes) =
this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, canIgnoreNullableContext: canIgnoreNullableContext, diagnostics);
this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, diagnostics);
if (results[ordinal] == null)
{
results[ordinal] = constraintClause;
Expand Down Expand Up @@ -119,7 +118,7 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
/// Bind and return a single type parameter constraint clause along with syntax nodes corresponding to type constraints.
/// </summary>
private (TypeParameterConstraintClause, ArrayBuilder<TypeConstraintSyntax>?) BindTypeParameterConstraints(
TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, bool canIgnoreNullableContext, DiagnosticBag diagnostics)
TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, DiagnosticBag diagnostics)
{
var constraints = TypeParameterConstraintKind.None;
ArrayBuilder<TypeWithAnnotations>? constraintTypes = null;
Expand Down Expand Up @@ -309,7 +308,7 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
Debug.Assert(!isForOverride ||
(constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType));

return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray<TypeWithAnnotations>.Empty, canIgnoreNullableContext), syntaxBuilder);
return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray<TypeWithAnnotations>.Empty), syntaxBuilder);

static void reportOverrideWithConstraints(ref bool reportedOverrideWithConstraints, TypeParameterConstraintSyntax syntax, DiagnosticBag diagnostics)
{
Expand Down Expand Up @@ -392,7 +391,7 @@ private static TypeParameterConstraintClause RemoveInvalidConstraints(

if (constraintTypeBuilder.Count < n)
{
return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree(), constraintClause.IgnoresNullableContext);
return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree());
}

constraintTypeBuilder.Free();
Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,10 @@ private TypeWithAnnotations BindGenericSimpleNamespaceOrTypeOrAliasSymbol(
resultType = unconstructedType.AsUnboundGenericType();
}
}
else if ((Flags & BinderFlags.SuppressTypeArgumentBinding) != 0)
{
resultType = unconstructedType.Construct(PlaceholderTypeArgumentSymbol.CreateTypeArguments(unconstructedType.TypeParameters), unbound: false);
}
else
{
// It's not an unbound type expression, so we must have type arguments, and we have a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public sealed override ImmutableArray<TypeParameterSymbol> TypeParameters
get { return _typeParameters; }
}

public sealed override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterConstraintClauses(bool canIgnoreNullableContext)
public sealed override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterConstraintClauses(bool canUseLightweightTypeConstraintBinding)
=> ImmutableArray<TypeParameterConstraintClause>.Empty;

internal override int ParameterCount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ public override VarianceKind Variance
get { return VarianceKind.None; }
}

internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext)
internal override void EnsureAllConstraintsAreResolved(bool canUseLightweightTypeConstraintBinding)
{
}

internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool canIgnoreNullableContext)
internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool canUseLightweightTypeConstraintBinding)
{
return ImmutableArray<TypeWithAnnotations>.Empty;
}
Expand Down
47 changes: 28 additions & 19 deletions src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ public TypeParameterDiagnosticInfo(TypeParameterSymbol typeParameter, Diagnostic
/// Helper methods for generic type parameter constraints. There are two sets of methods: one
/// set for resolving constraint "bounds" (that is, determining the effective base type, interface set,
/// etc.), and another set for checking for constraint violations in type and method references.
///
///
/// Bounds are resolved by calling one of the ResolveBounds overloads. Typically bounds are
/// resolved by each TypeParameterSymbol at, or before, one of the corresponding properties
/// (BaseType, Interfaces, etc.) is accessed. Resolving bounds may result in errors (cycles,
/// inconsistent constraints, etc.) and it is the responsibility of the caller to report any such
/// errors as declaration errors or use-site errors (depending on whether the type parameter
/// was from source or metadata) and to ensure bounds are resolved for source type parameters
/// even if the corresponding properties are never accessed directly.
///
///
/// Constraints are checked by calling one of the CheckConstraints or CheckAllConstraints
/// overloads for any generic type or method reference from source. In some circumstances,
/// references are checked at the time the generic type or generic method is bound and constructed
Expand All @@ -71,13 +71,13 @@ public static TypeParameterBounds ResolveBounds(
ConsList<TypeParameterSymbol> inProgress,
ImmutableArray<TypeWithAnnotations> constraintTypes,
bool inherited,
bool ignoresNullableContext,
bool canUseLightweightTypeConstraintBinding,
CSharpCompilation currentCompilation,
DiagnosticBag diagnostics)
{
var diagnosticsBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
var bounds = typeParameter.ResolveBounds(corLibrary, inProgress, constraintTypes, inherited, ignoresNullableContext: ignoresNullableContext, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder);
var bounds = typeParameter.ResolveBounds(corLibrary, inProgress, constraintTypes, inherited, canUseLightweightTypeConstraintBinding, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder);

if (useSiteDiagnosticsBuilder != null)
{
Expand All @@ -90,6 +90,14 @@ public static TypeParameterBounds ResolveBounds(
}

diagnosticsBuilder.Free();

if (bounds is null && canUseLightweightTypeConstraintBinding)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (bounds is null && canUseLightweightTypeConstraintBinding) [](start = 12, length = 61)

Would we need this additional logic if we never cached "light-bound" constraints on a symbol?

{
// We have no bounds, but need to record that we've only done
// lightweight binding of type constraints so far.
bounds = TypeParameterBounds.NullFromLightweightBinding;
}

return bounds;
}

Expand All @@ -100,7 +108,7 @@ public static TypeParameterBounds ResolveBounds(
ConsList<TypeParameterSymbol> inProgress,
ImmutableArray<TypeWithAnnotations> constraintTypes,
bool inherited,
bool ignoresNullableContext,
bool canUseLightweightTypeConstraintBinding,
CSharpCompilation currentCompilation,
ArrayBuilder<TypeParameterDiagnosticInfo> diagnosticsBuilder,
ref ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder)
Expand Down Expand Up @@ -299,7 +307,7 @@ public static TypeParameterBounds ResolveBounds(
return null;
}

var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType, ignoresNullableContext);
var bounds = new TypeParameterBounds(constraintTypes, interfaces, effectiveBaseClass, deducedBaseType, canUseLightweightTypeConstraintBinding);

// Additional constraint checks for overrides.
if (inherited)
Expand All @@ -310,37 +318,38 @@ public static TypeParameterBounds ResolveBounds(
return bounds;
}

internal static ImmutableArray<TypeParameterConstraintClause> MakeTypeParameterConstraints(
internal static TypeParameterConstraintClauses MakeTypeParameterConstraints(
this Symbol containingSymbol,
Binder binder,
ImmutableArray<TypeParameterSymbol> typeParameters,
TypeParameterListSyntax typeParameterList,
SyntaxList<TypeParameterConstraintClauseSyntax> constraintClauses,
bool canIgnoreNullableContext,
bool canUseLightweightTypeConstraintBinding,
DiagnosticBag diagnostics)
{
if (typeParameters.Length == 0)
{
return ImmutableArray<TypeParameterConstraintClause>.Empty;
return TypeParameterConstraintClauses.Create(ImmutableArray<TypeParameterConstraintClause>.Empty, usedLightweightTypeConstraintBinding: false);
}

if (constraintClauses.Count == 0)
{
ImmutableArray<TypeParameterConstraintClause> defaultClauses = binder.GetDefaultTypeParameterConstraintClauses(typeParameterList);

return defaultClauses.ContainsOnlyEmptyConstraintClauses() ? ImmutableArray<TypeParameterConstraintClause>.Empty : defaultClauses;
return TypeParameterConstraintClauses.Create(
defaultClauses.ContainsOnlyEmptyConstraintClauses() ? ImmutableArray<TypeParameterConstraintClause>.Empty : defaultClauses,
usedLightweightTypeConstraintBinding: false);
}

// 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);
binder = binder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks | (canUseLightweightTypeConstraintBinding ? BinderFlags.SuppressTypeArgumentBinding : 0));

IReadOnlyDictionary<TypeParameterSymbol, bool> isValueTypeOverride = null;
return binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses,
canIgnoreNullableContext,
ref isValueTypeOverride,
diagnostics);
return TypeParameterConstraintClauses.Create(
binder.BindTypeParameterConstraintClauses(containingSymbol, typeParameters, typeParameterList, constraintClauses, ref isValueTypeOverride, diagnostics),
usedLightweightTypeConstraintBinding: canUseLightweightTypeConstraintBinding);
}

// Based on SymbolLoader::SetOverrideConstraints.
Expand Down Expand Up @@ -496,7 +505,7 @@ void populateDiagnosticsAndClear(ArrayBuilder<TypeParameterDiagnosticInfo> build
{
var ordinal = pair.TypeParameter.Ordinal;

// If this is the TRest type parameter, we report it on
// If this is the TRest type parameter, we report it on
// the entire type syntax as it does not map to any tuple element.
var location = ordinal == NamedTypeSymbol.ValueTupleRestIndex ? typeSyntax.Location : elementLocations[ordinal + offset];
bag.Add(new CSDiagnostic(pair.DiagnosticInfo, location));
Expand Down Expand Up @@ -614,7 +623,7 @@ private static bool HasDuplicateInterfaces(NamedTypeSymbol type, ConsList<TypeSy
break;
}

// two unrelated interfaces
// two unrelated interfaces
return false;

default:
Expand All @@ -633,7 +642,7 @@ private static bool HasDuplicateInterfaces(NamedTypeSymbol type, ConsList<TypeSy
return false;
}

// very rare case.
// very rare case.
// some implemented interfaces are related
// will have to instantiate interfaces and check
hasRelatedInterfaces:
Expand Down Expand Up @@ -756,7 +765,7 @@ public static bool CheckMethodConstraints(
/// <param name="nullabilityDiagnosticsBuilderOpt">Nullability warnings.</param>
/// <param name="skipParameters">Parameters to skip.</param>
/// <param name="useSiteDiagnosticsBuilder"/>
/// <param name="ignoreTypeConstraintsDependentOnTypeParametersOpt">If an original form of a type constraint
/// <param name="ignoreTypeConstraintsDependentOnTypeParametersOpt">If an original form of a type constraint
/// depends on a type parameter from this set, do not verify this type constraint.</param>
/// <returns>True if the constraints were satisfied, false otherwise.</returns>
public static bool CheckConstraints(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,11 @@ public override bool IsImplicitlyDeclared
}
}

internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext)
internal override void EnsureAllConstraintsAreResolved(bool canUseLightweightTypeConstraintBinding)
{
}

internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool canIgnoreNullableContext)
internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool canUseLightweightTypeConstraintBinding)
{
return ImmutableArray<TypeWithAnnotations>.Empty;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,19 +570,19 @@ public override VarianceKind Variance
}
}

internal override void EnsureAllConstraintsAreResolved(bool canIgnoreNullableContext)
internal override void EnsureAllConstraintsAreResolved(bool canUseLightweightTypeConstraintBinding)
{
canIgnoreNullableContext = false; // Resolve bounds eagerly.
if (!_lazyBounds.HasValue(canIgnoreNullableContext))
canUseLightweightTypeConstraintBinding = false; // Resolve bounds eagerly.
if (!_lazyBounds.HasValue(canUseLightweightTypeConstraintBinding))
{
var typeParameters = (_containingSymbol.Kind == SymbolKind.Method) ?
((PEMethodSymbol)_containingSymbol).TypeParameters :
((PENamedTypeSymbol)_containingSymbol).TypeParameters;
EnsureAllConstraintsAreResolved(typeParameters, canIgnoreNullableContext);
EnsureAllConstraintsAreResolved(typeParameters, canUseLightweightTypeConstraintBinding);
}
}

internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool canIgnoreNullableContext)
internal override ImmutableArray<TypeWithAnnotations> GetConstraintTypes(ConsList<TypeParameterSymbol> inProgress, bool canUseLightweightTypeConstraintBinding)
{
var bounds = this.GetBounds(inProgress);
return (bounds != null) ? bounds.ConstraintTypes : ImmutableArray<TypeWithAnnotations>.Empty;
Expand Down Expand Up @@ -637,7 +637,7 @@ private TypeParameterBounds GetBounds(ConsList<TypeParameterSymbol> inProgress)
var diagnostics = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance();
ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null;
bool inherited = (_containingSymbol.Kind == SymbolKind.Method) && ((MethodSymbol)_containingSymbol).IsOverride;
var bounds = this.ResolveBounds(this.ContainingAssembly.CorLibrary, inProgress.Prepend(this), constraintTypes, inherited, ignoresNullableContext: false, currentCompilation: null,
var bounds = this.ResolveBounds(this.ContainingAssembly.CorLibrary, inProgress.Prepend(this), constraintTypes, inherited, canUseLightweightTypeConstraintBinding: false, currentCompilation: null,
diagnosticsBuilder: diagnostics, useSiteDiagnosticsBuilder: ref useSiteDiagnosticsBuilder);
DiagnosticInfo errorInfo = null;

Expand Down Expand Up @@ -673,7 +673,7 @@ private TypeParameterBounds GetBounds(ConsList<TypeParameterSymbol> inProgress)

internal override DiagnosticInfo GetConstraintsUseSiteErrorInfo()
{
EnsureAllConstraintsAreResolved(canIgnoreNullableContext: false);
EnsureAllConstraintsAreResolved(canUseLightweightTypeConstraintBinding: false);
Debug.Assert(!ReferenceEquals(_lazyConstraintsUseSiteErrorInfo, CSDiagnosticInfo.EmptyErrorInfo));
return _lazyConstraintsUseSiteErrorInfo;
}
Expand Down
Loading