Skip to content

Commit f62bfed

Browse files
committed
internal/core/adt: clean up Builtin and Validator semantics
- A Builtin now no longer validates - Instead, the evaluator explicitly converts Builtins to BuiltinValidators when appropriate - Builtins now have the "func" type. This is because their return type is not known (depends on how it is used). - Builtin implementations may now return *Bottom, insta-promoting them to validators (not strictly necessary, but they make no sense as a builtin per se). Fixes #603 Change-Id: Id72d7088fa1cea71b0b606ca7252399bea3518c1 Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7884 Reviewed-by: CUE cueckoo <[email protected]> Reviewed-by: Marcel van Lohuizen <[email protected]>
1 parent e841714 commit f62bfed

File tree

17 files changed

+389
-75
lines changed

17 files changed

+389
-75
lines changed

cue/builtin_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ func TestBuiltins(t *testing.T) {
104104
`{a:1}`,
105105
}, {
106106
test("struct", `struct.MinFields(2) & {a: 1}`),
107-
`_|_(invalid value {a:1} (does not satisfy struct.MinFields(2)))`,
107+
// TODO: original value may be better.
108+
// `_|_(invalid value {a:1} (does not satisfy struct.MinFields(2)))`,
109+
`_|_(struct has 1 fields < MinFields(2))`,
108110
}, {
109111
test("time", `time.Time & "1937-01-01T12:00:27.87+00:20"`),
110112
`"1937-01-01T12:00:27.87+00:20"`,
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
-- in.cue --
2+
import (
3+
"struct"
4+
"encoding/json"
5+
)
6+
7+
8+
// non-monotonic builtins must fail with an "incomplete" error if there
9+
// is a possibility the constraint can get resolved by becoming more specific.
10+
incompleteError1: {
11+
MyType: {
12+
kv: struct.MinFields(1)
13+
}
14+
15+
foo: MyType & {
16+
kv: joel: "testing"
17+
}
18+
}
19+
20+
incompleteError2: {
21+
MyType: {
22+
kv: [string]: string
23+
kv: struct.MinFields(1)
24+
}
25+
26+
foo: MyType & {
27+
kv: joel: "testing"
28+
}
29+
}
30+
31+
incompleteError3: {
32+
t: string
33+
t: json.Validate(string)
34+
}
35+
36+
uniqueConstrains1: {
37+
t: string
38+
t: json.Validate(string)
39+
t: json.Validate(string)
40+
}
41+
42+
uniqueConstrains2: {
43+
t: struct.MaxFields(1)
44+
t: struct.MaxFields(1)
45+
}
46+
47+
violation: {
48+
#MyType: {
49+
kv: [string]: string
50+
kv: struct.MinFields(1)
51+
}
52+
53+
foo: #MyType & {
54+
kv: joel: "testing"
55+
kv: tony: "testing"
56+
}
57+
}
58+
59+
conjuncts: {
60+
kv: struct.MinFields(1)
61+
kv: struct.MaxFields(3)
62+
}
63+
64+
// TODO: stripe off conflicting pairs
65+
// conflicting: {
66+
// kv: struct.MinFields(3)
67+
// kv: struct.MaxFields(1)
68+
// }
69+
70+
// Builtins with bool return that can be used as validator.
71+
72+
bareBuiltin: {
73+
a: json.Valid
74+
a: json.Valid
75+
}
76+
77+
bareBuiltinCheck: {
78+
a: json.Valid
79+
a: "3"
80+
}
81+
82+
builtinValidator: {
83+
a: json.Valid()
84+
a: json.Valid()
85+
}
86+
87+
builtinValidatorCheck: {
88+
a: json.Valid()
89+
a: "3"
90+
}
91+
92+
callOfCallToValidator: {
93+
a: json.Valid
94+
b: a()
95+
e: b() // not allowed
96+
e: "5"
97+
}
98+
99+
validatorAsFunction: {
100+
a: json.Valid
101+
b: a("3")
102+
c: json.Valid("3")
103+
}
104+
105+
-- out/eval --
106+
Errors:
107+
callOfCallToValidator.e: cannot call previously called validator b:
108+
./in.cue:94:8
109+
110+
Result:
111+
(_|_){
112+
// [eval]
113+
incompleteError1: (struct){
114+
MyType: (struct){
115+
kv: (struct){ struct.MinFields(1) }
116+
}
117+
foo: (struct){
118+
kv: (struct){
119+
joel: (string){ "testing" }
120+
}
121+
}
122+
}
123+
incompleteError2: (struct){
124+
MyType: (struct){
125+
kv: (_|_){
126+
// [incomplete] struct has 0 fields < MinFields(1)
127+
}
128+
}
129+
foo: (struct){
130+
kv: (struct){
131+
joel: (string){ "testing" }
132+
}
133+
}
134+
}
135+
incompleteError3: (struct){
136+
t: (string){ &("encoding/json".Validate(string), string) }
137+
}
138+
uniqueConstrains1: (struct){
139+
t: (string){ &("encoding/json".Validate(string), string) }
140+
}
141+
uniqueConstrains2: (struct){
142+
t: (struct){ struct.MaxFields(1) }
143+
}
144+
violation: (struct){
145+
#MyType: (#struct){
146+
kv: (_|_){
147+
// [incomplete] struct has 0 fields < MinFields(1)
148+
}
149+
}
150+
foo: (#struct){
151+
kv: (#struct){
152+
joel: (string){ "testing" }
153+
tony: (string){ "testing" }
154+
}
155+
}
156+
}
157+
conjuncts: (struct){
158+
kv: (struct){ &(struct.MinFields(1), struct.MaxFields(3)) }
159+
}
160+
bareBuiltin: (struct){
161+
a: ((string|bytes)){ "encoding/json".Valid() }
162+
}
163+
bareBuiltinCheck: (struct){
164+
a: (string){ "3" }
165+
}
166+
builtinValidator: (struct){
167+
a: ((string|bytes)){ "encoding/json".Valid() }
168+
}
169+
builtinValidatorCheck: (struct){
170+
a: (string){ "3" }
171+
}
172+
callOfCallToValidator: (_|_){
173+
// [eval]
174+
a: ((string|bytes)){ "encoding/json".Valid() }
175+
b: ((string|bytes)){ "encoding/json".Valid() }
176+
e: (_|_){
177+
// [eval] callOfCallToValidator.e: cannot call previously called validator b:
178+
// ./in.cue:94:8
179+
}
180+
}
181+
validatorAsFunction: (struct){
182+
a: ((string|bytes)){ "encoding/json".Valid() }
183+
b: (bool){ true }
184+
c: (bool){ true }
185+
}
186+
}
187+
-- out/compile --
188+
--- in.cue
189+
{
190+
incompleteError1: {
191+
MyType: {
192+
kv: 〈import;struct〉.MinFields(1)
193+
}
194+
foo: (〈0;MyType〉 & {
195+
kv: {
196+
joel: "testing"
197+
}
198+
})
199+
}
200+
incompleteError2: {
201+
MyType: {
202+
kv: {
203+
[string]: string
204+
}
205+
kv: 〈import;struct〉.MinFields(1)
206+
}
207+
foo: (〈0;MyType〉 & {
208+
kv: {
209+
joel: "testing"
210+
}
211+
})
212+
}
213+
incompleteError3: {
214+
t: string
215+
t: 〈import;"encoding/json"〉.Validate(string)
216+
}
217+
uniqueConstrains1: {
218+
t: string
219+
t: 〈import;"encoding/json"〉.Validate(string)
220+
t: 〈import;"encoding/json"〉.Validate(string)
221+
}
222+
uniqueConstrains2: {
223+
t: 〈import;struct〉.MaxFields(1)
224+
t: 〈import;struct〉.MaxFields(1)
225+
}
226+
violation: {
227+
#MyType: {
228+
kv: {
229+
[string]: string
230+
}
231+
kv: 〈import;struct〉.MinFields(1)
232+
}
233+
foo: (〈0;#MyType〉 & {
234+
kv: {
235+
joel: "testing"
236+
}
237+
kv: {
238+
tony: "testing"
239+
}
240+
})
241+
}
242+
conjuncts: {
243+
kv: 〈import;struct〉.MinFields(1)
244+
kv: 〈import;struct〉.MaxFields(3)
245+
}
246+
bareBuiltin: {
247+
a: 〈import;"encoding/json"〉.Valid
248+
a: 〈import;"encoding/json"〉.Valid
249+
}
250+
bareBuiltinCheck: {
251+
a: 〈import;"encoding/json"〉.Valid
252+
a: "3"
253+
}
254+
builtinValidator: {
255+
a: 〈import;"encoding/json"〉.Valid()
256+
a: 〈import;"encoding/json"〉.Valid()
257+
}
258+
builtinValidatorCheck: {
259+
a: 〈import;"encoding/json"〉.Valid()
260+
a: "3"
261+
}
262+
callOfCallToValidator: {
263+
a: 〈import;"encoding/json"〉.Valid
264+
b: 〈0;a〉()
265+
e: 〈0;b〉()
266+
e: "5"
267+
}
268+
validatorAsFunction: {
269+
a: 〈import;"encoding/json"〉.Valid
270+
b: 〈0;a〉("3")
271+
c: 〈import;"encoding/json"〉.Valid("3")
272+
}
273+
}

cue/testdata/eval/issue545.txtar

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ t2: {
6969
a: (string){ =~"foo" }
7070
b: (string){ =~"foo" }
7171
c: (string){ =~"foo" }
72-
d: (string){ time.Time }
72+
d: (string){ time.Time() }
7373
e: (string){ time.Time() }
74-
f: (string){ time.Time }
74+
f: (string){ time.Time() }
7575
}
7676
}
7777
-- out/compile --

cue/testdata/resolve/011_bounds.txtar

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ e9: _|_ // conflicting values >"a" and <1 (mismatched types string and number)
138138
}
139139
-- out/eval --
140140
Errors:
141-
e1: conflicting values null and !=null (mismatched types null and (bool|string|bytes|list|struct|number)):
141+
e1: conflicting values null and !=null (mismatched types null and (bool|string|bytes|func|list|struct|number)):
142142
./in.cue:40:5
143143
./in.cue:40:12
144-
e2: conflicting values !=null and null (mismatched types (bool|string|bytes|list|struct|number) and null):
144+
e2: conflicting values !=null and null (mismatched types (bool|string|bytes|func|list|struct|number) and null):
145145
./in.cue:41:5
146146
./in.cue:41:14
147147
e5: incompatible bounds >1 and <0:
@@ -200,12 +200,12 @@ Result:
200200
s23e: (number){ &(>0.0, <2.0) }
201201
s30: (int){ &(>0, int) }
202202
e1: (_|_){
203-
// [eval] e1: conflicting values null and !=null (mismatched types null and (bool|string|bytes|list|struct|number)):
203+
// [eval] e1: conflicting values null and !=null (mismatched types null and (bool|string|bytes|func|list|struct|number)):
204204
// ./in.cue:40:5
205205
// ./in.cue:40:12
206206
}
207207
e2: (_|_){
208-
// [eval] e2: conflicting values !=null and null (mismatched types (bool|string|bytes|list|struct|number) and null):
208+
// [eval] e2: conflicting values !=null and null (mismatched types (bool|string|bytes|func|list|struct|number) and null):
209209
// ./in.cue:41:5
210210
// ./in.cue:41:14
211211
}

encoding/openapi/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ func (b *builder) array(v cue.Value) {
792792
case cue.CallOp:
793793
name := fmt.Sprint(a[0])
794794
switch name {
795-
case "list.UniqueItems":
795+
case "list.UniqueItems", "list.UniqueItems()":
796796
b.checkArgs(a, 0)
797797
b.setFilter("Schema", "uniqueItems", ast.NewBool(true))
798798
return

encoding/openapi/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var cueToOpenAPI = map[string]string{
3636

3737
"bytes": "binary",
3838

39+
"time.Time()": "date-time",
3940
"time.Time": "date-time",
4041
`time.Format ("2006-01-02")`: "date",
4142

internal/core/adt/adt.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,7 @@ func (*Conjunction) Concreteness() Concreteness { return Constraint }
162162
func (*Disjunction) Concreteness() Concreteness { return Constraint }
163163
func (*BoundValue) Concreteness() Concreteness { return Constraint }
164164

165-
// Constraint only applies if Builtin is used as constraint.
166-
func (*Builtin) Concreteness() Concreteness { return Constraint }
165+
func (*Builtin) Concreteness() Concreteness { return Concrete }
167166
func (*BuiltinValidator) Concreteness() Concreteness { return Constraint }
168167

169168
// Value and Expr

0 commit comments

Comments
 (0)