Skip to content

Conversation

@mccanne
Copy link
Collaborator

@mccanne mccanne commented Oct 13, 2025

This commit integrates the lambda unraveling technique used in the resolver into the first-pass of the semantic translator. This is possible by indexing the lambda permutation using decl IDs instead of call tags based on the intuition that the only thing that can give rise to variation in unraveled combinations is the declared function, i.e., at the outermost call site, each variation of lamba-parameterized function depends only on the function passed in, which are uniquely defined by their ID not their unraveled instances.

This means the evaluator can simply traverse an expression to determine if it's constant and then dagen it. There is no need anymore to re-enter the resolver.

This is also the key step needed to integrate type checking into the translator, which will then allow us to utilize dataflow-analyzed fused types from pipe queries in schema-column resolution for SQL queries that include pipe subqueries. This will be forthcoming in a subsequent PR.

This commit integrates the lambda unraveling technique used in the
resolver into the first-pass of the semantic translator.  This is
possible by indexing the lambda permutation using decl IDs instead of
call tags based on the intuition that the only thing that can give
rise to variation in unraveled combinations is the declared function,
i.e., at the outermost call site, each variation of lamba-parameterized
function depends only on the function passed in, which are uniquely
defined by their ID not their unraveled instances.

This means the evaluator can simply traverse an expression to
determine if it's constant and then dagen it.  There is no need
anymore to re-enter the resolver.

This is also the key step needed to integrate type checking into
the translator, which will then allow us to utilize dataflow-analyzed
fused types from pipe queries in schema-column resolution for SQL
queries that include pipe subqueries.  This will be forthcoming in a
subsequent PR.
Copy link
Member

@nwt nwt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still working on understanding the nuance in here but it looks good at a high level and it clearly works so :shipit:.

Comment on lines 100 to 101
id := e.Name
if boundID, _ := t.scope.lookupFuncDeclOrParam(e.Name); boundID != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Using ID here would make the data flow a little clearer.

Suggested change
id := e.Name
if boundID, _ := t.scope.lookupFuncDeclOrParam(e.Name); boundID != "" {
id := e.Name
if boundID, _ := t.scope.lookupFuncDeclOrParam(id); boundID != "" {

Comment on lines 38 to 39
// at a built-in // or function declaration and they can't be modified
// *(only passed by reference), the variants are determined by decl ID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
// at a built-in // or function declaration and they can't be modified
// *(only passed by reference), the variants are determined by decl ID
// at a built-in or function declaration and they can't be modified
// (only passed by reference), the variants are determined by decl ID

}

type lambda struct {
param string // parameter name this lambda arg appeared asa
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
param string // parameter name this lambda arg appeared asa
param string // parameter name this lambda arg appeared as

Comment on lines 134 to 135
params := idsToStrings(d.lambda.Params)
for _, param := range idsToStrings(d.lambda.Params) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
params := idsToStrings(d.lambda.Params)
for _, param := range idsToStrings(d.lambda.Params) {
params := idsToStrings(d.lambda.Params)
for _, param := range params {

Comment on lines 799 to 810
func (t *translator) resolveCall(n ast.Node, id string, args []sem.Expr) sem.Expr {
if isBuiltin(id) {
// Check argument count here for builtin functions.
if _, err := function.New(super.NewContext(), id, len(args)); err != nil {
t.error(n, err)
return badExpr()
}
return sem.NewCall(n, id, args)
}
return t.resolver.mustResolveCall(n, id, args)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Putting this immediately before mustResolveCall would improve readability a bit.

@mccanne mccanne merged commit 9b7aa4c into main Oct 13, 2025
3 checks passed
@mccanne mccanne deleted the resolver-redo branch October 13, 2025 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants