Fix #891: arrow lambdas as named/extension arguments and extension-method hover#897
Merged
Merged
Conversation
…hover Three distinct problems reported in issue #891 are addressed: A) Arrow lambda as a NAMED argument to a constructor/method taking a Func<...>: the delegate-typed parameter now target-types the lambda before binding, so `Service(httpClientFactory: () -> { throw ... })` binds as Func<...> instead of misrouting to the data-struct .copy(...) named-argument path (the misleading GS0162). A statement-body arrow lambda whose every path throws now adopts the target delegate's return type (InferLambdaReturnType), mirroring `func() T { throw ... }`. B) Un-typed arrow lambda passed to a generic LINQ extension method (Single<TSource>(this IEnumerable<TSource>, Func<TSource,bool>)): such lambdas are now deferred during argument binding, the overload is resolved with the lambda slot treated as unconstrained (so TSource is inferred from the receiver), and the lambda is then bound against the resolved closed delegate parameter. Previously this reported "Cannot find function Single" / GS0304. C) Hover over an invoked (extension) method now resolves the actual method from the bound program before falling back to type-name resolution, so hovering `Single` shows the method instead of the unrelated type float32 (System.Single). Adds emit/runtime regression tests (DoctorService-style ctor with named Func arg incl. throw-only body; LINQ Single with arrow-lambda predicate) and LanguageServer hover tests for the extension-method case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #891
Issue #891 reported three distinct problems when using lambdas as arguments to method/constructor calls. All three are fixed here, with regression tests for each.
Problem A — arrow lambda as a NAMED argument to a ctor/method taking
Func<...>Symptom:
DoctorService(httpClientFactory: () -> { throw ... })failed with the misleadingNamed arguments are only supported for data-struct .copy(...).(GS0162), while thefunc() T { ... }form worked.Root cause: A block-body arrow lambda that only throws infers
() -> void. CLR constructor overload resolution then fails to match theFunc<...>parameter (void ≠ Func), and the single named-argument call falls through to the single-arg conversion path, which rebinds theNamedArgumentExpressionSyntaxdirectly and reports GS0162.Fix:
ExpressionBinder.Calls.cs(TryBindClrConstructorFromType): discover the delegate function-type for each argument position from the candidate constructors (by name for named args, by position otherwise) and target-type arrow/func literal arguments before binding, viaBindCallArgumentWithDelegateTargetTyping/TryResolveDelegateTargetFromCandidates.LambdaBinder.cs(InferLambdaReturnType): when a target delegate type is supplied and the lambda body never completes normally (all paths throw), adopt the target's return type — exactly as the equivalentfunc() T { throw ... }literal does. ReusesControlFlowGraph.AllPathsReturn.Problem B — arrow lambda passed to a generic LINQ extension method (
Single)Symptom:
list.Single((x) -> x == 3)failed withCannot find function Single./ GS0304, whilelist.Single(func(x int32) bool { ... })worked.Root cause: An un-typed arrow lambda parameter cannot be bound bare (GS0304), producing an error-typed argument whose null CLR type aborts overload resolution, so the generic extension method
Single<TSource>is never matched.Fix:
ExpressionBinder.Calls.cs(BindAccessorCall): un-typed arrow lambda arguments are deferred to a placeholder (suppressing GS0304).ResolveDeferredArrowLambdaArgumentsprobes the applicable candidates (instance methods on the receiver, imported extension methods, or static methods of the accessed class) with the lambda slot treated as unconstrained — generic inference skips it and infersTSourcefrom the receiver — then re-binds the lambda against the resolved, closed delegate parameter (Func<TSource,bool>). The predicate body then type-checks tobool.Problem C — wrong hover on extension methods
Symptom: Hovering the
Singleextension method showedstruct float32/ "Represents a single-precision floating-point number." — i.e. it matched the typeSystem.Singleby name instead of the method.Root cause:
HoverComputer.BuildImportedClrModelresolved the token as a type name (ResolveImportedClrType("Single")→System.Single) before any member resolution, and member resolution had no support for extension methods.Fix:
SemanticLookup.cs: newTryResolveInvokedImportedMethodresolves the actual method an invoked call bound to, by scanning the lowered bound program for imported call nodes matching the method name and arity (the extension form carries one extra leading receiver parameter). This naturally handles generic extension methods.HoverComputer.cs: when the hovered token is the identifier of an invoked call, resolve the invoked method before type-name resolution.Tests
test/Compiler.Tests/Emit/Issue891LambdaArgumentEmitTests.cs(emit + ilverify + run):Funcargument (value-returning and throw-only), incl. aDoctorService-style helper assembly whose ctor is(int logger = 0, Func<string> httpClientFactory = null)invoked with only the namedFuncargument;Singlewith an arrow-lambda predicate overList[int32]andList[string].test/LanguageServer.Tests/HoverHandlerTests.cs: hover over theSingleextension-method invocation resolves to the method (and notfloat32), for both the arrow-lambda andfunc(...)predicate forms.Validation
Local
dotnet build GSharp.sln -c Releasesucceeds (0 warnings/errors). Fulldotnet test GSharp.sln -c Release: 5480 passed, 1 skipped, 0 failed (Core 3340, Compiler 1353, LanguageServer 337, Interpreter 308, Extensions 104, Sdk 38). Compiler.Tests validate emitted IL with ilverify.