}
// package all the arguments that match a ... T parameter into a []T.
-func mkdotargslice(typ *types.Type, args []*Node, init *Nodes, ddd *Node) *Node {
- esc := uint16(EscUnknown)
- if ddd != nil {
- esc = ddd.Esc
- }
+func mkdotargslice(typ *types.Type, args []*Node) *Node {
+ var n *Node
if len(args) == 0 {
- n := nodnil()
+ n = nodnil()
n.Type = typ
- return n
+ } else {
+ n = nod(OCOMPLIT, nil, typenod(typ))
+ n.List.Append(args...)
}
- n := nod(OCOMPLIT, nil, typenod(typ))
- if ddd != nil && prealloc[ddd] != nil {
- prealloc[n] = prealloc[ddd] // temporary to use
- }
- n.List.Set(args)
- n.Esc = esc
n = typecheck(n, ctxExpr)
if n.Type == nil {
Fatalf("mkdotargslice: typecheck failed")
}
- n = walkexpr(n, init)
return n
}
+// fixVariadicCall rewrites calls to variadic functions to use an
+// explicit ... argument if one is not already present.
+func fixVariadicCall(call *Node, init *Nodes) {
+ fntype := call.Left.Type
+ if !fntype.IsVariadic() || call.IsDDD() {
+ return
+ }
+
+ vi := fntype.NumParams() - 1
+ vt := fntype.Params().Field(vi).Type
+
+ args := call.List.Slice()
+ extra := args[vi:]
+ slice := mkdotargslice(vt, extra)
+ for i := range extra {
+ extra[i] = nil // allow GC
+ }
+
+ if ddd := call.Right; ddd != nil && slice.Op == OSLICELIT {
+ slice.Esc = ddd.Esc
+ if prealloc[ddd] != nil {
+ prealloc[slice] = prealloc[ddd] // temporary to use
+ }
+ }
+
+ slice = walkexpr(slice, init)
+
+ call.List.Set(append(args[:vi], slice))
+ call.SetIsDDD(true)
+}
+
func walkCall(n *Node, init *Nodes) {
if n.Rlist.Len() != 0 {
return // already walked
}
+
n.Left = walkexpr(n.Left, init)
walkexprlist(n.List.Slice(), init)
+ fixVariadicCall(n, init)
params := n.Left.Type.Params()
args := n.List.Slice()
- // If there's a ... parameter (which is only valid as the final
- // parameter) and this is not a ... call expression,
- // then assign the remaining arguments as a slice.
- if nf := params.NumFields(); nf > 0 {
- if last := params.Field(nf - 1); last.IsDDD() && !n.IsDDD() {
- // The callsite does not use a ..., but the called function is declared
- // with a final argument that has a ... . Build the slice that we will
- // pass as the ... argument.
- tail := args[nf-1:]
- slice := mkdotargslice(last.Type, tail, init, n.Right)
- // Allow immediate GC.
- for i := range tail {
- tail[i] = nil
- }
- args = append(args[:nf-1], slice)
- }
- }
// If this is a method call, add the receiver at the beginning of the args.
if n.Op == OCALLMETH {
n = cheapexpr(n, init)
- ddd := nodl(n.Pos, ODDDARG, nil, nil)
- ddd.Type = types.NewPtr(types.NewArray(types.Types[TUNSAFEPTR], int64(len(originals))))
- ddd.Esc = EscNone
- slice := mkdotargslice(types.NewSlice(types.Types[TUNSAFEPTR]), originals, init, ddd)
+ slice := mkdotargslice(types.NewSlice(types.Types[TUNSAFEPTR]), originals)
+ slice.Esc = EscNone
init.Append(mkcall("checkptrArithmetic", nil, init, convnop(n, types.Types[TUNSAFEPTR]), slice))
// TODO(khr): Mark backing store of slice as dead. This will allow us to reuse