this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

internal/core/adt: preparatory work for user-defined functions

When introducing user-callable functions, we want to make
a break with the past and not support some of the modes that
the current builtin function calls support (like, for example,
we want a clear distinction between functions and validators).

In order to do that, the plan is to introduce a new `adt.Func` type
to implement the new semantics rather than complicate the
already complex `adt.Builtin` logic. That means that we want the
`CallExpr` logic to be independent of the `Builtin` logic, but they
are both somewhat intertwined currently.

Factor out `Builtin.rawCall` from `CallExpr.evaluate` in order to
separate the two some more, and also rename `CallContext` to
`BuiltinCallContext` make it clear that the type is about Builtin
calls - we will use a different API for `adt.Func`.

This should have no semantic effect at all.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I2d33b14a8cf5c431dd70cc7a9e14930ba43d807f
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1232332
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>

+80 -84
+1 -1
cue/interpreter/wasm/builtin.go
··· 55 55 call := &adt.CallExpr{Fun: &adt.Builtin{ 56 56 Result: adt.TopKind, 57 57 Name: name, 58 - Func: func(call adt.CallContext) adt.Expr { 58 + Func: func(call adt.BuiltinCallContext) adt.Expr { 59 59 opctx := call.OpContext() 60 60 61 61 scope := value.Make(opctx, scope)
+10 -10
internal/core/adt/call.go
··· 18 18 "cuelang.org/go/cue/token" 19 19 ) 20 20 21 - // A CallContext holds all relevant information for a function call to 21 + // A BuiltinCallContext holds all relevant information for a function call to 22 22 // be executed. 23 - type CallContext struct { 23 + type BuiltinCallContext struct { 24 24 ctx *OpContext 25 25 call *CallExpr 26 26 builtin *Builtin ··· 28 28 isValidator bool 29 29 } 30 30 31 - func (c CallContext) OpContext() *OpContext { 31 + func (c BuiltinCallContext) OpContext() *OpContext { 32 32 return c.ctx 33 33 } 34 34 35 - func (c CallContext) Pos() token.Pos { 35 + func (c BuiltinCallContext) Pos() token.Pos { 36 36 if c.call != nil { 37 37 return Pos(c.call) 38 38 } 39 39 return Pos(c.builtin) 40 40 } 41 41 42 - func (c CallContext) Value(i int) Value { 42 + func (c BuiltinCallContext) Value(i int) Value { 43 43 return c.args[i] 44 44 } 45 45 46 46 // NumParams returns the total number of parameters to this function. 47 - func (c CallContext) NumParams() int { 47 + func (c BuiltinCallContext) NumParams() int { 48 48 return len(c.args) 49 49 } 50 50 51 - func (c CallContext) AddPositions(err *ValueError) { 51 + func (c BuiltinCallContext) AddPositions(err *ValueError) { 52 52 for _, v := range c.args { 53 53 err.AddPosition(v) 54 54 } ··· 60 60 // 61 61 // This method of getting an argument should be used when the argument is used 62 62 // as a schema and may contain cycles. 63 - func (c CallContext) Arg(i int) Value { 63 + func (c BuiltinCallContext) Arg(i int) Value { 64 64 // If the call context represents a validator call, the argument will be 65 65 // offset by 1. 66 66 if c.isValidator { ··· 77 77 } 78 78 79 79 // Expr returns the nth argument expression without evaluating it. 80 - func (c CallContext) Expr(i int) Expr { 80 + func (c BuiltinCallContext) Expr(i int) Expr { 81 81 // If the call context represents a validator call, the argument will be 82 82 // offset by 1. 83 83 if c.isValidator { ··· 92 92 return x 93 93 } 94 94 95 - func (c CallContext) Errf(format string, args ...interface{}) *Bottom { 95 + func (c BuiltinCallContext) Errf(format string, args ...interface{}) *Bottom { 96 96 return c.ctx.NewErrf(format, args...) 97 97 }
+32 -36
internal/core/adt/expr.go
··· 1446 1446 } 1447 1447 1448 1448 func (x *CallExpr) evaluate(c *OpContext, state Flags) Value { 1449 - call := CallContext{ 1450 - ctx: c, 1451 - call: x, 1452 - } 1453 - 1454 1449 fun := c.value(x.Fun, Flags{ 1455 1450 status: partial, 1456 1451 condition: concreteKnown, ··· 1458 1453 }) 1459 1454 switch f := fun.(type) { 1460 1455 case *Builtin: 1461 - call.builtin = f 1462 - if f.RawFunc != nil { 1463 - if !call.builtin.checkArgs(c, Pos(x), len(x.Args)) { 1464 - return nil 1465 - } 1466 - return f.RawFunc(call) 1467 - } 1456 + return f.rawCall(c, x, state) 1468 1457 1469 1458 case *BuiltinValidator: 1470 1459 // We allow a validator that takes no arguments except the validated ··· 1472 1461 switch { 1473 1462 case f.Src != nil: 1474 1463 c.AddErrf("cannot call previously called validator %s", x.Fun) 1464 + return nil 1475 1465 1476 1466 case f.Builtin.IsValidator(len(x.Args)): 1477 1467 v := *f ··· 1479 1469 return &v 1480 1470 1481 1471 default: 1482 - call.builtin = f.Builtin 1472 + return f.Builtin.rawCall(c, x, state) 1483 1473 } 1484 - 1485 1474 default: 1486 1475 if !IsConcrete(fun) && fun.Kind()&FuncKind != 0 { 1487 1476 c.addErrf(IncompleteError, Pos(x.Fun), "cannot call non-concrete value %s (type %s)", x.Fun, kind(fun)) ··· 1490 1479 } 1491 1480 return nil 1492 1481 } 1482 + } 1493 1483 1484 + func (builtin *Builtin) rawCall(c *OpContext, call *CallExpr, state Flags) Value { 1485 + callCtx := BuiltinCallContext{ 1486 + ctx: c, 1487 + call: call, 1488 + builtin: builtin, 1489 + } 1490 + if builtin.RawFunc != nil { 1491 + if !builtin.checkArgs(c, Pos(call), len(call.Args)) { 1492 + return nil 1493 + } 1494 + return builtin.RawFunc(callCtx) 1495 + } 1494 1496 // Arguments to functions are open. This mostly matters for NonConcrete 1495 1497 // builtins. 1496 1498 saved := c.ci ··· 1501 1503 c.ci.FromEmbed = saved.FromEmbed 1502 1504 }() 1503 1505 1504 - args := make([]Value, 0, len(x.Args)) 1505 - for i, a := range x.Args { 1506 + args := make([]Value, 0, len(call.Args)) 1507 + for i, a := range call.Args { 1506 1508 saved := c.errs 1507 1509 c.errs = nil 1508 1510 // XXX: XXX: clear id.closeContext per argument and remove from runTask? ··· 1510 1512 runMode := state.mode 1511 1513 cond := state.condition 1512 1514 var expr Value 1513 - if call.builtin.NonConcrete { 1515 + if builtin.NonConcrete { 1514 1516 state = Flags{ 1515 1517 status: state.status, 1516 1518 condition: cond, ··· 1540 1542 if c.errs == nil { 1541 1543 // There SHOULD be an error in the context. If not, we generate 1542 1544 // one. 1543 - c.Assertf(Pos(x.Fun), c.HasErr(), 1544 - "argument %d to function %s is incomplete", i, x.Fun) 1545 + c.Assertf(Pos(call.Fun), c.HasErr(), 1546 + "argument %d to function %s is incomplete", i, call.Fun) 1545 1547 } 1546 1548 1547 1549 case *Bottom: ··· 1556 1558 if c.HasErr() { 1557 1559 return nil 1558 1560 } 1559 - if call.builtin.IsValidator(len(args)) { 1560 - return &BuiltinValidator{x, call.builtin, args} 1561 + if builtin.IsValidator(len(args)) { 1562 + return &BuiltinValidator{call, builtin, args} 1561 1563 } 1562 - call.args = args 1563 - result := call.builtin.call(call) 1564 + callCtx.args = args 1565 + result := builtin.call(callCtx) 1564 1566 if result == nil { 1565 1567 return nil 1566 1568 } ··· 1579 1581 // arguments. By default, all arguments are checked to be concrete. 1580 1582 NonConcrete bool 1581 1583 1582 - Func func(call CallContext) Expr 1584 + Func func(call BuiltinCallContext) Expr 1583 1585 1584 1586 // RawFunc gives low-level control to CUE's internals for builtins. 1585 1587 // It should be used when fine control over the evaluation process is ··· 1589 1591 // the Context. 1590 1592 // 1591 1593 // TODO: consider merging Func and RawFunc into a single field again. 1592 - RawFunc func(call CallContext) Value 1594 + RawFunc func(call BuiltinCallContext) Value 1593 1595 1594 1596 // Added indicates as of which language version this builtin can be used. 1595 1597 Added string ··· 1599 1601 } 1600 1602 1601 1603 type Param struct { 1602 - Name Feature // name of the argument; mostly for documentation 1603 - Value Value // Could become Value later, using disjunctions for defaults. 1604 + Value Value 1604 1605 } 1605 1606 1606 1607 // Kind returns the kind mask of this parameter. ··· 1673 1674 return true 1674 1675 } 1675 1676 1676 - func (x *Builtin) call(call CallContext) Expr { 1677 + func (x *Builtin) call(call BuiltinCallContext) Expr { 1677 1678 c := call.ctx 1678 1679 p := call.Pos() 1679 1680 ··· 1685 1686 call.args = append(call.args, x.Params[i].Default()) 1686 1687 } 1687 1688 for i, a := range call.args { 1688 - if x.Params[i].Kind() == BottomKind { 1689 - continue 1690 - } 1691 1689 if b := bottom(a); b != nil { 1692 1690 return b 1693 1691 } ··· 1765 1763 args[0] = v 1766 1764 copy(args[1:], x.Args) 1767 1765 1768 - call := CallContext{ 1766 + return validateWithBuiltin(BuiltinCallContext{ 1769 1767 ctx: c, 1770 1768 call: x.Src, 1771 1769 builtin: x.Builtin, 1772 1770 args: args, 1773 1771 isValidator: true, 1774 - } 1775 - 1776 - return validateWithBuiltin(call) 1772 + }) 1777 1773 } 1778 1774 1779 - func validateWithBuiltin(call CallContext) *Bottom { 1775 + func validateWithBuiltin(call BuiltinCallContext) *Bottom { 1780 1776 var severeness ErrorCode 1781 1777 var err errors.Error 1782 1778
+12 -12
internal/core/compile/builtin.go
··· 43 43 44 44 Params: []adt.Param{stringParam}, 45 45 Result: adt.BottomKind, 46 - RawFunc: func(call adt.CallContext) adt.Value { 46 + RawFunc: func(call adt.BuiltinCallContext) adt.Value { 47 47 ctx := call.OpContext() 48 48 arg := call.Expr(0) 49 49 ··· 86 86 Name: "len", 87 87 Params: []adt.Param{{Value: &adt.BasicType{K: supportedByLen}}}, 88 88 Result: adt.IntKind, 89 - Func: func(call adt.CallContext) adt.Expr { 89 + Func: func(call adt.BuiltinCallContext) adt.Expr { 90 90 c := call.OpContext() 91 91 92 92 v := call.Value(0) ··· 134 134 Name: "close", 135 135 Params: []adt.Param{structParam}, 136 136 Result: adt.StructKind, 137 - Func: func(call adt.CallContext) adt.Expr { 137 + Func: func(call adt.BuiltinCallContext) adt.Expr { 138 138 c := call.OpContext() 139 139 s, ok := call.Value(0).(*adt.Vertex) 140 140 if !ok { ··· 154 154 Name: "__closeAll", 155 155 Params: []adt.Param{topParam}, 156 156 Result: adt.TopKind, 157 - Func: func(call adt.CallContext) adt.Expr { 157 + Func: func(call adt.BuiltinCallContext) adt.Expr { 158 158 c := call.OpContext() 159 159 160 160 x := call.Expr(0) ··· 184 184 Name: "__reclose", 185 185 Params: []adt.Param{topParam}, 186 186 Result: adt.TopKind, 187 - Func: func(call adt.CallContext) adt.Expr { 187 + Func: func(call adt.BuiltinCallContext) adt.Expr { 188 188 c := call.OpContext() 189 189 190 190 x := call.Expr(0) ··· 219 219 Name: "and", 220 220 Params: []adt.Param{listParam}, 221 221 Result: adt.IntKind, 222 - Func: func(call adt.CallContext) adt.Expr { 222 + Func: func(call adt.BuiltinCallContext) adt.Expr { 223 223 c := call.OpContext() 224 224 seq := c.RawElems(call.Value(0)) 225 225 a := []adt.Value{} ··· 238 238 Params: []adt.Param{listParam}, 239 239 Result: adt.IntKind, 240 240 NonConcrete: true, 241 - Func: func(call adt.CallContext) adt.Expr { 241 + Func: func(call adt.BuiltinCallContext) adt.Expr { 242 242 c := call.OpContext() 243 243 244 244 d := []adt.Disjunct{} ··· 275 275 Name: "div", 276 276 Params: []adt.Param{intParam, intParam}, 277 277 Result: adt.IntKind, 278 - Func: func(call adt.CallContext) adt.Expr { 278 + Func: func(call adt.BuiltinCallContext) adt.Expr { 279 279 c := call.OpContext() 280 280 const name = "argument to div builtin" 281 281 return intDivOp(c, (*adt.OpContext).IntDiv, name, call.Value(0), call.Value(1)) ··· 286 286 Name: "mod", 287 287 Params: []adt.Param{intParam, intParam}, 288 288 Result: adt.IntKind, 289 - Func: func(call adt.CallContext) adt.Expr { 289 + Func: func(call adt.BuiltinCallContext) adt.Expr { 290 290 c := call.OpContext() 291 291 292 292 const name = "argument to mod builtin" ··· 299 299 Name: "quo", 300 300 Params: []adt.Param{intParam, intParam}, 301 301 Result: adt.IntKind, 302 - Func: func(call adt.CallContext) adt.Expr { 302 + Func: func(call adt.BuiltinCallContext) adt.Expr { 303 303 c := call.OpContext() 304 304 const name = "argument to quo builtin" 305 305 return intDivOp(c, (*adt.OpContext).IntQuo, name, call.Value(0), call.Value(1)) ··· 310 310 Name: "rem", 311 311 Params: []adt.Param{intParam, intParam}, 312 312 Result: adt.IntKind, 313 - Func: func(call adt.CallContext) adt.Expr { 313 + Func: func(call adt.BuiltinCallContext) adt.Expr { 314 314 c := call.OpContext() 315 315 const name = "argument to rem builtin" 316 316 return intDivOp(c, (*adt.OpContext).IntRem, name, call.Value(0), call.Value(1)) ··· 332 332 Name: "testExperiment", 333 333 Params: []adt.Param{topParam}, 334 334 Result: adt.TopKind, 335 - Func: func(call adt.CallContext) adt.Expr { 335 + Func: func(call adt.BuiltinCallContext) adt.Expr { 336 336 if call.Pos().Experiment().Testing { 337 337 return call.Value(0) 338 338 } else {
+2 -2
internal/core/compile/validator.go
··· 29 29 Params: []adt.Param{topParam, intParam, listParam}, // varargs 30 30 Result: adt.BoolKind, 31 31 NonConcrete: true, 32 - Func: func(call adt.CallContext) adt.Expr { 32 + Func: func(call adt.BuiltinCallContext) adt.Expr { 33 33 c := call.OpContext() 34 34 35 35 if !c.IsValidator { ··· 87 87 Params: []adt.Param{topParam, topParam, topParam, topParam}, 88 88 Result: adt.BoolKind, 89 89 NonConcrete: true, 90 - Func: func(call adt.CallContext) adt.Expr { 90 + Func: func(call adt.BuiltinCallContext) adt.Expr { 91 91 c := call.OpContext() 92 92 93 93 if !c.IsValidator {
+1 -1
internal/core/runtime/extern_test.go
··· 119 119 120 120 call := &adt.CallExpr{Fun: &adt.Builtin{ 121 121 Result: adt.TopKind, 122 - Func: func(call adt.CallContext) adt.Expr { 122 + Func: func(call adt.BuiltinCallContext) adt.Expr { 123 123 opctx := call.OpContext() 124 124 125 125 cuectx := (*cue.Context)(c.runtime)
+1 -1
internal/encoding/yaml/validate.go
··· 29 29 // 30 30 // If Validate is called in a broader context, like a validation or function 31 31 // call, the cycle context of n should be accumulated in c before this call. 32 - // This can be done by using the Expr method on the CallContext. 32 + // This can be done by using the Expr method on the BuiltinCallContext. 33 33 func Validate(c *adt.OpContext, b []byte, v cue.Value) (bool, error) { 34 34 d := NewDecoder("yaml.Validate", b) 35 35 r := v.Context()
+4 -4
internal/pkg/builtin.go
··· 130 130 Package: b.Pkg, 131 131 Name: b.Name, 132 132 } 133 - x.Func = func(call adt.CallContext) (ret adt.Expr) { 133 + x.Func = func(call adt.BuiltinCallContext) (ret adt.Expr) { 134 134 ctx := call.OpContext() 135 135 136 136 // call, _ := ctx.Source().(*ast.CallExpr) 137 137 c := &CallCtxt{ 138 - CallContext: call, 139 - ctx: ctx, 140 - builtin: b, 138 + BuiltinCallContext: call, 139 + ctx: ctx, 140 + builtin: b, 141 141 } 142 142 defer func() { 143 143 var errVal interface{} = c.Err
+17 -17
internal/pkg/context.go
··· 28 28 29 29 // CallCtxt is passed to builtin implementations that need to use a cue.Value. This is an internal type. Its interface may change. 30 30 type CallCtxt struct { 31 - adt.CallContext 31 + adt.BuiltinCallContext 32 32 ctx *adt.OpContext 33 33 builtin *Builtin 34 34 Err any ··· 46 46 47 47 // Schema returns the ith argument as is, without converting it to a cue.Value. 48 48 // 49 - // TODO: Schema should use CallContext.Expr to capture cycle information. 49 + // TODO: Schema should use [adt.BuiltinCallContext.Expr] to capture cycle information. 50 50 // However, this only makes sense if functions also use the same OpContext for 51 51 // further evaluation. We should enforce as we port the old calls. 52 52 func (c *CallCtxt) Schema(i int) Schema { ··· 56 56 57 57 // Value returns a finalized cue.Value for the ith argument. 58 58 func (c *CallCtxt) Value(i int) cue.Value { 59 - v := value.Make(c.ctx, c.CallContext.Value(i)) 59 + v := value.Make(c.ctx, c.BuiltinCallContext.Value(i)) 60 60 if c.builtin.NonConcrete { 61 61 // In case NonConcrete is false, the concreteness is already checked 62 62 // at call time. We may want to use finalize semantics in both cases, ··· 72 72 } 73 73 74 74 func (c *CallCtxt) Struct(i int) Struct { 75 - x := c.CallContext.Value(i) 75 + x := c.BuiltinCallContext.Value(i) 76 76 if c.builtin.NonConcrete { 77 77 x = adt.Default(x) 78 78 } ··· 106 106 func (c *CallCtxt) Int64(i int) int64 { return c.intValue(i, 64, "int64") } 107 107 108 108 func (c *CallCtxt) intValue(i, bitLen int, typ string) int64 { 109 - arg := c.CallContext.Value(i) 109 + arg := c.BuiltinCallContext.Value(i) 110 110 if num, _ := c.ctx.EvaluateKeepState(arg).(*adt.Num); num != nil { 111 111 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 112 112 if n, err := num.X.Int64(); err == nil && bits.Len64(uint64(n)) <= bitLen { ··· 135 135 func (c *CallCtxt) Uint64(i int) uint64 { return c.uintValue(i, 64, "uint64") } 136 136 137 137 func (c *CallCtxt) uintValue(i, bitLen int, typ string) uint64 { 138 - arg := c.CallContext.Value(i) 138 + arg := c.BuiltinCallContext.Value(i) 139 139 if num, _ := c.ctx.EvaluateKeepState(arg).(*adt.Num); num != nil { 140 140 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 141 141 // Note that [apd.Decimal] has an Int64 method, but no Uint64 method, ··· 159 159 } 160 160 161 161 func (c *CallCtxt) Decimal(i int) *apd.Decimal { 162 - arg := c.CallContext.Value(i) 162 + arg := c.BuiltinCallContext.Value(i) 163 163 if num, _ := c.ctx.EvaluateKeepState(arg).(*adt.Num); num != nil { 164 164 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 165 165 return &num.X ··· 174 174 } 175 175 176 176 func (c *CallCtxt) Float64(i int) float64 { 177 - arg := c.CallContext.Value(i) 177 + arg := c.BuiltinCallContext.Value(i) 178 178 if num, _ := c.ctx.EvaluateKeepState(arg).(*adt.Num); num != nil { 179 179 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 180 180 if f, err := num.X.Float64(); err == nil { ··· 191 191 } 192 192 193 193 func (c *CallCtxt) BigInt(i int) *big.Int { 194 - arg := c.CallContext.Value(i) 194 + arg := c.BuiltinCallContext.Value(i) 195 195 if num, _ := c.ctx.EvaluateKeepState(arg).(*adt.Num); num != nil { 196 196 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 197 197 return num.BigInt(nil) ··· 208 208 var ten = big.NewInt(10) 209 209 210 210 func (c *CallCtxt) BigFloat(i int) *big.Float { 211 - arg := c.CallContext.Value(i) 211 + arg := c.BuiltinCallContext.Value(i) 212 212 x := value.Make(c.ctx, arg) 213 213 var mant big.Int 214 214 exp, err := x.MantExp(&mant) ··· 227 227 } 228 228 229 229 func (c *CallCtxt) String(i int) string { 230 - arg := c.CallContext.Value(i) 230 + arg := c.BuiltinCallContext.Value(i) 231 231 if str, _ := c.ctx.EvaluateKeepState(arg).(*adt.String); str != nil { 232 232 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 233 233 return str.Str ··· 242 242 } 243 243 244 244 func (c *CallCtxt) Bytes(i int) []byte { 245 - arg := c.CallContext.Value(i) 245 + arg := c.BuiltinCallContext.Value(i) 246 246 if bs, _ := c.ctx.EvaluateKeepState(arg).(*adt.Bytes); bs != nil { 247 247 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 248 248 return bs.B ··· 257 257 } 258 258 259 259 func (c *CallCtxt) Reader(i int) io.Reader { 260 - arg := c.CallContext.Value(i) 260 + arg := c.BuiltinCallContext.Value(i) 261 261 x := value.Make(c.ctx, arg) 262 262 // TODO: optimize for string and bytes cases 263 263 r, err := x.Reader() ··· 269 269 } 270 270 271 271 func (c *CallCtxt) Bool(i int) bool { 272 - arg := c.CallContext.Value(i) 272 + arg := c.BuiltinCallContext.Value(i) 273 273 if b, _ := c.ctx.EvaluateKeepState(arg).(*adt.Bool); b != nil { 274 274 // In the happy path, avoid converting to the public [cue.Value] API, which is wasteful. 275 275 return b.B ··· 284 284 } 285 285 286 286 func (c *CallCtxt) List(i int) (a []cue.Value) { 287 - arg := c.CallContext.Value(i) 287 + arg := c.BuiltinCallContext.Value(i) 288 288 x := value.Make(c.ctx, arg) 289 289 v, err := x.List() 290 290 if err != nil { ··· 306 306 } 307 307 308 308 func (c *CallCtxt) Iter(i int) (a cue.Iterator) { 309 - arg := c.CallContext.Value(i) 309 + arg := c.BuiltinCallContext.Value(i) 310 310 x := value.Make(c.ctx, arg) 311 311 v, err := x.List() 312 312 if err != nil { ··· 316 316 } 317 317 318 318 func (c *CallCtxt) getList(i int) *adt.Vertex { 319 - x := c.CallContext.Value(i) 319 + x := c.BuiltinCallContext.Value(i) 320 320 if c.builtin.NonConcrete { 321 321 x = adt.Default(x) 322 322 }