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

Specially handle more scenarios for type parameters with 'allows ref struct' constraint #73059

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ private string GetDebuggerDisplay()
}

if (declTypeWithAnnotations.HasType &&
localSymbol.Scope == ScopedKind.ScopedValue && !declTypeWithAnnotations.Type.IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike())
localSymbol.Scope == ScopedKind.ScopedValue && !declTypeWithAnnotations.Type.IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3126,7 +3126,7 @@ private BoundExpression BindOutDeclarationArgument(DeclarationExpressionSyntax d

CheckRestrictedTypeInAsyncMethod(this.ContainingMemberOrLambda, declType.Type, diagnostics, typeSyntax);

if (localSymbol.Scope == ScopedKind.ScopedValue && !declType.Type.IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike())
if (localSymbol.Scope == ScopedKind.ScopedValue && !declType.Type.IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,7 @@ protected BoundExpression BindInferredVariableInitializer(BindingDiagnosticBag d

CheckRestrictedTypeInAsyncMethod(this.ContainingMemberOrLambda, declTypeOpt.Type, localDiagnostics, typeSyntax);

if (localSymbol.Scope == ScopedKind.ScopedValue && !declTypeOpt.Type.IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike())
if (localSymbol.Scope == ScopedKind.ScopedValue && !declTypeOpt.Type.IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike())
{
localDiagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno

CheckRestrictedTypeInAsyncMethod(this.ContainingMemberOrLambda, declType.Type, diagnostics, typeSyntax);

if (local.Scope == ScopedKind.ScopedValue && !declType.Type.IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike())
if (local.Scope == ScopedKind.ScopedValue && !declType.Type.IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal BoundExpression SetInferredTypeWithAnnotations(TypeWithAnnotations type

Binder.CheckRestrictedTypeInAsyncMethod(localSymbol.ContainingSymbol, type.Type, diagnosticsOpt, typeOrDesignationSyntax);

if (localSymbol.Scope == ScopedKind.ScopedValue && !type.Type.IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike())
if (localSymbol.Scope == ScopedKind.ScopedValue && !type.Type.IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike())
{
diagnosticsOpt.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly,
(typeOrDesignationSyntax is TypeSyntax typeSyntax ? typeSyntax.SkipScoped(out _).SkipRef() : typeOrDesignationSyntax).Location);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ private NamedTypeSymbol ConstructAnonymousDelegateImplementationSymbol(Anonymous
// If all parameter types and return type are valid type arguments, construct
// the delegate type from a generic template. Otherwise, use a non-generic template.
bool useUpdatedEscapeRules = Compilation.SourceModule.UseUpdatedEscapeRules;
if (allValidTypeArguments(useUpdatedEscapeRules, typeDescr, out var needsIndexedName))
bool runtimeSupportsByRefLikeGenerics = Compilation.SourceAssembly.RuntimeSupportsByRefLikeGenerics;

if (allValidTypeArguments(useUpdatedEscapeRules, runtimeSupportsByRefLikeGenerics, typeDescr, out var needsIndexedName))
{
var fields = typeDescr.Fields;
Debug.Assert(fields.All(f => hasDefaultScope(useUpdatedEscapeRules, f)));
Expand Down Expand Up @@ -294,20 +296,20 @@ private NamedTypeSymbol ConstructAnonymousDelegateImplementationSymbol(Anonymous
template.Construct(typeParameters);
}

static bool allValidTypeArguments(bool useUpdatedEscapeRules, AnonymousTypeDescriptor typeDescr, out bool needsIndexedName)
static bool allValidTypeArguments(bool useUpdatedEscapeRules, bool runtimeSupportsByRefLikeGenerics, AnonymousTypeDescriptor typeDescr, out bool needsIndexedName)
{
needsIndexedName = false;
var fields = typeDescr.Fields;
int n = fields.Length;
for (int i = 0; i < n - 1; i++)
{
if (!isValidTypeArgument(useUpdatedEscapeRules, fields[i], ref needsIndexedName))
if (!isValidTypeArgument(useUpdatedEscapeRules, runtimeSupportsByRefLikeGenerics, fields[i], ref needsIndexedName))
{
return false;
}
}
var returnParameter = fields[n - 1];
return returnParameter.Type.IsVoidType() || isValidTypeArgument(useUpdatedEscapeRules, returnParameter, ref needsIndexedName);
return returnParameter.Type.IsVoidType() || isValidTypeArgument(useUpdatedEscapeRules, runtimeSupportsByRefLikeGenerics, returnParameter, ref needsIndexedName);
}

static bool hasDefaultScope(bool useUpdatedEscapeRules, AnonymousTypeField field)
Expand All @@ -324,13 +326,13 @@ static bool hasDefaultScope(bool useUpdatedEscapeRules, AnonymousTypeField field
};
}

static bool isValidTypeArgument(bool useUpdatedEscapeRules, AnonymousTypeField field, ref bool needsIndexedName)
static bool isValidTypeArgument(bool useUpdatedEscapeRules, bool runtimeSupportsByRefLikeGenerics, AnonymousTypeField field, ref bool needsIndexedName)
{
needsIndexedName = needsIndexedName || field.IsParams || field.DefaultValue is not null;
return hasDefaultScope(useUpdatedEscapeRules, field) &&
field.Type is { } type &&
!type.IsPointerOrFunctionPointer() &&
!type.IsRestrictedType() && // PROTOTYPE(RefStructInterfaces): Is this doing the right thing for 'allows ref struct' type parameters?
(type.IsTypeParameter() || !type.IsRestrictedType(ignoreSpanLikeTypes: runtimeSupportsByRefLikeGenerics)) &&
(!field.IsParams || field.Type.IsSZArray()); // [params T collection] is not recognized as a valid params parameter definition
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ internal sealed partial class AnonymousTypeManager
/// </summary>
internal sealed class AnonymousTypeParameterSymbol : TypeParameterSymbol
{
private readonly Symbol _container;
private readonly AnonymousTypeOrDelegateTemplateSymbol _container;
private readonly int _ordinal;
private readonly string _name;

public AnonymousTypeParameterSymbol(Symbol container, int ordinal, string name)
public AnonymousTypeParameterSymbol(AnonymousTypeOrDelegateTemplateSymbol container, int ordinal, string name)
{
Debug.Assert((object)container != null);
Debug.Assert(!string.IsNullOrEmpty(name));
Expand Down Expand Up @@ -93,7 +93,10 @@ public override bool HasValueTypeConstraint

public override bool AllowByRefLike
{
get { return false; }
get
{
return _container.IsDelegateType() && _container.Manager.Compilation.SourceAssembly.RuntimeSupportsByRefLikeGenerics;
Copy link
Member

@cston cston Apr 19, 2024

Choose a reason for hiding this comment

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

Manager.Compilation.SourceAssembly

Consider simplifying to ContainingAssembly. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Manager.Compilation.SourceAssembly

Consider simplifying to ContainingAssembly.

Will do for the next PR.

}
}

public override bool IsValueTypeFromConstraintTypes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1538,7 +1538,7 @@ private void DoMiscValidation()
validateParamsType(diagnostics);
}

if (DeclaredScope == ScopedKind.ScopedValue && !Type.IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike())
if (DeclaredScope == ScopedKind.ScopedValue && !Type.IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike())
{
Debug.Assert(ParameterSyntax is not null);
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, ParameterSyntax);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols
Expand All @@ -13,6 +14,7 @@ internal sealed class SynthesizedReadOnlyListTypeParameterSymbol : TypeParameter

internal SynthesizedReadOnlyListTypeParameterSymbol(SynthesizedReadOnlyListTypeSymbol containingType)
{
Debug.Assert(containingType.IsClassType());
_containingType = containingType;
}

Expand All @@ -32,7 +34,7 @@ internal SynthesizedReadOnlyListTypeParameterSymbol(SynthesizedReadOnlyListTypeS

public override bool HasValueTypeConstraint => false;

public override bool AllowByRefLike => false; // PROTOTYPE(RefStructInterfaces): That should probably match constraints on implemented interface(s).
public override bool AllowByRefLike => false; // The list is a class type and cannot store ref structs as elements.

public override bool IsValueTypeFromConstraintTypes => false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
diagnostics.Add(ErrorCode.ERR_BadFieldTypeInRecord, f.GetFirstLocationOrNone(), parameterType);
foundBadField = true;
}
else if (parameterType.IsRestrictedType()) // PROTOTYPE(RefStructInterfaces): Is this doing the right thing for 'allows ref struct' type parameters?
else if (parameterType.IsRestrictedType())
{
// We'll have reported a diagnostic elsewhere (SourceMemberFieldSymbol.TypeChecks)
foundBadField = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,9 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
F.WellKnownMethod(WellKnownMember.System_Text_StringBuilder__AppendString),
F.Call(value, F.SpecialMethod(SpecialMember.System_Object__ToString)))));
}
else
else if (!value.Type.IsRestrictedType())
{
// Otherwise, an error has been reported elsewhere (SourceMemberFieldSymbol.TypeChecks)
block.Add(F.ExpressionStatement(
F.Call(receiver: builder,
F.WellKnownMethod(WellKnownMember.System_Text_StringBuilder__AppendObject),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ public static bool IsPossibleArrayGenericInterface(this TypeSymbol type)
return false;
}

internal static bool IsErrorTypeOrIsRefLikeTypeOrAllowByRefLike(this TypeSymbol type)
internal static bool IsErrorTypeOrIsRefLikeTypeOrAllowsByRefLike(this TypeSymbol type)
{
return type.IsErrorType() || type.IsRefLikeTypeOrAllowsByRefLike();
}
Expand Down