Skip to content

Commit

Permalink
Support 'scoped' modifier for parameters and locals (#61389)
Browse files Browse the repository at this point in the history
  • Loading branch information
cston authored Jun 3, 2022
1 parent 6b47535 commit 75ac25c
Show file tree
Hide file tree
Showing 111 changed files with 6,174 additions and 336 deletions.
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
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
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
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/CatchClauseBinder.cs
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

0 comments on commit 75ac25c

Please sign in to comment.