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

Implement lambda params with modifiers without type name #69273

Closed
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
59 changes: 28 additions & 31 deletions src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private UnboundLambda AnalyzeAnonymousFunction(
// x => ...
hasSignature = true;
var simple = (SimpleLambdaExpressionSyntax)syntax;
namesBuilder.Add(simple.Parameter.Identifier.ValueText);
parameterSyntaxList = SyntaxFactory.SingletonSeparatedList(simple.Parameter);
break;
case SyntaxKind.ParenthesizedLambdaExpression:
// (T x, U y) => ...
Expand Down Expand Up @@ -166,8 +166,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

var typeSyntax = p.Type;
TypeWithAnnotations type = default;
var refKind = RefKind.None;
var scope = ScopedKind.None;
var refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out var scope);

if (typeSyntax == null)
{
Expand All @@ -176,19 +175,20 @@ private UnboundLambda AnalyzeAnonymousFunction(
else
{
type = BindType(typeSyntax, diagnostics);
ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false,
parsingLambdaParams: !isAnonymousMethod,
parsingAnonymousMethodParams: isAnonymousMethod);
refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out scope);
}

var isLastParameter = parameterCount == parameterSyntaxList.Value.Count;
if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None && type.IsSZArray())
{
ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
paramsKeyword.GetLocation());
}
ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false,
parsingLambdaParams: !isAnonymousMethod,
parsingAnonymousMethodParams: isAnonymousMethod);

var isLastParameter = parameterCount == parameterSyntaxList.Value.Count;
if (isLastParameter && paramsKeyword.Kind() != SyntaxKind.None
&& !type.IsDefault && type.IsSZArray())
{
ReportUseSiteDiagnosticForSynthesizedAttribute(Compilation,
WellKnownMember.System_ParamArrayAttribute__ctor,
diagnostics,
paramsKeyword.GetLocation());
}

namesBuilder.Add(p.Identifier.ValueText);
Expand Down Expand Up @@ -349,26 +349,23 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy

var lambda = AnalyzeAnonymousFunction(syntax, diagnostics);
var data = lambda.Data;
if (data.HasExplicitlyTypedParameterList)

int firstDefault = -1;
for (int i = 0; i < lambda.ParameterCount; i++)
{
int firstDefault = -1;
for (int i = 0; i < lambda.ParameterCount; i++)
var paramSyntax = lambda.ParameterSyntax(i);
Debug.Assert(paramSyntax is not null);
if (paramSyntax.Default != null && firstDefault == -1)
{
// paramSyntax should not be null here; we should always be operating on an anonymous function which will have parameter information
var paramSyntax = lambda.ParameterSyntax(i);
Debug.Assert(paramSyntax is { });
if (paramSyntax.Default != null && firstDefault == -1)
{
firstDefault = i;
}
firstDefault = i;
}

ParameterHelpers.GetModifiers(paramSyntax.Modifiers, refnessKeyword: out _, out var paramsKeyword, thisKeyword: out _, scope: out _);
var isParams = paramsKeyword.Kind() != SyntaxKind.None;
ParameterHelpers.GetModifiers(paramSyntax.Modifiers, refnessKeyword: out _, out var paramsKeyword, thisKeyword: out _, scope: out _);
var isParams = paramsKeyword.Kind() != SyntaxKind.None;

// UNDONE: Where do we report improper use of pointer types?
ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i),
lambda.RefKind(i), lambda.DeclaredScope(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics);
}
// UNDONE: Where do we report improper use of pointer types?
ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i),
lambda.RefKind(i), lambda.DeclaredScope(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics);
}

// Parser will only have accepted static/async as allowed modifiers on this construct.
Expand Down
28 changes: 16 additions & 12 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2150,26 +2150,22 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag
Debug.Assert(anonymousFunction.ParameterCount == delegateParameters.Length);
for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
{
var lambdaParameterType = anonymousFunction.ParameterType(i);
if (lambdaParameterType.IsErrorType())
TypeSymbol? lambdaParameterType = null;
if (anonymousFunction.HasExplicitlyTypedParameterList)
{
continue;
lambdaParameterType = anonymousFunction.ParameterType(i);
if (lambdaParameterType.IsErrorType())
{
continue;
}
}

var lambdaParameterLocation = anonymousFunction.ParameterLocation(i);
var lambdaRefKind = anonymousFunction.RefKind(i);
var delegateParameterType = delegateParameters[i].Type;
var delegateRefKind = delegateParameters[i].RefKind;

if (!lambdaParameterType.Equals(delegateParameterType, TypeCompareKind.AllIgnoreOptions))
{
SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType);

// Parameter {0} is declared as type '{1}{2}' but should be '{3}{4}'
Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation,
i + 1, lambdaRefKind.ToParameterPrefix(), distinguisher.First, delegateRefKind.ToParameterPrefix(), distinguisher.Second);
}
else if (lambdaRefKind != delegateRefKind)
if (lambdaRefKind != delegateRefKind)
{
if (delegateRefKind == RefKind.None)
{
Expand All @@ -2182,6 +2178,14 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag
Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToParameterDisplayString());
}
}
else if (lambdaParameterType is not null && !lambdaParameterType.Equals(delegateParameterType, TypeCompareKind.AllIgnoreOptions))
{
SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType);

// Parameter {0} is declared as type '{1}{2}' but should be '{3}{4}'
Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation,
i + 1, lambdaRefKind.ToParameterPrefix(), distinguisher.First, delegateRefKind.ToParameterPrefix(), distinguisher.Second);
}
}
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1480,34 +1480,30 @@ private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate(
return LambdaConversionResult.BadParameterCount;
}

// SPEC: If F has an explicitly typed parameter list, each parameter in D has the same type
// SPEC: and modifiers as the corresponding parameter in F.
// SPEC: If F has an implicitly typed parameter list, D has no ref or out parameters.
// SPEC: If F has an explicitly typed or parenthesized implicitly type parameter list,
// SPEC: each parameter in D has the same type and modifiers as the corresponding parameter in F.
// SPEC: If F has an unparenthesized implicitly typed parameter list, D has no ref or out parameters.

if (anonymousFunction.HasExplicitlyTypedParameterList)
for (int p = 0; p < delegateParameters.Length; ++p)
{
for (int p = 0; p < delegateParameters.Length; ++p)
bool refsCompatible = OverloadResolution.AreRefsCompatibleForMethodConversion(
candidateMethodParameterRefKind: anonymousFunction.RefKind(p),
delegateParameterRefKind: delegateParameters[p].RefKind,
compilation);
bool typesCompatible = true;
if (anonymousFunction.HasExplicitlyTypedParameterList)
{
if (!OverloadResolution.AreRefsCompatibleForMethodConversion(
candidateMethodParameterRefKind: anonymousFunction.RefKind(p),
delegateParameterRefKind: delegateParameters[p].RefKind,
compilation) ||
!delegateParameters[p].Type.Equals(anonymousFunction.ParameterType(p), TypeCompareKind.AllIgnoreOptions))
{
return LambdaConversionResult.MismatchedParameterType;
}
typesCompatible = delegateParameters[p].Type.Equals(anonymousFunction.ParameterType(p), TypeCompareKind.AllIgnoreOptions);
}
}
else
{
for (int p = 0; p < delegateParameters.Length; ++p)

if (!typesCompatible || !refsCompatible)
{
if (delegateParameters[p].RefKind != RefKind.None)
{
return LambdaConversionResult.RefInImplicitlyTypedLambda;
}
return LambdaConversionResult.MismatchedParameterType;
}
}

if (!anonymousFunction.HasExplicitlyTypedParameterList)
{
// In C# it is not possible to make a delegate type
// such that one of its parameter types is a static type. But static types are
// in metadata just sealed abstract types; there is nothing stopping someone in
Expand Down
11 changes: 5 additions & 6 deletions src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1530,28 +1530,27 @@ public override bool ParameterIsDiscard(int index)

public override RefKind RefKind(int index)
{
Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length);
Debug.Assert(0 <= index);
return _parameterRefKinds.IsDefault ? Microsoft.CodeAnalysis.RefKind.None : _parameterRefKinds[index];
}

public override ScopedKind DeclaredScope(int index)
{
Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length);
Debug.Assert(0 <= index);
return _parameterDeclaredScopes.IsDefault ? ScopedKind.None : _parameterDeclaredScopes[index];
}

public override ParameterSyntax ParameterSyntax(int index)
{

Debug.Assert(_parameterSyntaxList is not null && 0 <= index && index < _parameterSyntaxList.Value.Count);
return _parameterSyntaxList.Value[index];
}

public override TypeWithAnnotations ParameterTypeWithAnnotations(int index)
{
Debug.Assert(this.HasExplicitlyTypedParameterList);
Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length);
return _parameterTypesWithAnnotations[index];
Debug.Assert(0 <= index);
Debug.Assert(_parameterTypesWithAnnotations.IsDefault || index < _parameterTypesWithAnnotations.Length);
return _parameterTypesWithAnnotations.IsDefault ? default : _parameterTypesWithAnnotations[index];
}

protected override UnboundLambdaState WithCachingCore(bool includeCache)
Expand Down
7 changes: 3 additions & 4 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12999,16 +12999,15 @@ private ParameterSyntax ParseLambdaParameter()
{
var attributes = ParseAttributeDeclarations(inExpressionContext: false);

// Params are actually illegal in a lambda, but we'll allow it for error recovery purposes and
// give the "params unexpected" error at semantic analysis time.
SyntaxListBuilder modifiers = _pool.Allocate();
if (IsParameterModifierExcludingScoped(this.CurrentToken) || this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword)
{
ParseParameterModifiers(modifiers, isFunctionPointerParameter: false);
}

// If we have "scoped/ref/out/in/params" always try to parse out a type.
var paramType = modifiers.Count != 0 || ShouldParseLambdaParameterType()
bool shouldParseLambdaParameterType = ShouldParseLambdaParameterType();

var paramType = shouldParseLambdaParameterType
? ParseType(ParseTypeMode.Parameter)
: null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,9 @@ private ImmutableArray<ParameterSymbol> MakeParameters(
else if (p < numDelegateParameters)
{
type = parameterTypes[p];
refKind = RefKind.None;
scope = ScopedKind.None;
refKind = unboundLambda.RefKind(p);
scope = unboundLambda.DeclaredScope(p);
paramSyntax = unboundLambda.ParameterSyntax(p);
}
else
{
Expand Down
43 changes: 23 additions & 20 deletions src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -675,26 +675,29 @@ public static void ReportParameterErrors(
// error CS1670: params is not valid in this context
diagnostics.Add(ErrorCode.ERR_IllegalParams, paramsKeyword.GetLocation());
}
else if (typeWithAnnotations.IsStatic)
else if (!typeWithAnnotations.IsDefault)
{
Debug.Assert(containingSymbol is null || (containingSymbol is FunctionPointerMethodSymbol or { ContainingType: not null }));
// error CS0721: '{0}': static types cannot be used as parameters
diagnostics.Add(
ErrorFacts.GetStaticClassParameterCode(containingSymbol?.ContainingType?.IsInterfaceType() ?? false),
syntax.Type?.Location ?? syntax.GetLocation(),
typeWithAnnotations.Type);
}
else if (firstDefault != -1 && parameterIndex > firstDefault && !isDefault && !isParams)
{
// error CS1737: Optional parameters must appear after all required parameters
Location loc = ((ParameterSyntax)syntax).Identifier.GetNextToken(includeZeroWidth: true).GetLocation(); //could be missing
diagnostics.Add(ErrorCode.ERR_DefaultValueBeforeRequiredValue, loc);
}
else if (refKind != RefKind.None &&
typeWithAnnotations.IsRestrictedType(ignoreSpanLikeTypes: true))
{
// CS1601: Cannot make reference to variable of type 'System.TypedReference'
diagnostics.Add(ErrorCode.ERR_MethodArgCantBeRefAny, syntax.Location, typeWithAnnotations.Type);
if (typeWithAnnotations.IsStatic)
{
Debug.Assert(containingSymbol is null || (containingSymbol is FunctionPointerMethodSymbol or { ContainingType: not null }));
// error CS0721: '{0}': static types cannot be used as parameters
diagnostics.Add(
ErrorFacts.GetStaticClassParameterCode(containingSymbol?.ContainingType?.IsInterfaceType() ?? false),
syntax.Type?.Location ?? syntax.GetLocation(),
typeWithAnnotations.Type);
}
else if (firstDefault != -1 && parameterIndex > firstDefault && !isDefault && !isParams)
{
// error CS1737: Optional parameters must appear after all required parameters
Location loc = ((ParameterSyntax)syntax).Identifier.GetNextToken(includeZeroWidth: true).GetLocation(); //could be missing
diagnostics.Add(ErrorCode.ERR_DefaultValueBeforeRequiredValue, loc);
}
else if (refKind != RefKind.None &&
typeWithAnnotations.IsRestrictedType(ignoreSpanLikeTypes: true))
{
// CS1601: Cannot make reference to variable of type 'System.TypedReference'
diagnostics.Add(ErrorCode.ERR_MethodArgCantBeRefAny, syntax.Location, typeWithAnnotations.Type);
}
}

if (isParams && ordinal != lastParameterIndex)
Expand All @@ -703,7 +706,7 @@ public static void ReportParameterErrors(
diagnostics.Add(ErrorCode.ERR_ParamsLast, syntax.GetLocation());
}

if (declaredScope == ScopedKind.ScopedValue && !typeWithAnnotations.IsRefLikeType())
if (declaredScope == ScopedKind.ScopedValue && !typeWithAnnotations.IsDefault && !typeWithAnnotations.IsRefLikeType())
{
diagnostics.Add(ErrorCode.ERR_ScopedRefAndRefStructOnly, syntax.Location);
}
Expand Down
Loading