Skip to content

Commit

Permalink
Support scoped keyword in declaration expressions. (#64380)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseyTs committed Oct 13, 2022
1 parent c44c7e8 commit f020a37
Show file tree
Hide file tree
Showing 30 changed files with 7,469 additions and 164 deletions.
109 changes: 84 additions & 25 deletions src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,10 @@ private void FailRemainingInferencesAndSetValEscape(ArrayBuilder<DeconstructionV
{
case BoundKind.Local:
var local = (BoundLocal)variable.Single;
if (local.DeclarationKind != BoundLocalDeclarationKind.None)
if (local.DeclarationKind != BoundLocalDeclarationKind.None &&
local.LocalSymbol is SourceLocalSymbol { ValEscapeScope: CallingMethodScope } localSymbol)
{
((SourceLocalSymbol)local.LocalSymbol).SetValEscape(rhsValEscape);
localSymbol.SetValEscape(rhsValEscape);
}
break;
case BoundKind.DeconstructionVariablePendingInference:
Expand Down Expand Up @@ -760,7 +761,7 @@ private DeconstructionVariable BindDeconstructionVariables(
bool isVar;
bool isConst = false;
AliasSymbol alias;
var declType = BindVariableTypeWithAnnotations(component.Designation, diagnostics, component.Type, ref isConst, out isVar, out alias);
var declType = BindVariableTypeWithAnnotations(component.Designation, diagnostics, component.Type.SkipScoped(out _).SkipRef(out _), ref isConst, out isVar, out alias);
Debug.Assert(isVar == !declType.HasType);
if (component.Designation.Kind() == SyntaxKind.ParenthesizedVariableDesignation && !isVar)
{
Expand Down Expand Up @@ -814,6 +815,23 @@ private DeconstructionVariable BindDeconstructionVariables(
case SyntaxKind.DiscardDesignation:
{
var discarded = (DiscardDesignationSyntax)node;

if (discarded.Parent is DeclarationExpressionSyntax declExpr && declExpr.Designation == discarded)
{
TypeSyntax typeSyntax = declExpr.Type;

if (typeSyntax is ScopedTypeSyntax scopedType)
{
diagnostics.Add(ErrorCode.ERR_ScopedDiscard, scopedType.ScopedKeyword.GetLocation());
typeSyntax = scopedType.Type;
}

if (typeSyntax is RefTypeSyntax refType)
{
diagnostics.Add(ErrorCode.ERR_DeconstructVariableCannotBeByRef, refType.RefKeyword.GetLocation());
}
}

return new DeconstructionVariable(BindDiscardExpression(syntax, declTypeWithAnnotations), syntax);
}
case SyntaxKind.ParenthesizedVariableDesignation:
Expand Down Expand Up @@ -855,6 +873,29 @@ private BoundExpression BindDeconstructionVariable(
// is this a local?
if ((object)localSymbol != null)
{
if (designation.Parent is DeclarationExpressionSyntax declExpr && declExpr.Designation == designation)
{
TypeSyntax typeSyntax = declExpr.Type;

if (typeSyntax is ScopedTypeSyntax scopedType)
{
// Check for support for 'scoped'.
ModifierUtils.CheckScopedModifierAvailability(typeSyntax, scopedType.ScopedKeyword, diagnostics);
typeSyntax = scopedType.Type;
}

if (typeSyntax is RefTypeSyntax refType)
{
diagnostics.Add(ErrorCode.ERR_DeconstructVariableCannotBeByRef, refType.RefKeyword.GetLocation());
}

if (declTypeWithAnnotations.HasType &&
localSymbol.Scope == DeclarationScope.ValueScoped && !declTypeWithAnnotations.Type.IsErrorTypeOrRefLikeType())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}
}

// Check for variable declaration errors.
// Use the binder that owns the scope for the local because this (the current) binder
// might own nested scope.
Expand All @@ -867,33 +908,51 @@ private BoundExpression BindDeconstructionVariable(

return new DeconstructionVariablePendingInference(syntax, localSymbol, receiverOpt: null);
}
else
{
// Is this a field?
GlobalExpressionVariable field = LookupDeclaredField(designation);

// Is this a field?
GlobalExpressionVariable field = LookupDeclaredField(designation);
if ((object)field == null)
{
// We should have the right binder in the chain, cannot continue otherwise.
throw ExceptionUtilities.Unreachable();
}

if ((object)field == null)
{
// We should have the right binder in the chain, cannot continue otherwise.
throw ExceptionUtilities.Unreachable();
}
if (designation.Parent is DeclarationExpressionSyntax declExpr && declExpr.Designation == designation)
{
TypeSyntax typeSyntax = declExpr.Type;

BoundThisReference receiver = ThisReference(designation, this.ContainingType, hasErrors: false,
wasCompilerGenerated: true);
if (typeSyntax is ScopedTypeSyntax scopedType)
{
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, scopedType.ScopedKeyword.GetLocation(), scopedType.ScopedKeyword.ValueText);
typeSyntax = scopedType.Type;
}

if (declTypeWithAnnotations.HasType)
{
var fieldType = field.GetFieldType(this.FieldsBeingBound);
Debug.Assert(TypeSymbol.Equals(declTypeWithAnnotations.Type, fieldType.Type, TypeCompareKind.ConsiderEverything2));
return new BoundFieldAccess(syntax,
receiver,
field,
constantValueOpt: null,
resultKind: LookupResultKind.Viable,
isDeclaration: true,
type: fieldType.Type);
}
if (typeSyntax is RefTypeSyntax refType)
{
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, refType.RefKeyword.GetLocation(), refType.RefKeyword.ValueText);
}
}

BoundThisReference receiver = ThisReference(designation, this.ContainingType, hasErrors: false,
wasCompilerGenerated: true);

return new DeconstructionVariablePendingInference(syntax, field, receiver);
if (declTypeWithAnnotations.HasType)
{
var fieldType = field.GetFieldType(this.FieldsBeingBound);
Debug.Assert(TypeSymbol.Equals(declTypeWithAnnotations.Type, fieldType.Type, TypeCompareKind.ConsiderEverything2));
return new BoundFieldAccess(syntax,
receiver,
field,
constantValueOpt: null,
resultKind: LookupResultKind.Viable,
isDeclaration: true,
type: fieldType.Type);
}

return new DeconstructionVariablePendingInference(syntax, field, receiver);
}
}
}
}
95 changes: 67 additions & 28 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ private BoundExpression BindDeclarationExpressionAsError(DeclarationExpressionSy
bool isVar;
bool isConst = false;
AliasSymbol alias;
var declType = BindVariableTypeWithAnnotations(node.Designation, diagnostics, node.Type, ref isConst, out isVar, out alias);
var declType = BindVariableTypeWithAnnotations(node.Designation, diagnostics, node.Type.SkipScoped(out _).SkipRef(out _), ref isConst, out isVar, out alias);
Error(diagnostics, ErrorCode.ERR_DeclarationExpressionNotPermitted, node);
return BindDeclarationVariablesForErrorRecovery(declType, node.Designation, node, diagnostics);
}
Expand Down Expand Up @@ -2792,16 +2792,23 @@ private BoundExpression BindOutDeclarationArgument(DeclarationExpressionSyntax d
TypeSyntax typeSyntax = declarationExpression.Type;
VariableDesignationSyntax designation = declarationExpression.Designation;

Debug.Assert(typeSyntax is not ScopedTypeSyntax);
if (typeSyntax.SkipScoped(out _).GetRefKind() != RefKind.None)
{
diagnostics.Add(ErrorCode.ERR_OutVariableCannotBeByRef, declarationExpression.Type.Location);
}

switch (designation.Kind())
{
case SyntaxKind.DiscardDesignation:
{
if (typeSyntax is ScopedTypeSyntax scopedType)
{
diagnostics.Add(ErrorCode.ERR_ScopedDiscard, scopedType.ScopedKeyword.GetLocation());
typeSyntax = scopedType.Type;
}

if (typeSyntax is RefTypeSyntax refType)
{
diagnostics.Add(ErrorCode.ERR_OutVariableCannotBeByRef, refType.Location);
typeSyntax = refType.Type;
}

bool isVar;
bool isConst = false;
AliasSymbol alias;
Expand Down Expand Up @@ -2831,6 +2838,19 @@ private BoundExpression BindOutVariableDeclarationArgument(
SourceLocalSymbol localSymbol = this.LookupLocal(designation.Identifier);
if ((object)localSymbol != null)
{
if (typeSyntax is ScopedTypeSyntax scopedType)
{
// Check for support for 'scoped'.
ModifierUtils.CheckScopedModifierAvailability(typeSyntax, scopedType.ScopedKeyword, diagnostics);
typeSyntax = scopedType.Type;
}

if (typeSyntax is RefTypeSyntax refType)
{
diagnostics.Add(ErrorCode.ERR_OutVariableCannotBeByRef, refType.Location);
typeSyntax = refType.Type;
}

Debug.Assert(localSymbol.DeclarationKind == LocalDeclarationKind.OutVariable);
if ((InConstructorInitializer || InFieldInitializer) && ContainingMemberOrLambda.ContainingSymbol.Kind == SymbolKind.NamedType)
{
Expand All @@ -2850,38 +2870,57 @@ private BoundExpression BindOutVariableDeclarationArgument(

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

if (localSymbol.Scope == DeclarationScope.ValueScoped && !declType.Type.IsErrorTypeOrRefLikeType())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, typeSyntax.Location);
}

return new BoundLocal(declarationExpression, localSymbol, BoundLocalDeclarationKind.WithExplicitType, constantValueOpt: null, isNullableUnknown: false, type: declType.Type);
}
else
{
// Is this a field?
GlobalExpressionVariable expressionVariableField = LookupDeclaredField(designation);

// Is this a field?
GlobalExpressionVariable expressionVariableField = LookupDeclaredField(designation);
if ((object)expressionVariableField == null)
{
// We should have the right binder in the chain, cannot continue otherwise.
throw ExceptionUtilities.Unreachable();
}

if ((object)expressionVariableField == null)
{
// We should have the right binder in the chain, cannot continue otherwise.
throw ExceptionUtilities.Unreachable();
}
BoundExpression receiver = SynthesizeReceiver(designation, expressionVariableField, diagnostics);

BoundExpression receiver = SynthesizeReceiver(designation, expressionVariableField, diagnostics);
if (typeSyntax is ScopedTypeSyntax scopedType)
{
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, scopedType.ScopedKeyword.GetLocation(), scopedType.ScopedKeyword.ValueText);
typeSyntax = scopedType.Type;
}

if (typeSyntax.IsVar)
{
BindTypeOrAliasOrVarKeyword(typeSyntax, BindingDiagnosticBag.Discarded, out isVar);
if (typeSyntax is RefTypeSyntax refType)
{
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, refType.RefKeyword.GetLocation(), refType.RefKeyword.ValueText);
typeSyntax = refType.Type;
}

if (isVar)
if (typeSyntax.IsVar)
{
return new OutVariablePendingInference(declarationExpression, expressionVariableField, receiver);
BindTypeOrAliasOrVarKeyword(typeSyntax, BindingDiagnosticBag.Discarded, out isVar);

if (isVar)
{
return new OutVariablePendingInference(declarationExpression, expressionVariableField, receiver);
}
}
}

TypeSymbol fieldType = expressionVariableField.GetFieldType(this.FieldsBeingBound).Type;
return new BoundFieldAccess(declarationExpression,
receiver,
expressionVariableField,
null,
LookupResultKind.Viable,
isDeclaration: true,
type: fieldType);
TypeSymbol fieldType = expressionVariableField.GetFieldType(this.FieldsBeingBound).Type;
return new BoundFieldAccess(declarationExpression,
receiver,
expressionVariableField,
null,
LookupResultKind.Viable,
isDeclaration: true,
type: fieldType);
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ internal BoundExpression SetInferredTypeWithAnnotations(TypeWithAnnotations type
this.Syntax;

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

if (localSymbol.Scope == DeclarationScope.ValueScoped && !type.Type.IsErrorTypeOrRefLikeType())
{
diagnosticsOpt.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly,
(typeOrDesignationSyntax is TypeSyntax typeSyntax ? typeSyntax.SkipScoped(out _).SkipRef(out _) : typeOrDesignationSyntax).Location);
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7376,4 +7376,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ReadOnlyNotSuppAsParamModDidYouMeanIn" xml:space="preserve">
<value>'readonly' is not supported as a parameter modifier. Did you mean 'in'?</value>
</data>
<data name="ERR_ScopedDiscard" xml:space="preserve">
<value>The 'scoped' modifier cannot be used with discard.</value>
</data>
<data name="ERR_DeconstructVariableCannotBeByRef" xml:space="preserve">
<value>A deconstruction variable cannot be declared as a ref local</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ private Symbol GetSemanticInfoSymbolInNonMemberContext(CSharpSyntaxNode node, bo
// However, we can return fieldSymbol.Type for implicitly typed field symbols in both cases.
// Note that for regular C#, fieldSymbol.Type would be an error type.

var variableDecl = type.Parent as VariableDeclarationSyntax;
var variableDecl = type.ModifyingScopedOrRefTypeOrSelf().Parent as VariableDeclarationSyntax;
if (variableDecl != null && variableDecl.Variables.Any())
{
var fieldSymbol = GetDeclaredFieldSymbol(variableDecl.Variables.First());
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2112,7 +2112,7 @@ internal enum ErrorCode
ERR_FeatureNotAvailableInVersion11 = 9058,
ERR_RefFieldInNonRefStruct = 9059,
ERR_CannotMatchOnINumberBase = 9060,
// Available 9061,
ERR_ScopedDiscard = 9061,
ERR_ScopedTypeNameDisallowed = 9062,
ERR_UnscopedRefAttributeUnsupportedTarget = 9063,
ERR_RuntimeDoesNotSupportRefFields = 9064,
Expand All @@ -2123,7 +2123,7 @@ internal enum ErrorCode
ERR_FilePathCannotBeConvertedToUtf8 = 9069,
ERR_ReadOnlyNotSuppAsParamModDidYouMeanIn = 9070,
ERR_FileLocalDuplicateNameInNS = 9071,
// Available 9072,
ERR_DeconstructVariableCannotBeByRef = 9072,
WRN_ScopedMismatchInParameterOfTarget = 9073,
WRN_ScopedMismatchInParameterOfOverrideOrImplementation = 9074,
ERR_RefReturnScopedParameter = 9075,
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code)
case ErrorCode.ERR_InvalidObjectCreation:
case ErrorCode.WRN_TypeParameterSameAsOuterMethodTypeParameter:
case ErrorCode.ERR_OutVariableCannotBeByRef:
case ErrorCode.ERR_DeconstructVariableCannotBeByRef:
case ErrorCode.ERR_OmittedTypeArgument:
case ErrorCode.ERR_FeatureNotAvailableInVersion8:
case ErrorCode.ERR_AltInterpolatedVerbatimStringsNotAvailable:
Expand Down Expand Up @@ -2219,6 +2220,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code)
case ErrorCode.ERR_BadAbstractEqualityOperatorSignature:
case ErrorCode.ERR_BadBinaryReadOnlySpanConcatenation:
case ErrorCode.ERR_ScopedRefAndRefStructOnly:
case ErrorCode.ERR_ScopedDiscard:
case ErrorCode.ERR_FixedFieldMustNotBeRef:
case ErrorCode.ERR_RefFieldCannotReferToRefStruct:
case ErrorCode.ERR_FileTypeDisallowedInSignature:
Expand Down
Loading

0 comments on commit f020a37

Please sign in to comment.