this repo has no description
0
fork

Configure Feed

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

internal/core: render predeclared ranged numeric types by name

Error messages for values like int16 or float32 previously printed
their raw expansion (e.g. `int & >=-32768 & <=32767`), while the
plain-kind builtins int and float rendered by name. The two cases
diverged because the debug compact printer has a BasicType case but
no Conjunction collapse, and the export package owned the only
table mapping ranges back to a predeclared name.

Lift that table and a small matcher into internal/core/adt so both
export (via boundSimplifier) and debug (via compactNode) can share it,
and teach the compact Conjunction case to emit the name when a
conjunction matches int8..int128, uint, uint8..uint128, or float32..64.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: I925ea80e646f5f5bce6cf0664c2fd0da4cbd1312
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1236171
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>

+181 -73
+5 -5
cmd/cue/cmd/testdata/script/numeric_ranges.txtar
··· 26 26 f64: float64 & "foo" 27 27 un: uint & "foo" 28 28 -- expect-stderr -- 29 - f32: conflicting values >=-340282346638528859811704183484516925440 & <=340282346638528859811704183484516925440 and "foo" (mismatched types number and string): 29 + f32: conflicting values float32 and "foo" (mismatched types number and string): 30 30 ./bad.cue:3:16 31 - f64: conflicting values >=-1.797693134862315708145274237317043567981E+308 & <=1.797693134862315708145274237317043567981E+308 and "foo" (mismatched types number and string): 31 + f64: conflicting values float64 and "foo" (mismatched types number and string): 32 32 ./bad.cue:4:16 33 - i16: conflicting values int & >=-32768 & <=32767 and "foo" (mismatched types int and string): 33 + i16: conflicting values int16 and "foo" (mismatched types int and string): 34 34 ./bad.cue:1:14 35 - u8: conflicting values int & >=0 & <=255 and "foo" (mismatched types int and string): 35 + u8: conflicting values uint8 and "foo" (mismatched types int and string): 36 36 ./bad.cue:2:14 37 - un: conflicting values int & >=0 and "foo" (mismatched types int and string): 37 + un: conflicting values uint and "foo" (mismatched types int and string): 38 38 ./bad.cue:5:13
+1 -1
cue/testdata/interpolation/issue487.txtar
··· 21 21 a: #R & {pos: "a"} @test(eq, {pos: "a", name: "hello_a"}) @test(closed) 22 22 } 23 23 -- out/errors.txt -- 24 - [incomplete] t1.#R.name: invalid interpolation: non-concrete value >=0 & int (type int): 24 + [incomplete] t1.#R.name: invalid interpolation: non-concrete value uint (type int): 25 25 ./in.cue:4:9 26 26 [incomplete] t2.#R.name: invalid interpolation: non-concrete value string (type string): 27 27 ./in.cue:11:9
+132
internal/core/adt/builtinrange.go
··· 1 + // Copyright 2026 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + package adt 16 + 17 + import "github.com/cockroachdb/apd/v3" 18 + 19 + // BuiltinRange describes a predeclared ranged numeric type such as int16 or 20 + // float32. 21 + type BuiltinRange struct { 22 + Name string 23 + Lo *apd.Decimal 24 + Hi *apd.Decimal 25 + } 26 + 27 + func mustDec(s string) *apd.Decimal { 28 + d, _, err := apd.NewFromString(s) 29 + if err != nil { 30 + panic(err) 31 + } 32 + return d 33 + } 34 + 35 + // IntBuiltinRanges lists the predeclared sized integer types. 36 + var IntBuiltinRanges = []BuiltinRange{ 37 + {"int8", mustDec("-128"), mustDec("127")}, 38 + {"int16", mustDec("-32768"), mustDec("32767")}, 39 + {"int32", mustDec("-2147483648"), mustDec("2147483647")}, 40 + {"int64", mustDec("-9223372036854775808"), mustDec("9223372036854775807")}, 41 + {"int128", 42 + mustDec("-170141183460469231731687303715884105728"), 43 + mustDec("170141183460469231731687303715884105727")}, 44 + 45 + {"uint8", mustDec("0"), mustDec("255")}, 46 + {"uint16", mustDec("0"), mustDec("65535")}, 47 + {"uint32", mustDec("0"), mustDec("4294967295")}, 48 + {"uint64", mustDec("0"), mustDec("18446744073709551615")}, 49 + {"uint128", mustDec("0"), mustDec("340282366920938463463374607431768211455")}, 50 + } 51 + 52 + // FloatBuiltinRanges lists the predeclared sized float types. 53 + var FloatBuiltinRanges = []BuiltinRange{ 54 + // 2**127 * (2**24 - 1) / 2**23 55 + {"float32", 56 + mustDec("-3.40282346638528859811704183484516925440e+38"), 57 + mustDec("3.40282346638528859811704183484516925440e+38")}, 58 + 59 + // 2**1023 * (2**53 - 1) / 2**52 60 + {"float64", 61 + mustDec("-1.797693134862315708145274237317043567981e+308"), 62 + mustDec("1.797693134862315708145274237317043567981e+308")}, 63 + } 64 + 65 + // MatchBuiltinRange reports the predeclared identifier name (e.g. "int16", 66 + // "uint8", "float32", "uint") that a Conjunction precisely represents, or the 67 + // empty string if it does not represent any such type. 68 + // 69 + // It recognises the shapes produced by the predeclared compiler for ranged 70 + // numeric types: 71 + // 72 + // - sized int types: {BasicType{Int}, BoundValue{>=lo}, BoundValue{<=hi}} 73 + // - sized uint types: {BasicType{Int}, BoundValue{>=0}, BoundValue{<=hi}} 74 + // - sized float types: {BoundValue{>=lo}, BoundValue{<=hi}} 75 + // - uint: {BasicType{Int}, BoundValue{>=0}} 76 + // 77 + // The order of values is not significant. 78 + func MatchBuiltinRange(c *Conjunction) string { 79 + var hasInt bool 80 + var lo, hi *Num 81 + for _, v := range c.Values { 82 + switch x := v.(type) { 83 + case *BasicType: 84 + if x.K != IntKind || hasInt { 85 + return "" 86 + } 87 + hasInt = true 88 + case *BoundValue: 89 + n, ok := x.Value.(*Num) 90 + if !ok { 91 + return "" 92 + } 93 + switch x.Op { 94 + case GreaterEqualOp: 95 + if lo != nil { 96 + return "" 97 + } 98 + lo = n 99 + case LessEqualOp: 100 + if hi != nil { 101 + return "" 102 + } 103 + hi = n 104 + default: 105 + return "" 106 + } 107 + default: 108 + return "" 109 + } 110 + } 111 + 112 + if lo != nil && hi == nil && hasInt && lo.X.Sign() == 0 { 113 + return "uint" 114 + } 115 + if lo == nil || hi == nil { 116 + return "" 117 + } 118 + 119 + var ranges []BuiltinRange 120 + switch { 121 + case hasInt: 122 + ranges = IntBuiltinRanges 123 + default: 124 + ranges = FloatBuiltinRanges 125 + } 126 + for _, r := range ranges { 127 + if lo.X.Cmp(r.Lo) == 0 && hi.X.Cmp(r.Hi) == 0 { 128 + return r.Name 129 + } 130 + } 131 + return "" 132 + }
+1 -1
internal/core/convert/go_test.go
··· 385 385 C int8 `cue:"A+B"` 386 386 }{}, 387 387 // TODO: should B be marked as optional? 388 - want: "(struct){\n A: (_|_){\n // [incomplete] A: non-concrete value >=-128 & <=127 & int in operand to -:\n // <field:>:1:1\n // A: cannot reference optional field: B:\n // <field:>:1:3\n }\n B?: (_|_){\n // [incomplete] B: non-concrete value >=-128 & <=127 & int in operand to -:\n // <field:>:1:1\n }\n C: (_|_){\n // [incomplete] C: non-concrete value >=-128 & <=127 & int in operand to +:\n // <field:>:1:1\n // C: cannot reference optional field: B:\n // <field:>:1:3\n }\n}", 388 + want: "(struct){\n A: (_|_){\n // [incomplete] A: non-concrete value int8 in operand to -:\n // <field:>:1:1\n // A: cannot reference optional field: B:\n // <field:>:1:3\n }\n B?: (_|_){\n // [incomplete] B: non-concrete value int8 in operand to -:\n // <field:>:1:1\n }\n C: (_|_){\n // [incomplete] C: non-concrete value int8 in operand to +:\n // <field:>:1:1\n // C: cannot reference optional field: B:\n // <field:>:1:3\n }\n}", 389 389 }, { 390 390 goTyp: []string{}, 391 391 want: `((null|list)){ |(*(null){ null }, (list){
+6
internal/core/debug/compact.go
··· 330 330 w.string(")") 331 331 332 332 case *adt.Conjunction: 333 + if w.compactBuiltins { 334 + if name := adt.MatchBuiltinRange(x); name != "" { 335 + w.string(name) 336 + break 337 + } 338 + } 333 339 for i, c := range x.Values { 334 340 if i > 0 { 335 341 w.string(" & ")
+25 -11
internal/core/debug/debug.go
··· 46 46 // Error arguments are always rendered compactly, regardless of this flag. 47 47 Compact bool 48 48 49 + // CompactBuiltins, when set together with Compact, collapses conjunctions 50 + // that precisely represent a predeclared ranged numeric type back to that 51 + // name (e.g. {int, >=-128, <=127} renders as "int8"). It is always enabled 52 + // when rendering error arguments. 53 + CompactBuiltins bool 54 + 49 55 // Raw, when set in combination with Compact, prints a Vertex as the 50 56 // conjunction of its original conjuncts rather than its evaluated value, 51 57 // unless the Vertex already holds concrete data. ··· 62 68 if config == nil { 63 69 config = &Config{} 64 70 } 65 - p := printer{dst: dst, index: i, cfg: config, compact: config.Compact} 71 + p := printer{ 72 + dst: dst, 73 + index: i, 74 + cfg: config, 75 + compact: config.Compact, 76 + compactBuiltins: config.CompactBuiltins, 77 + } 66 78 p.node(n) 67 79 return p.dst 68 80 } ··· 77 89 } 78 90 79 91 type printer struct { 80 - dst []byte 81 - index adt.StringIndexer 82 - indent string 83 - cfg *Config 84 - compact bool // copied from config.Compact 92 + dst []byte 93 + index adt.StringIndexer 94 + indent string 95 + cfg *Config 96 + compact bool // copied from config.Compact 97 + compactBuiltins bool // copied from config.CompactBuiltins 85 98 86 99 // keep track of vertices to avoid cycles. 87 100 stack []*adt.Vertex ··· 130 143 131 144 func (f formatter) String() string { 132 145 p := printer{ 133 - dst: make([]byte, 0, 128), 134 - index: f.r, 135 - cfg: f.p.cfg, 136 - compact: true, // Always compact for error arguments. 137 - stack: f.p.stack, 146 + dst: make([]byte, 0, 128), 147 + index: f.r, 148 + cfg: f.p.cfg, 149 + compact: true, // Always compact for error arguments. 150 + compactBuiltins: true, 151 + stack: f.p.stack, 138 152 } 139 153 p.node(f.x) 140 154 return string(p.dst)
+1 -1
internal/core/eval/eval.go
··· 55 55 return NewContext(e.r, v) 56 56 } 57 57 58 - var printConfig = &debug.Config{Compact: true} 58 + var printConfig = &debug.Config{Compact: true, CompactBuiltins: true} 59 59 60 60 func nodeFormat(r adt.Runtime, n adt.Node) string { 61 61 return debug.NodeString(r, n, printConfig)
+10 -54
internal/core/export/bounds.go
··· 17 17 import ( 18 18 "cuelang.org/go/cue/ast" 19 19 "cuelang.org/go/internal/core/adt" 20 - "github.com/cockroachdb/apd/v3" 21 20 ) 22 21 23 22 // boundSimplifier simplifies bound values into predeclared identifiers, if ··· 87 86 return false 88 87 } 89 88 90 - type builtinRange struct { 91 - typ string 92 - lo *apd.Decimal 93 - hi *apd.Decimal 94 - } 95 - 96 - func makeDec(s string) *apd.Decimal { 97 - d, _, err := apd.NewFromString(s) 98 - if err != nil { 99 - panic(err) 100 - } 101 - return d 102 - } 103 - 104 89 func (s *boundSimplifier) expr(ctx *adt.OpContext) (e ast.Expr) { 105 90 if s.min == nil || s.max == nil { 106 91 return nil 107 92 } 108 93 switch { 109 94 case s.isInt: 110 - t := s.matchRange(intRanges) 95 + t := s.matchRange(adt.IntBuiltinRanges) 111 96 if t != "" { 112 97 e = ast.NewIdent(t) 113 98 break ··· 124 109 } 125 110 fallthrough 126 111 default: 127 - t := s.matchRange(floatRanges) 112 + t := s.matchRange(adt.FloatBuiltinRanges) 128 113 if t != "" { 129 114 e = wrapBin(e, ast.NewIdent(t), adt.AndOp) 130 115 } ··· 139 124 return e 140 125 } 141 126 142 - func (s *boundSimplifier) matchRange(ranges []builtinRange) (t string) { 127 + func (s *boundSimplifier) matchRange(ranges []adt.BuiltinRange) (t string) { 143 128 for _, r := range ranges { 144 - if !s.minNum.X.IsZero() && s.min.Op == adt.GreaterEqualOp && s.minNum.X.Cmp(r.lo) == 0 { 145 - switch s.maxNum.X.Cmp(r.hi) { 129 + if !s.minNum.X.IsZero() && s.min.Op == adt.GreaterEqualOp && s.minNum.X.Cmp(r.Lo) == 0 { 130 + switch s.maxNum.X.Cmp(r.Hi) { 146 131 case 0: 147 132 if s.max.Op == adt.LessEqualOp { 148 133 s.max = nil 149 134 } 150 135 s.min = nil 151 - return r.typ 136 + return r.Name 152 137 case -1: 153 138 if !s.minNum.X.IsZero() { 154 139 s.min = nil 155 - return r.typ 140 + return r.Name 156 141 } 157 142 case 1: 158 143 } 159 - } else if s.max.Op == adt.LessEqualOp && s.maxNum.X.Cmp(r.hi) == 0 { 160 - switch s.minNum.X.Cmp(r.lo) { 144 + } else if s.max.Op == adt.LessEqualOp && s.maxNum.X.Cmp(r.Hi) == 0 { 145 + switch s.minNum.X.Cmp(r.Lo) { 161 146 case -1: 162 147 case 0: 163 148 if s.min.Op == adt.GreaterEqualOp { ··· 166 151 fallthrough 167 152 case 1: 168 153 s.max = nil 169 - return r.typ 154 + return r.Name 170 155 } 171 156 } 172 157 } 173 158 return "" 174 - } 175 - 176 - var intRanges = []builtinRange{ 177 - {"int8", makeDec("-128"), makeDec("127")}, 178 - {"int16", makeDec("-32768"), makeDec("32767")}, 179 - {"int32", makeDec("-2147483648"), makeDec("2147483647")}, 180 - {"int64", makeDec("-9223372036854775808"), makeDec("9223372036854775807")}, 181 - {"int128", makeDec("-170141183460469231731687303715884105728"), 182 - makeDec("170141183460469231731687303715884105727")}, 183 - 184 - {"uint8", makeDec("0"), makeDec("255")}, 185 - {"uint16", makeDec("0"), makeDec("65535")}, 186 - {"uint32", makeDec("0"), makeDec("4294967295")}, 187 - {"uint64", makeDec("0"), makeDec("18446744073709551615")}, 188 - {"uint128", makeDec("0"), makeDec("340282366920938463463374607431768211455")}, 189 - 190 - // {"rune", makeDec("0"), makeDec(strconv.Itoa(0x10FFFF))}, 191 - } 192 - 193 - var floatRanges = []builtinRange{ 194 - // 2**127 * (2**24 - 1) / 2**23 195 - {"float32", 196 - makeDec("-3.40282346638528859811704183484516925440e+38"), 197 - makeDec("3.40282346638528859811704183484516925440e+38")}, 198 - 199 - // 2**1023 * (2**53 - 1) / 2**52 200 - {"float64", 201 - makeDec("-1.797693134862315708145274237317043567981e+308"), 202 - makeDec("1.797693134862315708145274237317043567981e+308")}, 203 159 } 204 160 205 161 func wrapBin(a, b ast.Expr, op adt.Op) ast.Expr {