Skip to content

Commit

Permalink
Merge branch 'features/RefStructInterfaces' into 'main' (#73567)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseyTs authored May 18, 2024
2 parents 9c89909 + 82888a2 commit 07d428b
Show file tree
Hide file tree
Showing 180 changed files with 32,948 additions and 979 deletions.
212 changes: 175 additions & 37 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs

Large diffs are not rendered by default.

43 changes: 42 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
diagnostics.Add(ErrorCode.ERR_NewBoundWithUnmanaged, syntax.GetFirstToken().GetLocation());
}

if (i != n - 1)
if (i != n - 1 && constraintsSyntax[i + 1].Kind() != SyntaxKind.AllowsConstraintClause)
{
diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation());
}
Expand Down Expand Up @@ -290,6 +290,47 @@ internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstrai
syntaxBuilder!.Add(typeConstraintSyntax);
}
continue;

case SyntaxKind.AllowsConstraintClause:

if (isForOverride)
{
reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics);
continue;
}

if (i != n - 1)
{
diagnostics.Add(ErrorCode.ERR_AllowsClauseMustBeLast, syntax.GetFirstToken().GetLocation());
}

bool hasRefStructConstraint = false;

foreach (var allowsConstraint in ((AllowsConstraintClauseSyntax)syntax).Constraints)
{
if (allowsConstraint.Kind() == SyntaxKind.RefStructConstraint)
{
if (hasRefStructConstraint)
{
diagnostics.Add(ErrorCode.ERR_RefStructConstraintAlreadySpecified, allowsConstraint);
}
else
{
CheckFeatureAvailability(allowsConstraint, MessageID.IDS_FeatureRefStructInterfaces, diagnostics);

if (!Compilation.Assembly.RuntimeSupportsByRefLikeGenerics)
{
Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, allowsConstraint);
}

constraints |= TypeParameterConstraintKind.AllowByRefLike;
hasRefStructConstraint = true;
}
}
}

continue;

default:
throw ExceptionUtilities.UnexpectedValue(syntax.Kind());
}
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
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ private BoundExpression BindDeconstructionVariable(
}

if (declTypeWithAnnotations.HasType &&
localSymbol.Scope == ScopedKind.ScopedValue && !declTypeWithAnnotations.Type.IsErrorTypeOrRefLikeType())
localSymbol.Scope == ScopedKind.ScopedValue && !declTypeWithAnnotations.Type.IsErrorOrRefLikeOrAllowsRefLikeType())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down
41 changes: 27 additions & 14 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2046,7 +2046,7 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind
}
else
{
Debug.Assert(parameter.Type.IsRefLikeType);
Debug.Assert(parameter.Type.IsRefLikeOrAllowsRefLikeType());
Error(diagnostics, ErrorCode.ERR_AnonDelegateCantUseRefLike, node, parameter.Name);
}
}
Expand All @@ -2069,7 +2069,7 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind
}
else
{
Debug.Assert(parameter.Type.IsRefLikeType);
Debug.Assert(parameter.Type.IsRefLikeOrAllowsRefLikeType());
Error(diagnostics, ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike, node, parameter.Name);
}
}
Expand Down Expand Up @@ -3126,7 +3126,7 @@ private BoundExpression BindOutVariableDeclarationArgument(

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

if (localSymbol.Scope == ScopedKind.ScopedValue && !declType.Type.IsErrorTypeOrRefLikeType())
if (localSymbol.Scope == ScopedKind.ScopedValue && !declType.Type.IsErrorOrRefLikeOrAllowsRefLikeType())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down Expand Up @@ -6151,15 +6151,22 @@ 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, out bool needSupportForRefStructInterfaces);
diagnostics.Add(node, useSiteInfo);

if (needSupportForRefStructInterfaces &&
initializerType.ContainingModule != Compilation.SourceModule)
{
CheckFeatureAvailability(node, MessageID.IDS_FeatureRefStructInterfaces, diagnostics);
}

return result;
}
else
Expand Down Expand Up @@ -8621,10 +8628,14 @@ private void CheckReceiverAndRuntimeSupportForSymbolAccess(SyntaxNode node, Boun
}
}

if (!Compilation.Assembly.RuntimeSupportsDefaultInterfaceImplementation && Compilation.SourceModule != symbol.ContainingModule)
if (receiverOpt is { Type: TypeParameterSymbol { AllowsRefLikeType: true } } &&
isNotImplementableInstanceMember(symbol))
{
Error(diagnostics, ErrorCode.ERR_BadNonVirtualInterfaceMemberAccessOnAllowsRefLike, node);
}
else if (!Compilation.Assembly.RuntimeSupportsDefaultInterfaceImplementation && Compilation.SourceModule != symbol.ContainingModule)
{
if (!symbol.IsStatic && !(symbol is TypeSymbol) &&
!symbol.IsImplementableInterfaceMember())
if (isNotImplementableInstanceMember(symbol))
{
Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, node);
}
Expand All @@ -8642,6 +8653,12 @@ private void CheckReceiverAndRuntimeSupportForSymbolAccess(SyntaxNode node, Boun
}
}
}

static bool isNotImplementableInstanceMember(Symbol symbol)
{
return !symbol.IsStatic && !(symbol is TypeSymbol) &&
!symbol.IsImplementableInterfaceMember();
}
}

private BoundExpression BindEventAccess(
Expand Down Expand Up @@ -9069,15 +9086,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
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ private void CheckRestrictedTypeReceiver(BoundExpression expression, CSharpCompi
// error CS0029: Cannot implicitly convert type 'A' to 'B'

// Case 1: receiver is a restricted type, and method called is defined on a parent type
if (call.ReceiverOpt.Type.IsRestrictedType() && !TypeSymbol.Equals(call.Method.ContainingType, call.ReceiverOpt.Type, TypeCompareKind.ConsiderEverything2))
if (call.ReceiverOpt.Type.IsRestrictedType() && !call.Method.ContainingType.IsInterface && !TypeSymbol.Equals(call.Method.ContainingType, call.ReceiverOpt.Type, TypeCompareKind.ConsiderEverything2))
{
SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, call.ReceiverOpt.Type, call.Method.ContainingType);
Error(diagnostics, ErrorCode.ERR_NoImplicitConv, call.ReceiverOpt.Syntax, distinguisher.First, distinguisher.Second);
Expand Down Expand Up @@ -1359,7 +1359,7 @@ internal ThreeState ReceiverIsSubjectToCloning(BoundExpression? receiver, Proper

internal ThreeState ReceiverIsSubjectToCloning(BoundExpression? receiver, MethodSymbol method)
{
if (receiver is BoundValuePlaceholderBase || receiver?.Type?.IsValueType != true)
if (receiver is BoundValuePlaceholderBase || receiver?.Type is null or { IsReferenceType: true })
{
return ThreeState.False;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy

// UNDONE: Where do we report improper use of pointer types?
ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i),
lambda.RefKind(i), lambda.DeclaredScope(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics);
lambda.RefKind(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics);
}
}

Expand Down
34 changes: 33 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3511,10 +3511,37 @@ internal static ConstantValue GetIsOperatorConstantResult(
return ConstantValue.False;
}

// * If either type is a restricted type, the type check isn't supported because
// * If either type is a restricted type, the type check isn't supported for some scenarios because
// a restricted type cannot be boxed or unboxed into.
if (targetType.IsRestrictedType() || operandType.IsRestrictedType())
{
if (targetType is TypeParameterSymbol { AllowsRefLikeType: true })
{
if (!operandType.IsRefLikeType && operandType is not TypeParameterSymbol)
{
return null;
}
}
else if (operandType is not TypeParameterSymbol { AllowsRefLikeType: true })
{
if (targetType.IsRefLikeType)
{
if (operandType is TypeParameterSymbol)
{
Debug.Assert(operandType is TypeParameterSymbol { AllowsRefLikeType: false });
return ConstantValue.False;
}
}
else if (operandType.IsRefLikeType)
{
if (targetType is TypeParameterSymbol)
{
Debug.Assert(targetType is TypeParameterSymbol { AllowsRefLikeType: false });
return ConstantValue.False;
}
}
}

return ConstantValue.Bad;
}

Expand Down Expand Up @@ -3926,6 +3953,11 @@ internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType

if (!isOperatorConstantResult.BooleanValue)
{
if (operandType?.IsRefLikeType == true)
{
return ConstantValue.Bad;
}

return ConstantValue.Null;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,7 @@ protected BoundLocalDeclaration BindVariableDeclaration(

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

if (localSymbol.Scope == ScopedKind.ScopedValue && !declTypeOpt.Type.IsErrorTypeOrRefLikeType())
if (localSymbol.Scope == ScopedKind.ScopedValue && !declTypeOpt.Type.IsErrorOrRefLikeOrAllowsRefLikeType())
{
localDiagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
Expand Down Expand Up @@ -1581,7 +1581,7 @@ private void ValidateAssignment(
leftEscape = GetValEscape(op1, _localScopeDepth);
rightEscape = GetValEscape(op2, _localScopeDepth);

Debug.Assert(leftEscape == rightEscape || op1.Type.IsRefLikeType);
Debug.Assert(leftEscape == rightEscape || op1.Type.IsRefLikeOrAllowsRefLikeType());

// We only check if the safe-to-escape of e2 is wider than the safe-to-escape of e1 here,
// we don't check for equality. The case where the safe-to-escape of e2 is narrower than
Expand All @@ -1600,7 +1600,7 @@ private void ValidateAssignment(
}
}

if (!hasErrors && op1.Type.IsRefLikeType)
if (!hasErrors && op1.Type.IsRefLikeOrAllowsRefLikeType())
{
var leftEscape = GetValEscape(op1, _localScopeDepth);
ValidateEscape(op2, leftEscape, isByRef: false, diagnostics);
Expand Down
Loading

0 comments on commit 07d428b

Please sign in to comment.