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

Support 'scoped' modifier for parameters and locals #61389

Merged
merged 33 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0055014
Support 'scoped' modifier for parameters and locals
cston May 18, 2022
405ce6a
Misc.
cston May 18, 2022
c67d771
Missing ConvertToKeyword()
cston May 20, 2022
35672d2
Bind scope for lambda parameters
cston May 20, 2022
88de445
PR feedback
cston May 21, 2022
322a61c
Update parsing
cston May 23, 2022
0d48f5a
Report error if scoped value is not ref struct
cston May 23, 2022
413b85b
Fix tests
cston May 25, 2022
6406e1c
More tests
cston May 25, 2022
f24747d
Add IsRefScoped and IsValueScoped to public API
cston May 25, 2022
290be82
Fix build
cston May 26, 2022
48a7a21
Parse 'scoped' as modifier regardless of -langversion
cston May 26, 2022
2346121
Fix tests
cston May 26, 2022
ba42c34
Update DeclarationScope enum
cston May 26, 2022
47c6f90
Additional tests
cston May 26, 2022
dbcd9ff
Misc.
cston May 26, 2022
5a376f2
PR feedback
cston May 29, 2022
12e5e65
Update tests
cston May 29, 2022
dd2b6a7
More tests
cston May 30, 2022
4ff6d0f
PR feedback
cston Jun 1, 2022
8f2cc65
Treat method as not supported if parameter has unexpected LifetimeAnn…
cston Jun 1, 2022
88f0053
Update SymbolDisplay
cston Jun 1, 2022
7d31323
PR feedback
cston Jun 2, 2022
3432100
Disallow scoped in function pointer signatures
cston Jun 2, 2022
1888ac7
PR feedback
cston Jun 2, 2022
6028395
Filter out attribute from PEParameterSymbol.GetAttributes()
cston Jun 2, 2022
d90e587
Revert some public API changes
cston Jun 2, 2022
452d6a5
Formatting
cston Jun 2, 2022
7d82030
Fix tests
cston Jun 3, 2022
65fb9fd
Revert public API changes
cston Jun 3, 2022
436677a
Rename helper method
cston Jun 3, 2022
5a3815a
Fix ScanExplicitlyTypedLambda()
cston Jun 3, 2022
3c5d140
Filter out attribute unconditionally
cston Jun 3, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnno
public override bool IsAsync { get { return false; } }
public override bool IsStatic => false;
public override RefKind RefKind(int index) { return Microsoft.CodeAnalysis.RefKind.None; }
public override DeclarationScope Scope(int index) => DeclarationScope.Unscoped;
public override MessageID MessageID { get { return MessageID.IDS_FeatureQueryExpression; } } // TODO: what is the correct ID here?
public override Location ParameterLocation(int index) { return _parameters[index].Locations[0]; }
public override TypeWithAnnotations ParameterTypeWithAnnotations(int index) { throw new ArgumentException(); } // implicitly typed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2814,6 +2814,7 @@ private BoundExpression BindOutVariableDeclarationArgument(
CheckFeatureAvailability(declarationExpression, MessageID.IDS_FeatureExpressionVariablesInQueriesAndInitializers, diagnostics);
}

// PROTOTYPE: Test with 'out scoped R' and 'out scoped var', with -langversion:10 and -langversion:11.
bool isConst = false;
AliasSymbol alias;
var declType = BindVariableTypeWithAnnotations(declarationExpression, diagnostics, typeSyntax, ref isConst, out isVar, out alias);
Expand Down Expand Up @@ -7105,7 +7106,7 @@ protected BoundExpression BindFieldAccess(
// If this is a ref field from another compilation, check for support for ref fields.
// No need to check for a reference to a field declared in this compilation since
// we check at the declaration site. (Check RefKind after checking compilation to
// avoid cycles for source symbols.
// avoid cycles for source symbols.)
if ((object)Compilation.SourceModule != fieldSymbol.OriginalDefinition.ContainingModule &&
fieldSymbol.RefKind != RefKind.None)
{
Expand Down
53 changes: 44 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

ImmutableArray<string> names = default;
ImmutableArray<RefKind> refKinds = default;
ImmutableArray<DeclarationScope> scopes = default;
ImmutableArray<bool> nullCheckedOpt = default;
ImmutableArray<TypeWithAnnotations> types = default;
RefKind returnRefKind = RefKind.None;
Expand Down Expand Up @@ -99,10 +100,10 @@ private UnboundLambda AnalyzeAnonymousFunction(
if (parameterSyntaxList != null)
{
var hasExplicitlyTypedParameterList = true;
var allValue = true;

var typesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
var refKindsBuilder = ArrayBuilder<RefKind>.GetInstance();
var scopesBuilder = ArrayBuilder<DeclarationScope>.GetInstance();
var nullCheckedBuilder = ArrayBuilder<bool>.GetInstance();
var attributesBuilder = ArrayBuilder<SyntaxList<AttributeListSyntax>>.GetInstance();

Expand Down Expand Up @@ -138,31 +139,31 @@ private UnboundLambda AnalyzeAnonymousFunction(
var typeSyntax = p.Type;
TypeWithAnnotations type = default;
var refKind = RefKind.None;
var scope = DeclarationScope.Unscoped;

if (typeSyntax == null)
{
hasExplicitlyTypedParameterList = false;
}
else
{
bool scopedBeforeRef = false;
bool scopedAfterRef = false;
type = BindType(typeSyntax, diagnostics);
foreach (var modifier in p.Modifiers)
{
switch (modifier.Kind())
{
case SyntaxKind.RefKeyword:
refKind = RefKind.Ref;
allValue = false;
break;

case SyntaxKind.OutKeyword:
refKind = RefKind.Out;
allValue = false;
break;

case SyntaxKind.InKeyword:
refKind = RefKind.In;
allValue = false;
break;

case SyntaxKind.ParamsKeyword:
Expand All @@ -175,13 +176,34 @@ private UnboundLambda AnalyzeAnonymousFunction(
case SyntaxKind.ThisKeyword:
Error(diagnostics, ErrorCode.ERR_ThisInBadContext, modifier);
break;

case SyntaxKind.ScopedKeyword:
ModifierUtils.CheckScopedModifierAvailability(p, modifier, diagnostics);
if (refKind == RefKind.None)
{
scopedBeforeRef = true;
}
else
{
scopedAfterRef = true;
}
break;
}
}
if (scopedAfterRef)
{
scope = DeclarationScope.ValueScoped;
}
else if (scopedBeforeRef)
{
scope = (refKind == RefKind.None) ? DeclarationScope.ValueScoped : DeclarationScope.RefScoped;
}
}

namesBuilder.Add(p.Identifier.ValueText);
typesBuilder.Add(type);
refKindsBuilder.Add(refKind);
scopesBuilder.Add(scope);
nullCheckedBuilder.Add(isNullChecked(p));
attributesBuilder.Add(syntax.Kind() == SyntaxKind.ParenthesizedLambdaExpression ? p.AttributeLists : default);
}
Expand All @@ -193,11 +215,16 @@ private UnboundLambda AnalyzeAnonymousFunction(
types = typesBuilder.ToImmutable();
}

if (!allValue)
if (refKindsBuilder.Any(r => r != RefKind.None))
{
refKinds = refKindsBuilder.ToImmutable();
}

if (scopesBuilder.Any(s => s != DeclarationScope.Unscoped))
{
scopes = scopesBuilder.ToImmutable();
}

if (nullCheckedBuilder.Contains(true))
{
nullCheckedOpt = nullCheckedBuilder.ToImmutable();
Expand All @@ -209,6 +236,7 @@ private UnboundLambda AnalyzeAnonymousFunction(
}

typesBuilder.Free();
scopesBuilder.Free();
refKindsBuilder.Free();
nullCheckedBuilder.Free();
attributesBuilder.Free();
Expand All @@ -221,7 +249,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

namesBuilder.Free();

return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, types, names, discardsOpt, nullCheckedOpt, isAsync, isStatic);
return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, scopes, types, names, discardsOpt, nullCheckedOpt, isAsync, isStatic);

static bool isNullChecked(ParameterSyntax parameter)
=> parameter.ExclamationExclamationToken.IsKind(SyntaxKind.ExclamationExclamationToken);
Expand Down Expand Up @@ -322,10 +350,17 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy
for (int i = 0; i < lambda.ParameterCount; i++)
{
// UNDONE: Where do we report improper use of pointer types?
var type = lambda.Data.ParameterTypeWithAnnotations(i);
if (type.HasType && type.IsStatic)
var type = data.ParameterTypeWithAnnotations(i).Type;
if (type is { })
{
Error(diagnostics, ErrorFacts.GetStaticClassParameterCode(useWarning: false), syntax, type.Type);
if (type.IsStatic)
{
Error(diagnostics, ErrorFacts.GetStaticClassParameterCode(useWarning: false), syntax, type);
}
if (data.Scope(i) == DeclarationScope.ValueScoped && !type.IsErrorTypeOrRefLikeType())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, data.ParameterLocation(i));
}
}
}
}
Expand Down
26 changes: 24 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,22 @@ private BoundStatement BindDeclarationStatementParts(LocalDeclarationStatementSy
var typeSyntax = node.Declaration.Type.SkipRef(out _);
bool isConst = node.IsConst;

foreach (var modifier in node.Modifiers)
{
// Check for support for 'scoped'. Duplicate modifiers are reported
// as errors in parsing rather than here.
if (modifier.Kind() == SyntaxKind.ScopedKeyword)
{
ModifierUtils.CheckScopedModifierAvailability(node, modifier, diagnostics);
}
}

if (node.Declaration.Type is RefTypeSyntax { ScopedKeyword: var scopedKeyword } &&
scopedKeyword.Kind() == SyntaxKind.ScopedKeyword)
{
ModifierUtils.CheckScopedModifierAvailability(typeSyntax, scopedKeyword, diagnostics);
}

bool isVar;
AliasSymbol alias;
TypeWithAnnotations declType = BindVariableTypeWithAnnotations(node.Declaration, diagnostics, typeSyntax, ref isConst, isVar: out isVar, alias: out alias);
Expand Down Expand Up @@ -1078,6 +1094,11 @@ protected BoundLocalDeclaration BindVariableDeclaration(
hasErrors = true;
}

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

localSymbol.SetTypeWithAnnotations(declTypeOpt);

if (initializerOpt != null)
Expand Down Expand Up @@ -1203,11 +1224,12 @@ private SourceLocalSymbol LocateDeclaredVariableSymbol(SyntaxToken identifier, T
localSymbol = SourceLocalSymbol.MakeLocal(
ContainingMemberOrLambda,
this,
false, // do not allow ref
allowRefKind: false, // do not allow ref
typeSyntax,
identifier,
kind,
equalsValue);
equalsValue,
hasScopedModifier: false);
}

return localSymbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected override ImmutableArray<LocalSymbol> BuildLocals()
var declarationOpt = _syntax.Declaration;
if ((declarationOpt != null) && (declarationOpt.Identifier.Kind() != SyntaxKind.None))
{
locals.Add(SourceLocalSymbol.MakeLocal(this.ContainingMemberOrLambda, this, false, declarationOpt.Type, declarationOpt.Identifier, LocalDeclarationKind.CatchVariable));
locals.Add(SourceLocalSymbol.MakeLocal(this.ContainingMemberOrLambda, this, allowRefKind: false, declarationOpt.Type, declarationOpt.Identifier, LocalDeclarationKind.CatchVariable, initializer: null, hasScopedModifier: false));
}

if (_syntax.Filter != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected override ImmutableArray<LocalSymbol> BuildLocals()

foreach (VariableDeclaratorSyntax declarator in _syntax.Declaration.Variables)
{
locals.Add(MakeLocal(_syntax.Declaration, declarator, LocalDeclarationKind.FixedVariable));
locals.Add(MakeLocal(_syntax.Declaration, declarator, LocalDeclarationKind.FixedVariable, hasScopedModifier: false)); // PROTOTYPE: Handle 'scoped' modifier.

// also gather expression-declared variables from the bracketed argument lists and the initializers
ExpressionVariableFinder.FindExpressionVariables(this, locals, declarator);
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/ForLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected override ImmutableArray<LocalSymbol> BuildLocals()

foreach (var vdecl in _syntax.Declaration.Variables)
{
var localSymbol = MakeLocal(_syntax.Declaration, vdecl, LocalDeclarationKind.RegularVariable);
var localSymbol = MakeLocal(_syntax.Declaration, vdecl, LocalDeclarationKind.RegularVariable, hasScopedModifier: false); // PROTOTYPE: Handle 'scoped' modifier.
locals.Add(localSymbol);

// also gather expression-declared variables from the bracketed argument lists and the initializers
Expand Down
6 changes: 4 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,10 @@ internal void BuildLocals(Binder enclosingBinder, StatementSyntax statement, Arr
{
kind = LocalDeclarationKind.RegularVariable;
}
bool hasScopedModifier = decl.Modifiers.Any(SyntaxKind.ScopedKeyword);
foreach (var vdecl in decl.Declaration.Variables)
{
var localSymbol = MakeLocal(decl.Declaration, vdecl, kind, localDeclarationBinder);
var localSymbol = MakeLocal(decl.Declaration, vdecl, kind, hasScopedModifier, localDeclarationBinder);
locals.Add(localSymbol);

// also gather expression-declared variables from the bracketed argument lists and the initializers
Expand Down Expand Up @@ -313,7 +314,7 @@ internal void BuildLocalFunctions(StatementSyntax statement, ref ArrayBuilder<Lo
}
}

protected SourceLocalSymbol MakeLocal(VariableDeclarationSyntax declaration, VariableDeclaratorSyntax declarator, LocalDeclarationKind kind, Binder initializerBinderOpt = null)
protected SourceLocalSymbol MakeLocal(VariableDeclarationSyntax declaration, VariableDeclaratorSyntax declarator, LocalDeclarationKind kind, bool hasScopedModifier, Binder initializerBinderOpt = null)
{
return SourceLocalSymbol.MakeLocal(
this.ContainingMemberOrLambda,
Expand All @@ -323,6 +324,7 @@ protected SourceLocalSymbol MakeLocal(VariableDeclarationSyntax declaration, Var
declarator.Identifier,
kind,
declarator.Initializer,
hasScopedModifier,
initializerBinderOpt);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected override ImmutableArray<LocalSymbol> BuildLocals()

foreach (VariableDeclaratorSyntax declarator in declarationSyntax.Variables)
{
locals.Add(MakeLocal(declarationSyntax, declarator, LocalDeclarationKind.UsingVariable));
locals.Add(MakeLocal(declarationSyntax, declarator, LocalDeclarationKind.UsingVariable, hasScopedModifier: false)); // PROTOTYPE: Handle 'scoped' modifier.

// also gather expression-declared variables from the bracketed argument lists and the initializers
ExpressionVariableFinder.FindExpressionVariables(this, locals, declarator);
Expand Down
Loading