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 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnno
}

public override bool HasExplicitlyTypedParameterList { get { return false; } }
public override bool HasParameterRefKinds => false;
public override int ParameterCount { get { return _parameters.Length; } }
public override bool IsAsync { get { return false; } }
public override bool IsStatic => false;
Expand Down
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ private UnboundLambda AnalyzeAnonymousFunction(
var refKind = RefKind.None;
var scope = ScopedKind.None;

refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out scope);

if (typeSyntax == null)
{
hasExplicitlyTypedParameterList = false;
Expand All @@ -180,7 +182,6 @@ private UnboundLambda AnalyzeAnonymousFunction(
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)
Expand Down
12 changes: 8 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2141,18 +2141,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))
if (lambdaParameterType is not null && !lambdaParameterType.Equals(delegateParameterType, TypeCompareKind.AllIgnoreOptions))
{
SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1453,31 +1453,27 @@ 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(delegateParameters[p].RefKind, anonymousFunction.RefKind(p), compilation);
bool typesCompatible = true;
if (anonymousFunction.HasExplicitlyTypedParameterList)
{
if (!OverloadResolution.AreRefsCompatibleForMethodConversion(delegateParameters[p].RefKind, anonymousFunction.RefKind(p), 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
6 changes: 5 additions & 1 deletion src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ public bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations r
public Binder GetWithParametersBinder(LambdaSymbol lambdaSymbol, Binder binder)
=> Data.GetWithParametersBinder(lambdaSymbol, binder);
public bool HasExplicitlyTypedParameterList { get { return Data.HasExplicitlyTypedParameterList; } }
public bool HasParameterRefKinds { get { return Data.HasParameterRefKinds; } }
public int ParameterCount { get { return Data.ParameterCount; } }
public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, out bool inferredFromFunctionType)
=> BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteInfo, out inferredFromFunctionType);
Expand Down Expand Up @@ -546,6 +547,7 @@ internal UnboundLambdaState WithCaching(bool includeCache)
public abstract bool HasSignature { get; }
public abstract bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType);
public abstract bool HasExplicitlyTypedParameterList { get; }
public abstract bool HasParameterRefKinds { get; }
public abstract int ParameterCount { get; }
public abstract bool IsAsync { get; }
public abstract bool IsStatic { get; }
Expand Down Expand Up @@ -1443,6 +1445,8 @@ public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnno

public override bool HasExplicitlyTypedParameterList { get { return !_parameterTypesWithAnnotations.IsDefault; } }

public override bool HasParameterRefKinds { get { return !_parameterRefKinds.IsDefault; } }

public override int ParameterCount { get { return _parameterNames.IsDefault ? 0 : _parameterNames.Length; } }

public override bool IsAsync { get { return _isAsync; } }
Expand Down Expand Up @@ -1497,7 +1501,7 @@ 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];
}

Expand Down
14 changes: 10 additions & 4 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12862,16 +12862,22 @@ 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()
// If we have "scoped/params" always try to parse out a type.
// Otherwise, allow the omission of a type

bool containsModifierRequiringType = modifiers.Count > 0
&& (modifiers.Any((int)SyntaxKind.ScopedKeyword)
|| modifiers.Any((int)SyntaxKind.ParamsKeyword));

bool shouldParseLambdaParameterType = containsModifierRequiringType || ShouldParseLambdaParameterType();

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private ImmutableArray<ParameterSymbol> MakeParameters(
else if (p < numDelegateParameters)
{
type = parameterTypes[p];
refKind = RefKind.None;
refKind = unboundLambda.RefKind(p);
scope = ScopedKind.None;
}
else
Expand Down
Loading