in internal/compile/compile.go [1805:1869]
func (fcomp *fcomp) function(f *resolve.Function) {
// Evaluation of the defaults may fail, so record the position.
fcomp.setPos(f.Pos)
// To reduce allocation, we emit a combined tuple
// for the defaults and the freevars.
// The function knows where to split it at run time.
// Generate tuple of parameter defaults. For:
// def f(p1, p2=dp2, p3=dp3, *, k1, k2=dk2, k3, **kwargs)
// the tuple is:
// (dp2, dp3, MANDATORY, dk2, MANDATORY).
ndefaults := 0
seenStar := false
for _, param := range f.Params {
switch param := param.(type) {
case *syntax.BinaryExpr:
fcomp.expr(param.Y)
ndefaults++
case *syntax.UnaryExpr:
seenStar = true // * or *args (also **kwargs)
case *syntax.Ident:
if seenStar {
fcomp.emit(MANDATORY)
ndefaults++
}
}
}
// Capture the cells of the function's
// free variables from the lexical environment.
for _, freevar := range f.FreeVars {
// Don't call fcomp.lookup because we want
// the cell itself, not its content.
switch freevar.Scope {
case resolve.Free:
fcomp.emit1(FREE, uint32(freevar.Index))
case resolve.Cell:
fcomp.emit1(LOCAL, uint32(freevar.Index))
}
}
fcomp.emit1(MAKETUPLE, uint32(ndefaults+len(f.FreeVars)))
funcode := fcomp.pcomp.function(f.Name, f.Pos, f.Body, f.Locals, f.FreeVars)
if debug {
// TODO(adonovan): do compilations sequentially not as a tree,
// to make the log easier to read.
// Simplify by identifying Toplevel and functionIndex 0.
fmt.Fprintf(os.Stderr, "resuming %s @ %s\n", fcomp.fn.Name, fcomp.pos)
}
// def f(a, *, b=1) has only 2 parameters.
numParams := len(f.Params)
if f.NumKwonlyParams > 0 && !f.HasVarargs {
numParams--
}
funcode.NumParams = numParams
funcode.NumKwonlyParams = f.NumKwonlyParams
funcode.HasVarargs = f.HasVarargs
funcode.HasKwargs = f.HasKwargs
fcomp.emit1(MAKEFUNC, fcomp.pcomp.functionIndex(funcode))
}