Skip to content

Commit

Permalink
Scoped implicitly-typed lambda parameters (#64680)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Oct 17, 2022
1 parent bf23384 commit 5bf1a43
Show file tree
Hide file tree
Showing 8 changed files with 410 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ Symbol remapLambda(BoundLambda boundLambda, LambdaSymbol lambda)
if (updatedDelegateType is null)
{
Debug.Assert(updatedContaining is object);
updatedLambda = boundLambda.CreateLambdaSymbol(updatedContaining, lambda.ReturnTypeWithAnnotations, lambda.ParameterTypesWithAnnotations, lambda.ParameterRefKinds, lambda.RefKind);
var parameterEffectiveScopes = lambda.GetParameterEffectiveScopes();
updatedLambda = boundLambda.CreateLambdaSymbol(updatedContaining, lambda.ReturnTypeWithAnnotations, lambda.ParameterTypesWithAnnotations, lambda.ParameterRefKinds, parameterEffectiveScopes, lambda.RefKind);
}
else
{
Expand Down
77 changes: 53 additions & 24 deletions src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,5 +225,17 @@ internal static bool IsValidUnscopedRefAttributeTarget(this MethodSymbol method)
!method.IsConstructor() &&
!method.IsInitOnly;
}

#nullable enable
internal static ImmutableArray<DeclarationScope> GetParameterEffectiveScopes(this MethodSymbol? method)
{
if (method is null)
return default;

if (method.Parameters.All(p => p.EffectiveScope == DeclarationScope.Unscoped))
return default;

return method.Parameters.SelectAsArray(p => p.EffectiveScope);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

Expand All @@ -11,25 +12,31 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
internal sealed class LambdaParameterSymbol : SourceComplexParameterSymbolBase
{
private readonly SyntaxList<AttributeListSyntax> _attributeLists;
private readonly DeclarationScope? _effectiveScope;

public LambdaParameterSymbol(
LambdaSymbol owner,
SyntaxList<AttributeListSyntax> attributeLists,
TypeWithAnnotations parameterType,
int ordinal,
RefKind refKind,
DeclarationScope scope,
DeclarationScope? declaredScope,
DeclarationScope? effectiveScope,
string name,
bool isDiscard,
ImmutableArray<Location> locations)
: base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef: null, isParams: false, isExtensionMethodThis: false, scope)
: base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef: null, isParams: false, isExtensionMethodThis: false, scope: declaredScope)
{
Debug.Assert(declaredScope.HasValue != effectiveScope.HasValue);
_attributeLists = attributeLists;
_effectiveScope = effectiveScope;
IsDiscard = isDiscard;
}

public override bool IsDiscard { get; }

internal override DeclarationScope EffectiveScope => _effectiveScope ?? base.EffectiveScope;

internal override bool IsMetadataOptional
{
get { return false; }
Expand Down
36 changes: 24 additions & 12 deletions src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
Expand Down Expand Up @@ -42,12 +43,14 @@ public LambdaSymbol(
UnboundLambda unboundLambda,
ImmutableArray<TypeWithAnnotations> parameterTypes,
ImmutableArray<RefKind> parameterRefKinds,
ImmutableArray<DeclarationScope> parameterEffectiveScopes,
RefKind refKind,
TypeWithAnnotations returnType) :
base(unboundLambda.Syntax.GetReference())
{
Debug.Assert(syntaxReferenceOpt is not null);
Debug.Assert(containingSymbol.DeclaringCompilation == compilation);
Debug.Assert(parameterEffectiveScopes.IsDefault || parameterTypes.Length == parameterEffectiveScopes.Length);

_binder = binder;
_containingSymbol = containingSymbol;
Expand All @@ -62,7 +65,7 @@ public LambdaSymbol(
_isAsync = unboundLambda.IsAsync;
_isStatic = unboundLambda.IsStatic;
// No point in making this lazy. We are always going to need these soon after creation of the symbol.
_parameters = MakeParameters(compilation, unboundLambda, parameterTypes, parameterRefKinds);
_parameters = MakeParameters(compilation, unboundLambda, parameterTypes, parameterRefKinds, parameterEffectiveScopes);
_declarationDiagnostics = new BindingDiagnosticBag();
}

Expand Down Expand Up @@ -304,9 +307,11 @@ private ImmutableArray<ParameterSymbol> MakeParameters(
CSharpCompilation compilation,
UnboundLambda unboundLambda,
ImmutableArray<TypeWithAnnotations> parameterTypes,
ImmutableArray<RefKind> parameterRefKinds)
ImmutableArray<RefKind> parameterRefKinds,
ImmutableArray<DeclarationScope> parameterEffectiveScopes)
{
Debug.Assert(parameterTypes.Length == parameterRefKinds.Length);
Debug.Assert(parameterEffectiveScopes.IsDefault || parameterTypes.Length == parameterEffectiveScopes.Length);

if (!unboundLambda.HasSignature || unboundLambda.ParameterCount == 0)
{
Expand All @@ -317,8 +322,9 @@ private ImmutableArray<ParameterSymbol> MakeParameters(
type,
ordinal,
arg.refKinds[ordinal],
GeneratedNames.LambdaCopyParameterName(ordinal)), // Make sure nothing binds to this.
(owner: this, refKinds: parameterRefKinds));
GeneratedNames.LambdaCopyParameterName(ordinal), // Make sure nothing binds to this.
getScope(arg.parameterEffectiveScopes, ordinal)),
(owner: this, refKinds: parameterRefKinds, parameterEffectiveScopes));
}

var builder = ArrayBuilder<ParameterSymbol>.GetInstance(unboundLambda.ParameterCount);
Expand All @@ -335,40 +341,46 @@ private ImmutableArray<ParameterSymbol> MakeParameters(

TypeWithAnnotations type;
RefKind refKind;
DeclarationScope scope;
DeclarationScope? declaredScope;
DeclarationScope? effectiveScope;
if (hasExplicitlyTypedParameterList)
{
type = unboundLambda.ParameterTypeWithAnnotations(p);
refKind = unboundLambda.RefKind(p);
scope = unboundLambda.DeclaredScope(p);
declaredScope = unboundLambda.DeclaredScope(p);
effectiveScope = null;
}
else if (p < numDelegateParameters)
{
type = parameterTypes[p];
refKind = parameterRefKinds[p];
// https://github.com/dotnet/roslyn/issues/62080: DeclarationScope should be taken from delegate signature.
// We probably should propagate effective scope from the target delegate and make sure parameter symbol doesn't adjust it in any way.
scope = DeclarationScope.Unscoped;
declaredScope = null;
effectiveScope = getScope(parameterEffectiveScopes, p);
}
else
{
type = TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(compilation, name: string.Empty, arity: 0, errorInfo: null));
refKind = RefKind.None;
scope = DeclarationScope.Unscoped;
declaredScope = DeclarationScope.Unscoped;
effectiveScope = null;
}

var attributeLists = unboundLambda.ParameterAttributes(p);
var name = unboundLambda.ParameterName(p);
var location = unboundLambda.ParameterLocation(p);
var locations = location == null ? ImmutableArray<Location>.Empty : ImmutableArray.Create<Location>(location);

var parameter = new LambdaParameterSymbol(owner: this, attributeLists, type, ordinal: p, refKind, scope, name, unboundLambda.ParameterIsDiscard(p), locations);
var parameter = new LambdaParameterSymbol(owner: this, attributeLists, type, ordinal: p, refKind, declaredScope, effectiveScope, name, unboundLambda.ParameterIsDiscard(p), locations);
builder.Add(parameter);
}

var result = builder.ToImmutableAndFree();

return result;

static DeclarationScope getScope(ImmutableArray<DeclarationScope> scopes, int ordinal)
{
return scopes.IsDefault ? DeclarationScope.Unscoped : scopes[ordinal];
}
}

public sealed override bool Equals(Symbol symbol, TypeCompareKind compareKind)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected SourceComplexParameterSymbolBase(
SyntaxReference syntaxRef,
bool isParams,
bool isExtensionMethodThis,
DeclarationScope scope)
DeclarationScope? scope)
: base(owner, parameterType, ordinal, refKind, scope, name, locations)
{
Debug.Assert((syntaxRef == null) || (syntaxRef.GetSyntax().IsKind(SyntaxKind.Parameter)));
Expand Down Expand Up @@ -198,7 +198,7 @@ internal bool HasEnumeratorCancellationAttribute

#nullable enable

internal sealed override DeclarationScope EffectiveScope
internal override DeclarationScope EffectiveScope
{
get
{
Expand Down Expand Up @@ -1503,7 +1503,7 @@ internal SourceComplexParameterSymbol(
SyntaxReference syntaxRef,
bool isParams,
bool isExtensionMethodThis,
DeclarationScope scope)
DeclarationScope? scope)
: base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef, isParams, isExtensionMethodThis, scope)
{
}
Expand All @@ -1526,7 +1526,7 @@ internal SourceComplexParameterSymbolWithCustomModifiersPrecedingRef(
SyntaxReference syntaxRef,
bool isParams,
bool isExtensionMethodThis,
DeclarationScope scope)
DeclarationScope? scope)
: base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef, isParams, isExtensionMethodThis, scope)
{
Debug.Assert(!refCustomModifiers.IsEmpty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal abstract class SourceParameterSymbol : SourceParameterSymbolBase
private readonly string _name;
private readonly ImmutableArray<Location> _locations;
private readonly RefKind _refKind;
private readonly DeclarationScope _scope;
private readonly DeclarationScope? _scope;

public static SourceParameterSymbol Create(
Binder context,
Expand Down Expand Up @@ -101,7 +101,7 @@ protected SourceParameterSymbol(
TypeWithAnnotations parameterType,
int ordinal,
RefKind refKind,
DeclarationScope scope,
DeclarationScope? scope,
string name,
ImmutableArray<Location> locations)
: base(owner, ordinal)
Expand Down Expand Up @@ -225,13 +225,16 @@ public sealed override RefKind RefKind
/// <summary>
/// The declared scope. From source, this is from the <c>scope</c> keyword only.
/// </summary>
internal DeclarationScope DeclaredScope => _scope;
internal DeclarationScope? DeclaredScope => _scope;

internal abstract override DeclarationScope EffectiveScope { get; }

protected DeclarationScope CalculateEffectiveScopeIgnoringAttributes()
{
var declaredScope = this.DeclaredScope;
// DeclaredScope is only null in LambdaParameterSymbol where EffectiveScope can be provided rather than computed
Debug.Assert(this.DeclaredScope is not null);
var declaredScope = this.DeclaredScope.Value;

return declaredScope == DeclarationScope.Unscoped && ParameterHelpers.IsRefScopedByDefault(this) ?
DeclarationScope.RefScoped :
declaredScope;
Expand Down
Loading

0 comments on commit 5bf1a43

Please sign in to comment.