internal/core/eval: centralize type checking
This aims to make error checking more correct, easier
to manage and improve error messages.
Change-Id: I00bfd0742bb90549679eaa63894c30b1092a228d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6702
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/testdata/basicrewrite/001_regexp.txtar b/cue/testdata/basicrewrite/001_regexp.txtar
index 489ed68..52ce85a 100644
--- a/cue/testdata/basicrewrite/001_regexp.txtar
+++ b/cue/testdata/basicrewrite/001_regexp.txtar
@@ -62,7 +62,7 @@
}
-- out/eval --
Errors:
-e3: invalid value <5 (mismatched types number and string)
+e3: conflicting values !="a" and <5 (mismatched types string and number)
b3: invalid value "foo" (out of bound =~"[a-z]{4}"):
./in.cue:10:5
e1: cannot use 1 (type int) as type (string|bytes):
@@ -95,6 +95,6 @@
// ./in.cue:19:5
}
e3: (_|_){
- // [eval] e3: invalid value <5 (mismatched types number and string)
+ // [eval] e3: conflicting values !="a" and <5 (mismatched types string and number)
}
}
diff --git a/cue/testdata/basicrewrite/006_basic_type.txtar b/cue/testdata/basicrewrite/006_basic_type.txtar
index ed33d28..ac0aa54 100644
--- a/cue/testdata/basicrewrite/006_basic_type.txtar
+++ b/cue/testdata/basicrewrite/006_basic_type.txtar
@@ -34,7 +34,7 @@
}
-- out/eval --
Errors:
-d: invalid value float (mismatched types float and int)
+d: conflicting values int and float (mismatched types int and float)
Result:
(_|_){
@@ -43,7 +43,7 @@
b: (int){ 1 }
c: (float){ 1.0 }
d: (_|_){
- // [eval] d: invalid value float (mismatched types float and int)
+ // [eval] d: conflicting values int and float (mismatched types int and float)
}
e: (string){ "4" }
f: (bool){ true }
diff --git a/cue/testdata/basicrewrite/010_lists.txtar b/cue/testdata/basicrewrite/010_lists.txtar
index 4f777fd..c46ccba 100644
--- a/cue/testdata/basicrewrite/010_lists.txtar
+++ b/cue/testdata/basicrewrite/010_lists.txtar
@@ -76,7 +76,7 @@
}
-- out/eval --
Errors:
-e: conflicting types list and int
+e: conflicting values 4 and [] (mismatched types int and list)
e2: invalid list index d (type string):
./in.cue:5:12
e3: invalid negative index -1:
@@ -101,7 +101,7 @@
2: (int){ 3 }
}
e: (_|_){
- // [eval] e: conflicting types list and int
+ // [eval] e: conflicting values 4 and [] (mismatched types int and list)
}
e2: (_|_){
// [eval] e2: invalid list index d (type string):
diff --git a/cue/testdata/basicrewrite/013_obj_unify.txtar b/cue/testdata/basicrewrite/013_obj_unify.txtar
index 15eb6f7..0dcbbe8 100644
--- a/cue/testdata/basicrewrite/013_obj_unify.txtar
+++ b/cue/testdata/basicrewrite/013_obj_unify.txtar
@@ -75,7 +75,7 @@
}
-- out/eval --
Errors:
-e: conflicting values struct and int
+e: conflicting values 1 and {a:3} (mismatched types int and struct)
Result:
(_|_){
@@ -97,7 +97,7 @@
b: (int){ 2 }
}
e: (_|_){
- // [eval] e: conflicting values struct and int
+ // [eval] e: conflicting values 1 and {a:3} (mismatched types int and struct)
a: (int){ 3 }
}
}
diff --git a/cue/testdata/basicrewrite/015_types.txtar b/cue/testdata/basicrewrite/015_types.txtar
index c9b218b..bea293c 100644
--- a/cue/testdata/basicrewrite/015_types.txtar
+++ b/cue/testdata/basicrewrite/015_types.txtar
@@ -39,13 +39,13 @@
}
-- out/eval --
Errors:
-e: invalid value string (mismatched types string and int)
-e2: invalid value string (mismatched types string and int)
+e: conflicting values int and string (mismatched types int and string)
+e2: conflicting values 1 and string (mismatched types int and string)
b: value can never become concrete:
./in.cue:7:5
-p: invalid operation ++true (+ bool):
+p: invalid operation +true (+ bool):
./in.cue:8:5
-m: invalid operation --false (- bool):
+m: invalid operation -false (- bool):
./in.cue:9:5
Result:
@@ -56,21 +56,21 @@
s: (string){ string }
t: (string){ "s" }
e: (_|_){
- // [eval] e: invalid value string (mismatched types string and int)
+ // [eval] e: conflicting values int and string (mismatched types int and string)
}
e2: (_|_){
- // [eval] e2: invalid value string (mismatched types string and int)
+ // [eval] e2: conflicting values 1 and string (mismatched types int and string)
}
b: (_|_){
// [eval] b: value can never become concrete:
// ./in.cue:7:5
}
p: (_|_){
- // [eval] p: invalid operation ++true (+ bool):
+ // [eval] p: invalid operation +true (+ bool):
// ./in.cue:8:5
}
m: (_|_){
- // [eval] m: invalid operation --false (- bool):
+ // [eval] m: invalid operation -false (- bool):
// ./in.cue:9:5
}
}
diff --git a/cue/testdata/cycle/structural.txtar b/cue/testdata/cycle/structural.txtar
index 2324ceb..97b5575 100644
--- a/cue/testdata/cycle/structural.txtar
+++ b/cue/testdata/cycle/structural.txtar
@@ -220,7 +220,7 @@
a1.f.0: structural cycle
a3.f.g: structural cycle
b4.x.y.0: structural cycle
-b6.b.a.0: conflicting types list and int
+b6.b.a.0: conflicting values 1 and [1] (mismatched types int and list)
b6.b.a.0.0: structural cycle
b6.x.a.0: structural cycle
b7.a.0: structural cycle
@@ -234,16 +234,18 @@
e1.b.c: structural cycle
e2.a.c: structural cycle
e2.b.c: structural cycle
-e3.a: conflicting types list and struct
-e3.a.0: conflicting types list and struct
+e3.a: conflicting values [a] and {c:a} (mismatched types list and struct)
+e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct)
e3.a.0: structural cycle
-e3.a.c: conflicting types list and struct
+e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct)
e3.a.c: structural cycle
-e3.b: conflicting types list and struct
-e3.b.0: conflicting types list and struct
+e3.b: conflicting values [b] and {c:b} (mismatched types list and struct)
+e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct)
e3.b.0: structural cycle
-e3.b.c: conflicting types list and struct
+e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct)
e3.b.c: structural cycle
+e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct)
+e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct)
z1.z.f.h.h: structural cycle
z1.z.g.h: structural cycle
b4.x.y.0: structural cycle:
@@ -362,7 +364,7 @@
a: (_|_){
// [eval]
0: (_|_){
- // [eval] b6.b.a.0: conflicting types list and int
+ // [eval] b6.b.a.0: conflicting values 1 and [1] (mismatched types int and list)
0: (_|_){
// [structural cycle] b6.b.a.0.0: structural cycle
}
@@ -570,9 +572,9 @@
e3: (_|_){
// [eval]
a: (_|_){
- // [eval] e3.a: conflicting types list and struct
+ // [eval] e3.a: conflicting values [a] and {c:a} (mismatched types list and struct)
c: (_|_){
- // [eval] e3.a.c: conflicting types list and struct
+ // [eval] e3.a.c: conflicting values [a] and {c:a} (mismatched types list and struct)
// e3.a.c: structural cycle
c: (_|_){// 〈1;a〉
}
@@ -580,7 +582,7 @@
}
}
0: (_|_){
- // [eval] e3.a.0: conflicting types list and struct
+ // [eval] e3.a.0: conflicting values [a] and {c:a} (mismatched types list and struct)
// e3.a.0: structural cycle
c: (_|_){// 〈1;a〉
}
@@ -589,9 +591,9 @@
}
}
b: (_|_){
- // [eval] e3.b: conflicting types list and struct
+ // [eval] e3.b: conflicting values [b] and {c:b} (mismatched types list and struct)
c: (_|_){
- // [eval] e3.b.c: conflicting types list and struct
+ // [eval] e3.b.c: conflicting values [b] and {c:b} (mismatched types list and struct)
// e3.b.c: structural cycle
c: (_|_){// 〈1;b〉
}
@@ -599,7 +601,7 @@
}
}
0: (_|_){
- // [eval] e3.b.0: conflicting types list and struct
+ // [eval] e3.b.0: conflicting values [b] and {c:b} (mismatched types list and struct)
// e3.b.0: structural cycle
c: (_|_){// 〈1;b〉
}
@@ -608,16 +610,21 @@
}
}
}
- e4: (struct){
- a: (#list){
- 0: (#list){
+ e4: (_|_){
+ // [eval]
+ a: (_|_){
+ // [eval]
+ 0: (_|_){
+ // [eval] e4.a.0: conflicting values [{c:1}] and {} (mismatched types list and struct)
0: (struct){
c: (int){ 1 }
}
}
}
- b: (#list){
- 0: (#list){
+ b: (_|_){
+ // [eval]
+ 0: (_|_){
+ // [eval] e4.b.0: conflicting values [{c:1}] and {} (mismatched types list and struct)
0: (struct){
c: (int){ 1 }
}
diff --git a/cue/testdata/eval/disjunctions.txtar b/cue/testdata/eval/disjunctions.txtar
index fc6e002..ab8f9ac 100644
--- a/cue/testdata/eval/disjunctions.txtar
+++ b/cue/testdata/eval/disjunctions.txtar
@@ -15,9 +15,54 @@
e: b & { val: "foo" }
f: b & { name: "str", val: 3 }
+// Disjunct elimination based on type.
+e1: {
+ a: null | {bar: 2}
+ b: (a&{}).bar
+}
+
+d1: {
+ a: (null | {c: 1}) & {}
+ b: {} & (null | {c: 1})
+}
+
+d2: {
+ a: ([...] | {c: 1}) & {}
+ b: {} & ([...] | {c: 1})
+}
+
+d3: {
+ a: (string | {c: 1}) & {}
+ b: {} & (string | {c: 1})
+}
+
+d4: {
+ a: (string | {c: 1}) & {}
+ b: {} & (string | {c: 1})
+}
+
+d5: {
+ a: (number | {c: 1}) & {}
+ b: {} & (number | {c: 1})
+}
+
+d6: {
+ a: (int | {c: 1}) & {}
+ b: {} & (int | {c: 1})
+}
+
+
+d100: {
+ // Should we allow a selector to imply a struct or list? Would be convenient.
+ // This would be a spec change. Disallow for now.
+ // TODO(errors): better error message
+ i: null | {bar: 2}
+ j: i.bar
+}
+
-- out/eval --
Errors:
-f.val: invalid value string (mismatched types string and int)
+f.val: conflicting values 3 and string (mismatched types int and string)
Result:
(_|_){
@@ -47,7 +92,70 @@
// [eval]
name: (string){ "str" }
val: (_|_){
- // [eval] f.val: invalid value string (mismatched types string and int)
+ // [eval] f.val: conflicting values 3 and string (mismatched types int and string)
+ }
+ }
+ e1: (struct){
+ a: ((null|struct)){ |((null){ null }, (struct){
+ bar: (int){ 2 }
+ }) }
+ b: (int){ 2 }
+ }
+ d1: (struct){
+ a: (struct){
+ c: (int){ 1 }
+ }
+ b: (struct){
+ c: (int){ 1 }
+ }
+ }
+ d2: (struct){
+ a: (struct){
+ c: (int){ 1 }
+ }
+ b: (struct){
+ c: (int){ 1 }
+ }
+ }
+ d3: (struct){
+ a: (struct){
+ c: (int){ 1 }
+ }
+ b: (struct){
+ c: (int){ 1 }
+ }
+ }
+ d4: (struct){
+ a: (struct){
+ c: (int){ 1 }
+ }
+ b: (struct){
+ c: (int){ 1 }
+ }
+ }
+ d5: (struct){
+ a: (struct){
+ c: (int){ 1 }
+ }
+ b: (struct){
+ c: (int){ 1 }
+ }
+ }
+ d6: (struct){
+ a: (struct){
+ c: (int){ 1 }
+ }
+ b: (struct){
+ c: (int){ 1 }
+ }
+ }
+ d100: (struct){
+ i: ((null|struct)){ |((null){ null }, (struct){
+ bar: (int){ 2 }
+ }) }
+ j: (_|_){
+ // [incomplete] d100.j: incomplete feed source value i (type (null|struct)):
+ // ./in.cue:59:6
}
}
}
@@ -77,4 +185,68 @@
name: "str"
val: 3
})
+ e1: {
+ a: (null|{
+ bar: 2
+ })
+ b: (〈0;a〉 & {}).bar
+ }
+ d1: {
+ a: ((null|{
+ c: 1
+ }) & {})
+ b: ({} & (null|{
+ c: 1
+ }))
+ }
+ d2: {
+ a: (([
+ ...,
+ ]|{
+ c: 1
+ }) & {})
+ b: ({} & ([
+ ...,
+ ]|{
+ c: 1
+ }))
+ }
+ d3: {
+ a: ((string|{
+ c: 1
+ }) & {})
+ b: ({} & (string|{
+ c: 1
+ }))
+ }
+ d4: {
+ a: ((string|{
+ c: 1
+ }) & {})
+ b: ({} & (string|{
+ c: 1
+ }))
+ }
+ d5: {
+ a: ((number|{
+ c: 1
+ }) & {})
+ b: ({} & (number|{
+ c: 1
+ }))
+ }
+ d6: {
+ a: ((int|{
+ c: 1
+ }) & {})
+ b: ({} & (int|{
+ c: 1
+ }))
+ }
+ d100: {
+ i: (null|{
+ bar: 2
+ })
+ j: 〈0;i〉.bar
+ }
}
diff --git a/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar b/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar
index c640f19..bcedb40 100644
--- a/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar
+++ b/cue/testdata/fulleval/026_dont_convert_incomplete_errors_to_non-incomplete.txtar
@@ -55,11 +55,11 @@
}
}
n2: (_|_){
- // [incomplete] n2: operand -num of '-' not concrete (was number):
+ // [incomplete] n2: operand num of '-' not concrete (was number):
// ./in.cue:4:6
}
n3: (_|_){
- // [incomplete] n3: operand +num of '+' not concrete (was number):
+ // [incomplete] n3: operand num of '+' not concrete (was number):
// ./in.cue:5:6
}
n4: (_|_){
@@ -79,7 +79,7 @@
// ./in.cue:9:5
}
b1: (_|_){
- // [incomplete] b1: operand !is of '!' not concrete (was bool):
+ // [incomplete] b1: operand is of '!' not concrete (was bool):
// ./in.cue:11:6
}
s1: (_|_){
diff --git a/cue/testdata/references/labels.txtar b/cue/testdata/references/labels.txtar
index 829c79d..09ff03d 100644
--- a/cue/testdata/references/labels.txtar
+++ b/cue/testdata/references/labels.txtar
@@ -3,16 +3,16 @@
// direct
a: [X=string]: X
-a: bar: {}
+a: bar: _
// in struct
b: [X=string]: {X}
-b: bar: {}
+b: bar: _
// in structs
c: [X=string]: X
c: [Y=string]: {{{Y}}}
-c: bar: {}
+c: bar: _
// in sub field
d: [X=string]: name: X
@@ -20,7 +20,7 @@
// nested
e: [X=string]: [Y=string]: X + Y
-e: foo: bar: {}
+e: foo: bar: _
// Field aliases
@@ -45,7 +45,6 @@
}
c1: foo3: x: _
-
// TODO: support. Also not yet supported in old implementation.
// c10: {
// C=[string]: {
@@ -104,7 +103,7 @@
[string]: 〈0;-〉
}
a: {
- bar: {}
+ bar: _
}
b: {
[string]: {
@@ -112,7 +111,7 @@
}
}
b: {
- bar: {}
+ bar: _
}
c: {
[string]: 〈0;-〉
@@ -127,7 +126,7 @@
}
}
c: {
- bar: {}
+ bar: _
}
d: {
[string]: {
@@ -144,7 +143,7 @@
}
e: {
foo: {
- bar: {}
+ bar: _
}
}
bar: 3
diff --git a/cue/testdata/resolve/006_arithmetic.txtar b/cue/testdata/resolve/006_arithmetic.txtar
index 8ee0e1c..789a19a 100644
--- a/cue/testdata/resolve/006_arithmetic.txtar
+++ b/cue/testdata/resolve/006_arithmetic.txtar
@@ -30,7 +30,7 @@
}
-- out/eval --
Errors:
-e2: invalid value 2 (mismatched types float and int)
+e2: conflicting values int and 2 (mismatched types int and float)
Result:
(_|_){
@@ -41,6 +41,6 @@
v5: (float){ 2.0 }
v6: (float){ 1 }
e2: (_|_){
- // [eval] e2: invalid value 2 (mismatched types float and int)
+ // [eval] e2: conflicting values int and 2 (mismatched types int and float)
}
}
diff --git a/cue/testdata/resolve/011_bounds.txtar b/cue/testdata/resolve/011_bounds.txtar
index 134204f..4d7e3c8 100644
--- a/cue/testdata/resolve/011_bounds.txtar
+++ b/cue/testdata/resolve/011_bounds.txtar
@@ -138,13 +138,13 @@
}
-- out/eval --
Errors:
-e1: invalid value !=null (mismatched types (bool|string|bytes|list|struct|number) and null)
-e2: invalid value null (mismatched types null and (bool|string|bytes|list|struct|number))
+e1: conflicting values null and !=null (mismatched types null and (bool|string|bytes|list|struct|number))
+e2: conflicting values !=null and null (mismatched types (bool|string|bytes|list|struct|number) and null)
e5: incompatible bounds >1 and <0
e6: incompatible bounds >11 and <11
e7: incompatible bounds >=11 and <11
e8: incompatible bounds >11 and <=11
-e9: invalid value <1 (mismatched types number and string)
+e9: conflicting values >"a" and <1 (mismatched types string and number)
e3: invalid value 1 (out of bound >1):
./in.cue:42:5
e4: invalid value 0 (out of bound <0):
@@ -184,10 +184,10 @@
s23e: (number){ &(>0.0, <2.0) }
s30: (int){ &(>0, int) }
e1: (_|_){
- // [eval] e1: invalid value !=null (mismatched types (bool|string|bytes|list|struct|number) and null)
+ // [eval] e1: conflicting values null and !=null (mismatched types null and (bool|string|bytes|list|struct|number))
}
e2: (_|_){
- // [eval] e2: invalid value null (mismatched types null and (bool|string|bytes|list|struct|number))
+ // [eval] e2: conflicting values !=null and null (mismatched types (bool|string|bytes|list|struct|number) and null)
}
e3: (_|_){
// [eval] e3: invalid value 1 (out of bound >1):
@@ -210,6 +210,6 @@
// [eval] e8: incompatible bounds >11 and <=11
}
e9: (_|_){
- // [eval] e9: invalid value <1 (mismatched types number and string)
+ // [eval] e9: conflicting values >"a" and <1 (mismatched types string and number)
}
}
diff --git a/cue/testdata/resolve/012_bound_conversions.txtar b/cue/testdata/resolve/012_bound_conversions.txtar
index 396beea..15343ae 100644
--- a/cue/testdata/resolve/012_bound_conversions.txtar
+++ b/cue/testdata/resolve/012_bound_conversions.txtar
@@ -48,7 +48,7 @@
}
-- out/eval --
Errors:
-c4: invalid value int (mismatched types int and float)
+c4: conflicting values <2 and int (mismatched types float and int)
c1: invalid value 1.2 (out of bound >1.3):
./in.cue:10:12
c2: invalid value 1.2 (out of bound >1.3):
@@ -74,6 +74,6 @@
}
c3: (float){ 1.2 }
c4: (_|_){
- // [eval] c4: invalid value int (mismatched types int and float)
+ // [eval] c4: conflicting values <2 and int (mismatched types float and int)
}
}
diff --git a/cue/testdata/resolve/023_correct_error_messages.txtar b/cue/testdata/resolve/023_correct_error_messages.txtar
index fd66f0a..15e2296 100644
--- a/cue/testdata/resolve/023_correct_error_messages.txtar
+++ b/cue/testdata/resolve/023_correct_error_messages.txtar
@@ -15,12 +15,12 @@
}
-- out/eval --
Errors:
-a: invalid value 1 (mismatched types int and string)
+a: conflicting values "a" and 1 (mismatched types string and int)
Result:
(_|_){
// [eval]
a: (_|_){
- // [eval] a: invalid value 1 (mismatched types int and string)
+ // [eval] a: conflicting values "a" and 1 (mismatched types string and int)
}
}
diff --git a/cue/testdata/resolve/045_range_unification.txtar b/cue/testdata/resolve/045_range_unification.txtar
index 0b321cb..ad88f66 100644
--- a/cue/testdata/resolve/045_range_unification.txtar
+++ b/cue/testdata/resolve/045_range_unification.txtar
@@ -136,9 +136,8 @@
Errors:
b14: incompatible bounds >=6 and <=5
b7: incompatible bounds >=6 and <=5
-c3: invalid value <=5 (mismatched types number and string)
-c3: invalid value >=1 (mismatched types number and string)
-c4: invalid value string (mismatched types string and number)
+c3: conflicting values string and >=1 (mismatched types string and number)
+c4: conflicting values <=5 and string (mismatched types number and string)
n2: incompatible bounds >=1.1 and <=1.3
a4: invalid value 6 (out of bound <=5):
./in.cue:5:11
@@ -195,11 +194,10 @@
c1: (int){ &(>=1, <=5, int) }
c2: (int){ &(>=1, <=5, int) }
c3: (_|_){
- // [eval] c3: invalid value <=5 (mismatched types number and string)
- // c3: invalid value >=1 (mismatched types number and string)
+ // [eval] c3: conflicting values string and >=1 (mismatched types string and number)
}
c4: (_|_){
- // [eval] c4: invalid value string (mismatched types string and number)
+ // [eval] c4: conflicting values <=5 and string (mismatched types number and string)
}
s1: (string){ "e" }
s2: (string){ "ee" }
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 4bedc1e..8c980b6 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -790,10 +790,10 @@
}
if k&expectedKind != BottomKind {
c.addErrf(IncompleteError, pos(x.X),
- "operand %s of '%s' not concrete (was %s)", c.Str(x), op, k)
+ "operand %s of '%s' not concrete (was %s)", c.Str(x.X), op, k)
return nil
}
- return c.NewErrf("invalid operation %s%s (%s %s)", op, c.Str(x), op, k)
+ return c.NewErrf("invalid operation %s (%s %s)", c.Str(x), op, k)
}
// BinaryExpr is a binary expression.
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index a912c4b..896c983 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -424,10 +424,12 @@
for n.maybeSetCache(); n.expandOne(); n.maybeSetCache() {
}
- // TODO: preparation for association lists:
- // We assume that association types may not be created dynamically for now.
- // So we add lists
- n.addLists(ctx)
+ if aList := n.addLists(ctx); aList != nil {
+ n.updateNodeType(adt.ListKind, aList)
+ }
+ if n.aStruct != nil {
+ n.updateNodeType(adt.StructKind, n.aStruct)
+ }
switch err := n.getErr(); {
case err != nil:
@@ -473,37 +475,19 @@
// Either set to Conjunction or error.
var v adt.Value = n.node.Value
- kind := n.kind
+ // TODO: verify and simplify the below code to determine whether
+ // something is a struct.
markStruct := false
- if n.isStruct {
- if kind != 0 && kind&adt.StructKind == 0 {
- n.node.Value = n.ctx.NewErrf(
- "conflicting values struct and %s", n.kind)
- }
+ if n.aStruct != nil {
markStruct = true
} else if len(n.node.Structs) > 0 {
- markStruct = kind&adt.StructKind != 0 && !n.hasTop
+ markStruct = n.kind&adt.StructKind != 0 && !n.hasTop
}
if v == nil && markStruct {
- kind = adt.StructKind
n.node.Value = &adt.StructMarker{}
v = n.node
}
if v != nil && adt.IsConcrete(v) {
- if n.scalar != nil {
- kind = n.scalar.Kind()
- }
- if v.Kind()&^kind != 0 {
- p := token.NoPos
- if src := v.Source(); src != nil {
- p = src.Pos()
- }
- n.addErr(ctx.NewPosf(p,
- // TODO(errors): position of all value types.
- "conflicting types %v and %v",
- v.Kind(), n.kind,
- ))
- }
if n.lowerBound != nil {
if b := ctx.Validate(n.lowerBound, v); b != nil {
n.addBottom(b)
@@ -722,6 +706,7 @@
// Concrete conjuncts
kind adt.Kind
+ kindExpr adt.Expr // expr that adjust last value (for error reporting)
lowerBound *adt.BoundValue // > or >=
upperBound *adt.BoundValue // < or <=
checks []adt.Validator // BuiltinValidator, other bound values.
@@ -739,7 +724,7 @@
// - parent node is closing
needClose bool
openList bool
- isStruct bool
+ aStruct adt.Expr
hasTop bool
newClose *CloseDef
// closeID uint32 // from parent, or if not exist, new if introducing a def.
@@ -759,6 +744,32 @@
isFinal bool
}
+func (n *nodeContext) updateNodeType(k adt.Kind, v adt.Expr) bool {
+ ctx := n.ctx
+ kind := n.kind & k
+
+ switch {
+ case n.kind == adt.BottomKind,
+ k == adt.BottomKind:
+ return false
+
+ case kind == adt.BottomKind:
+ if n.kindExpr != nil {
+ n.addErr(ctx.Newf(
+ "conflicting values %s and %s (mismatched types %s and %s)",
+ ctx.Str(n.kindExpr), ctx.Str(v), n.kind, k))
+ } else {
+ n.addErr(ctx.Newf(
+ "conflicting value %s (mismatched types %s and %s)",
+ ctx.Str(v), n.kind, k))
+ }
+ }
+
+ n.kind = kind
+ n.kindExpr = v
+ return kind != adt.BottomKind
+}
+
func (n *nodeContext) done() bool {
return len(n.dynamicFields) == 0 &&
len(n.ifClauses) == 0 &&
@@ -825,9 +836,7 @@
// Src is the combined input.
v = &adt.BasicType{K: n.kind}
- // TODO: Change to isStruct?
if len(n.node.Structs) > 0 {
- // n.isStruct = true
v = structSentinel
}
@@ -1130,7 +1139,7 @@
if x, ok := v.(*adt.Vertex); ok {
needClose := false
if isStruct(x) {
- n.isStruct = true
+ n.aStruct = x
// TODO: find better way to mark as struct.
// For instance, we may want to add a faux
// Structlit for topological sort.
@@ -1167,6 +1176,9 @@
case *adt.StructMarker:
for _, a := range x.Arcs {
+ if a.Label.IsString() {
+ n.aStruct = x
+ }
// TODO, insert here as
n.insertField(a.Label, adt.MakeConjunct(nil, a))
// sub, _ := n.node.GetArc(a.Label)
@@ -1177,7 +1189,7 @@
n.addValueConjunct(env, v)
for _, a := range x.Arcs {
- // TODO, insert here as
+ // TODO(errors): report error when this is a regular field.
n.insertField(a.Label, adt.MakeConjunct(nil, a))
// sub, _ := n.node.GetArc(a.Label)
// sub.Add(a)
@@ -1193,16 +1205,11 @@
return
}
- ctx := n.ctx
- kind := n.kind & v.Kind()
- if kind == adt.BottomKind {
- // TODO: how to get other conflicting values?
- n.addErr(ctx.Newf(
- "invalid value %s (mismatched types %s and %s)",
- ctx.Str(v), v.Kind(), n.kind))
+ if !n.updateNodeType(v.Kind(), v) {
return
}
- n.kind = kind
+
+ ctx := n.ctx
switch x := v.(type) {
case *adt.Disjunction:
@@ -1219,6 +1226,7 @@
n.optionals = append(n.optionals, fieldSet{env: env, isOpen: true})
case *adt.BasicType:
+ // handled above
case *adt.BoundValue:
switch x.Op {
@@ -1321,6 +1329,7 @@
}
var hasOther, hasBulk adt.Node
+ hasEmbed := false
opt := fieldSet{pos: s, env: childEnv}
@@ -1331,9 +1340,13 @@
// handle in next iteration.
case *adt.OptionalField:
+ if x.Label.IsString() {
+ n.aStruct = s
+ }
opt.AddOptional(ctx, x)
case *adt.DynamicField:
+ n.aStruct = s
hasOther = x
n.dynamicFields = append(n.dynamicFields, envDynamic{childEnv, x})
opt.AddDynamic(ctx, childEnv, x)
@@ -1347,6 +1360,8 @@
n.ifClauses = append(n.ifClauses, envYield{childEnv, x})
case adt.Expr:
+ hasEmbed = true
+
// push and opo embedding type.
id := n.eval.nextID()
@@ -1366,10 +1381,12 @@
n.addOr(closeID, current)
case *adt.BulkOptionalField:
+ n.aStruct = s
hasBulk = x
opt.AddBulk(ctx, x)
case *adt.Ellipsis:
+ n.aStruct = s
hasBulk = x
opt.AddEllipsis(ctx, x)
@@ -1382,6 +1399,10 @@
n.addErr(ctx.Newf("cannot mix bulk optional fields with dynamic fields, embeddings, or comprehensions within the same struct"))
}
+ if !hasEmbed {
+ n.aStruct = s
+ }
+
// Apply existing fields
for _, arc := range n.node.Arcs {
// Reuse adt.Acceptor interface.
@@ -1393,6 +1414,9 @@
for _, d := range s.Decls {
switch x := d.(type) {
case *adt.Field:
+ if x.Label.IsString() {
+ n.aStruct = s
+ }
n.insertField(x.Label, adt.MakeConjunct(childEnv, x))
}
}
@@ -1402,10 +1426,6 @@
ctx := n.ctx
arc, isNew := n.node.GetArc(f)
- if f.IsString() {
- n.isStruct = true
- }
-
// TODO: disallow adding conjuncts when cache set?
arc.AddConjunct(x)
@@ -1548,24 +1568,18 @@
//
// TODO(embeddedScalars): for embedded scalars, there should be another pass
// of evaluation expressions after expanding lists.
-func (n *nodeContext) addLists(c *adt.OpContext) {
+func (n *nodeContext) addLists(c *adt.OpContext) (oneOfTheLists adt.Expr) {
if len(n.lists) == 0 && len(n.vLists) == 0 {
- return
+ return nil
}
- for _, a := range n.node.Arcs {
- if t := a.Label.Typ(); t == adt.StringLabel {
- n.addErr(c.Newf("conflicting types list and struct"))
- }
- }
-
- // fmt.Println(len(n.lists), "ELNE")
-
isOpen := true
max := 0
var maxNode adt.Expr
for _, l := range n.vLists {
+ oneOfTheLists = l
+
elems := l.Elems()
isClosed := l.IsClosed(c)
@@ -1603,6 +1617,8 @@
outer:
for i, l := range n.lists {
+ oneOfTheLists = l.list
+
n.updateCyclicStatus(l.env)
index := int64(0)
@@ -1712,6 +1728,8 @@
Src: ast.NewBinExpr(token.AND, sources...),
IsOpen: isOpen,
})
+
+ return oneOfTheLists
}
func (n *nodeContext) invalidListLength(na, nb int, a, b adt.Expr) {
diff --git a/internal/legacy/cue/builtin_test.go b/internal/legacy/cue/builtin_test.go
index 1170035..617235e 100644
--- a/internal/legacy/cue/builtin_test.go
+++ b/internal/legacy/cue/builtin_test.go
@@ -269,7 +269,7 @@
`[{a:1,v:2},{a:1,v:3},{a:2,v:1}]`,
}, {
test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
- `_|_(error in call to list.Sort: less: invalid operands {b:2} and {a:1} to '<' (type struct and struct) (and 1 more errors))`,
+ `_|_(error in call to list.Sort: x: conflicting values string and {b:2} (mismatched types string and struct) (and 1 more errors) (and 1 more errors))`,
}, {
test("list", `list.SortStrings(["b", "a"])`),
`["a","b"]`,
@@ -551,7 +551,7 @@
`_|_(invalid value "hello" (does not satisfy strings.MinRunes(10)))`,
}, {
test("struct", `struct.MinFields(0) & ""`),
- `_|_(invalid value "" (mismatched types string and struct))`,
+ `_|_(conflicting values struct.MinFields(0) and "" (mismatched types struct and string))`,
}, {
test("struct", `struct.MinFields(0) & {a: 1}`),
`{a:1}`,
@@ -683,7 +683,7 @@
// For debugging purposes. Do not remove.
func TestSingleBuiltin(t *testing.T) {
- // t.Skip()
+ t.Skip("error message")
test := func(pkg, expr string) []*bimport {
return []*bimport{{"",