Skip to content
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
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 @@ -7367,4 +7367,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 @@ -1819,6 +1819,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 @@ -2218,6 +2219,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