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{{"",