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 #73363

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4155,8 +4155,14 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres
return GetValEscape(conversion.Operand, scopeOfTheContainingExpression);

case BoundKind.AssignmentOperator:
// PROTOTYPE(RefStructInterfaces): We do not have a test that demonstrates that the statement below makes a difference.
// If 'scopeOfTheContainingExpression' is always returned, not a single test fails.
// Same for the 'case BoundKind.NullCoalescingAssignmentOperator:' below.
return GetValEscape(((BoundAssignmentOperator)expr).Right, scopeOfTheContainingExpression);

case BoundKind.NullCoalescingAssignmentOperator:
return GetValEscape(((BoundNullCoalescingAssignmentOperator)expr).RightOperand, scopeOfTheContainingExpression);

case BoundKind.IncrementOperator:
return GetValEscape(((BoundIncrementOperator)expr).Operand, scopeOfTheContainingExpression);

Expand Down Expand Up @@ -4267,7 +4273,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres

case BoundKind.AsOperator:
case BoundKind.AwaitExpression:
case BoundKind.ConditionalAccess:
case BoundKind.ConditionalAccess: // PROTOTYPE(RefStructInterfaces): Conditional access is allowed on type parameters. Is current implementation correct for 'allows ref struct' case?
case BoundKind.ConditionalReceiver:
case BoundKind.ArrayAccess:
// only possible in error cases (if possible at all)
Expand Down Expand Up @@ -4847,6 +4853,10 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF
var assignment = (BoundAssignmentOperator)expr;
return CheckValEscape(node, assignment.Left, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);

case BoundKind.NullCoalescingAssignmentOperator:
var nullCoalescingAssignment = (BoundNullCoalescingAssignmentOperator)expr;
return CheckValEscape(node, nullCoalescingAssignment.LeftOperand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);

case BoundKind.IncrementOperator:
var increment = (BoundIncrementOperator)expr;
return CheckValEscape(node, increment.Operand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics);
Expand Down Expand Up @@ -5001,7 +5011,6 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF
// case BoundKind.SizeOfOperator:
// case BoundKind.DynamicMemberAccess:
// case BoundKind.DynamicInvocation:
// case BoundKind.NewT:
// case BoundKind.DelegateCreationExpression:
// case BoundKind.ArrayCreation:
// case BoundKind.AnonymousObjectCreationExpression:
Expand Down
24 changes: 15 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -454,15 +454,13 @@ void checkConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNode syn

diagnostics.ReportUseSite(elementField, syntax);

Symbol? unsafeAsMethod = null;

if (destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions))
{
if (CheckValueKind(syntax, source, BindValueKind.RefersToLocation, checkingReceiver: false, BindingDiagnosticBag.Discarded))
{
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_MemoryMarshal__CreateReadOnlySpan, diagnostics, syntax: syntax); // This also takes care of an 'int' type
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__AsRef_T, diagnostics, syntax: syntax);
unsafeAsMethod = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: syntax);
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: syntax);
}
else
{
Expand All @@ -476,23 +474,31 @@ void checkConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNode syn
if (CheckValueKind(syntax, source, BindValueKind.RefersToLocation | BindValueKind.Assignable, checkingReceiver: false, BindingDiagnosticBag.Discarded))
{
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_InteropServices_MemoryMarshal__CreateSpan, diagnostics, syntax: syntax); // This also takes care of an 'int' type
unsafeAsMethod = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: syntax);
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: syntax);
}
else
{
Error(diagnostics, ErrorCode.ERR_InlineArrayConversionToSpanNotSupported, syntax, destination);
}
}

if (unsafeAsMethod is MethodSymbol { HasUnsupportedMetadata: false } method)
{
method.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(source.Type), elementField.TypeWithAnnotations)).
CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, syntax.GetLocation(), diagnostics));
}
CheckInlineArrayTypeIsSupported(syntax, source.Type, elementField.Type, diagnostics);
}
}
}

private static void CheckInlineArrayTypeIsSupported(SyntaxNode syntax, TypeSymbol inlineArrayType, TypeSymbol elementType, BindingDiagnosticBag diagnostics)
{
if (elementType.IsPointerOrFunctionPointer() || elementType.IsRestrictedType())
{
Error(diagnostics, ErrorCode.ERR_BadTypeArgument, syntax, elementType);
}
else if (inlineArrayType.IsRestrictedType())
{
Error(diagnostics, ErrorCode.ERR_BadTypeArgument, syntax, inlineArrayType);
}
}

private static BoundExpression ConvertObjectCreationExpression(
SyntaxNode syntax, BoundUnconvertedObjectCreationExpression node, Conversion conversion, bool isCast, TypeSymbol destination,
ConversionGroup? conversionGroupOpt, bool wasCompilerGenerated, BindingDiagnosticBag diagnostics)
Expand Down
12 changes: 4 additions & 8 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6172,14 +6172,14 @@ private bool CollectionInitializerTypeImplementsIEnumerable(TypeSymbol initializ
}
else if (!initializerType.IsErrorType())
{
TypeSymbol collectionsIEnumerableType = this.GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, node);
NamedTypeSymbol collectionsIEnumerableType = this.GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, node);

// NOTE: Ideally, to check if the initializer type implements System.Collections.IEnumerable we can walk through
// NOTE: its implemented interfaces. However the native compiler checks to see if there is conversion from initializer
// NOTE: type to the predefined System.Collections.IEnumerable type, so we do the same.

CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var result = Conversions.ClassifyImplicitConversionFromType(initializerType, collectionsIEnumerableType, ref useSiteInfo).IsValid;
var result = Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(initializerType, collectionsIEnumerableType, ref useSiteInfo);
diagnostics.Add(node, useSiteInfo);
return result;
}
Expand Down Expand Up @@ -9131,15 +9131,11 @@ BoundExpression bindInlineArrayElementAccess(ExpressionSyntax node, BoundExpress
}
}

var unsafeAsMethod = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: node);
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: node);
_ = GetWellKnownTypeMember(createSpanHelper, diagnostics, syntax: node);
_ = GetWellKnownTypeMember(getItemOrSliceHelper, diagnostics, syntax: node);

if (unsafeAsMethod is MethodSymbol { HasUnsupportedMetadata: false } method)
{
method.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(expr.Type), elementField.TypeWithAnnotations)).
CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, node.GetLocation(), diagnostics));
}
CheckInlineArrayTypeIsSupported(node, expr.Type, elementField.Type, diagnostics);

if (!Compilation.Assembly.RuntimeSupportsInlineArrayTypes)
{
Expand Down
8 changes: 2 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -882,13 +882,9 @@ private EnumeratorResult GetEnumeratorInfoCore(SyntaxNode syntax, SyntaxNode col
}

_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__Add_T, diagnostics, syntax: collectionExpr.Syntax);
var unsafeAsMethod = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: collectionExpr.Syntax);
_ = GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_Unsafe__As_T, diagnostics, syntax: collectionExpr.Syntax);

if (unsafeAsMethod is MethodSymbol { HasUnsupportedMetadata: false } method)
{
method.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(collectionExpr.Type), elementField.TypeWithAnnotations)).
CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, collectionExpr.Syntax.GetLocation(), diagnostics));
}
CheckInlineArrayTypeIsSupported(collectionExpr.Syntax, collectionExpr.Type, elementField.Type, diagnostics);
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,6 @@ private BoundExpression MakeObjectInitializerMemberAccess(

#if DEBUG
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
// PROTOTYPE(RefStructInterfaces): Test various flavors of object/collection initializers on a type parameter that allows ref struct
Debug.Assert(_compilation.Conversions.ClassifyConversionFromType(rewrittenReceiver.Type, memberSymbol.ContainingType, isChecked: false, ref discardedUseSiteInfo).IsImplicit ||
_compilation.Conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(rewrittenReceiver.Type, memberSymbol.ContainingType, ref discardedUseSiteInfo));
// It is possible there are use site diagnostics from the above, but none that we need report as we aren't generating code for the conversion
Expand Down
Loading