internal/core/adt: record more error positions

Method: replace `ctx.Str(x)` arguments to formatting
with `x` and let the error methods do the conversion.
This gives access to position information as well, which
can then be added to the errors.

This has the additional benefit that this will allow lazy
expansion of error printing down the road.

Note that in some cases we now drop the "main"
position as it would result in too much redundancy.
In tests this manifests itself as a slight change of
the column position.

Fixes #905
Closes #129

Issue #52

Change-Id: I044cf34b9718f05553139974c6c166d2630568e6
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9446
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cue/testdata/basicrewrite/001_regexp.txtar b/cue/testdata/basicrewrite/001_regexp.txtar
index 0381a5f..8de10e6 100644
--- a/cue/testdata/basicrewrite/001_regexp.txtar
+++ b/cue/testdata/basicrewrite/001_regexp.txtar
@@ -74,8 +74,10 @@
     ./in.cue:11:5
 e1: cannot use 1 (type int) as type (string|bytes):
     ./in.cue:20:5
+    ./in.cue:20:14
 e2: cannot use true (type bool) as type (string|bytes):
     ./in.cue:21:5
+    ./in.cue:21:14
 
 Result:
 (_|_){
@@ -99,10 +101,12 @@
   e1: (_|_){
     // [eval] e1: cannot use 1 (type int) as type (string|bytes):
     //     ./in.cue:20:5
+    //     ./in.cue:20:14
   }
   e2: (_|_){
     // [eval] e2: cannot use true (type bool) as type (string|bytes):
     //     ./in.cue:21:5
+    //     ./in.cue:21:14
   }
   e3: (_|_){
     // [eval] e3: conflicting values !="a" and <5 (mismatched types string and number):
diff --git a/cue/testdata/basicrewrite/002_arithmetic.txtar b/cue/testdata/basicrewrite/002_arithmetic.txtar
index 21d4cfb..2286250 100644
--- a/cue/testdata/basicrewrite/002_arithmetic.txtar
+++ b/cue/testdata/basicrewrite/002_arithmetic.txtar
@@ -109,14 +109,19 @@
     ./in.cue:16:9
 e0: invalid operands 2 and "a" to '+' (type int and string):
     ./in.cue:23:5
+    ./in.cue:23:9
 e5: invalid operands 1.0 and 2 to 'div' (type float and int):
     ./in.cue:29:5
+    ./in.cue:29:13
 e6: invalid operands 2 and 2.0 to 'rem' (type int and float):
     ./in.cue:30:5
+    ./in.cue:30:11
 e7: invalid operands 2 and 2.0 to 'quo' (type int and float):
     ./in.cue:31:5
+    ./in.cue:31:11
 e8: invalid operands 1.0 and 1 to 'mod' (type float and int):
     ./in.cue:32:5
+    ./in.cue:32:13
 
 Result:
 (_|_){
@@ -160,21 +165,26 @@
   e0: (_|_){
     // [eval] e0: invalid operands 2 and "a" to '+' (type int and string):
     //     ./in.cue:23:5
+    //     ./in.cue:23:9
   }
   e5: (_|_){
     // [eval] e5: invalid operands 1.0 and 2 to 'div' (type float and int):
     //     ./in.cue:29:5
+    //     ./in.cue:29:13
   }
   e6: (_|_){
     // [eval] e6: invalid operands 2 and 2.0 to 'rem' (type int and float):
     //     ./in.cue:30:5
+    //     ./in.cue:30:11
   }
   e7: (_|_){
     // [eval] e7: invalid operands 2 and 2.0 to 'quo' (type int and float):
     //     ./in.cue:31:5
+    //     ./in.cue:31:11
   }
   e8: (_|_){
     // [eval] e8: invalid operands 1.0 and 1 to 'mod' (type float and int):
     //     ./in.cue:32:5
+    //     ./in.cue:32:13
   }
 }
diff --git a/cue/testdata/basicrewrite/003_integer-specific_arithmetic.txtar b/cue/testdata/basicrewrite/003_integer-specific_arithmetic.txtar
index 742b92a..0ed6240 100644
--- a/cue/testdata/basicrewrite/003_integer-specific_arithmetic.txtar
+++ b/cue/testdata/basicrewrite/003_integer-specific_arithmetic.txtar
@@ -89,20 +89,28 @@
 Errors:
 qe1: invalid operands 2.0 and 1 to 'quo' (type float and int):
     ./in.cue:5:6
+    ./in.cue:5:14
 qe2: invalid operands 2 and 1.0 to 'quo' (type int and float):
     ./in.cue:6:6
+    ./in.cue:6:12
 re1: invalid operands 2.0 and 1 to 'rem' (type float and int):
     ./in.cue:12:6
+    ./in.cue:12:14
 re2: invalid operands 2 and 1.0 to 'rem' (type int and float):
     ./in.cue:13:6
+    ./in.cue:13:12
 de1: invalid operands 2.0 and 1 to 'div' (type float and int):
     ./in.cue:19:6
+    ./in.cue:19:14
 de2: invalid operands 2 and 1.0 to 'div' (type int and float):
     ./in.cue:20:6
+    ./in.cue:20:12
 me1: invalid operands 2.0 and 1 to 'mod' (type float and int):
     ./in.cue:26:6
+    ./in.cue:26:14
 me2: invalid operands 2 and 1.0 to 'mod' (type int and float):
     ./in.cue:27:6
+    ./in.cue:27:12
 
 Result:
 (_|_){
@@ -114,10 +122,12 @@
   qe1: (_|_){
     // [eval] qe1: invalid operands 2.0 and 1 to 'quo' (type float and int):
     //     ./in.cue:5:6
+    //     ./in.cue:5:14
   }
   qe2: (_|_){
     // [eval] qe2: invalid operands 2 and 1.0 to 'quo' (type int and float):
     //     ./in.cue:6:6
+    //     ./in.cue:6:12
   }
   r1: (int){ 1 }
   r2: (int){ 1 }
@@ -126,10 +136,12 @@
   re1: (_|_){
     // [eval] re1: invalid operands 2.0 and 1 to 'rem' (type float and int):
     //     ./in.cue:12:6
+    //     ./in.cue:12:14
   }
   re2: (_|_){
     // [eval] re2: invalid operands 2 and 1.0 to 'rem' (type int and float):
     //     ./in.cue:13:6
+    //     ./in.cue:13:12
   }
   d1: (int){ 2 }
   d2: (int){ -2 }
@@ -138,10 +150,12 @@
   de1: (_|_){
     // [eval] de1: invalid operands 2.0 and 1 to 'div' (type float and int):
     //     ./in.cue:19:6
+    //     ./in.cue:19:14
   }
   de2: (_|_){
     // [eval] de2: invalid operands 2 and 1.0 to 'div' (type int and float):
     //     ./in.cue:20:6
+    //     ./in.cue:20:12
   }
   m1: (int){ 1 }
   m2: (int){ 1 }
@@ -150,9 +164,11 @@
   me1: (_|_){
     // [eval] me1: invalid operands 2.0 and 1 to 'mod' (type float and int):
     //     ./in.cue:26:6
+    //     ./in.cue:26:14
   }
   me2: (_|_){
     // [eval] me2: invalid operands 2 and 1.0 to 'mod' (type int and float):
     //     ./in.cue:27:6
+    //     ./in.cue:27:12
   }
 }
diff --git a/cue/testdata/basicrewrite/007_strings_and_bytes.txtar b/cue/testdata/basicrewrite/007_strings_and_bytes.txtar
index 27383f7..a1ffb65 100644
--- a/cue/testdata/basicrewrite/007_strings_and_bytes.txtar
+++ b/cue/testdata/basicrewrite/007_strings_and_bytes.txtar
@@ -43,8 +43,10 @@
 Errors:
 e0: invalid operands "a" and '' to '+' (type string and bytes):
     ./in.cue:10:5
+    ./in.cue:10:11
 e1: invalid operands 'b' and "c" to '+' (type bytes and string):
     ./in.cue:11:5
+    ./in.cue:11:11
 
 Result:
 (_|_){
@@ -58,9 +60,11 @@
   e0: (_|_){
     // [eval] e0: invalid operands "a" and '' to '+' (type string and bytes):
     //     ./in.cue:10:5
+    //     ./in.cue:10:11
   }
   e1: (_|_){
     // [eval] e1: invalid operands 'b' and "c" to '+' (type bytes and string):
     //     ./in.cue:11:5
+    //     ./in.cue:11:11
   }
 }
diff --git a/cue/testdata/basicrewrite/010_lists.txtar b/cue/testdata/basicrewrite/010_lists.txtar
index 9f65dc6..e6f90cd 100644
--- a/cue/testdata/basicrewrite/010_lists.txtar
+++ b/cue/testdata/basicrewrite/010_lists.txtar
@@ -83,6 +83,7 @@
     ./in.cue:5:12
 e3: invalid index -1 (index must be non-negative):
     ./in.cue:6:8
+    ./in.cue:6:12
 e4.3: invalid value 8 (out of bound <=5):
     ./in.cue:7:24
     ./in.cue:7:41
@@ -116,6 +117,7 @@
   e3: (_|_){
     // [eval] e3: invalid index -1 (index must be non-negative):
     //     ./in.cue:6:8
+    //     ./in.cue:6:12
   }
   e4: (_|_){
     // [eval]
diff --git a/cue/testdata/basicrewrite/015_types.txtar b/cue/testdata/basicrewrite/015_types.txtar
index 1616b60..0e79e4a 100644
--- a/cue/testdata/basicrewrite/015_types.txtar
+++ b/cue/testdata/basicrewrite/015_types.txtar
@@ -39,14 +39,14 @@
 }
 -- out/eval --
 Errors:
+b: invalid operand int ('!' requires concrete value):
+    ./in.cue:7:6
 e: conflicting values int and string (mismatched types int and string):
     ./in.cue:5:5
     ./in.cue:5:11
 e2: conflicting values 1 and string (mismatched types int and string):
     ./in.cue:6:5
     ./in.cue:6:9
-b: invalid operand int ('!' requires concrete value):
-    ./in.cue:7:5
 p: invalid operation +true (+ bool):
     ./in.cue:8:5
 m: invalid operation -false (- bool):
@@ -71,7 +71,7 @@
   }
   b: (_|_){
     // [eval] b: invalid operand int ('!' requires concrete value):
-    //     ./in.cue:7:5
+    //     ./in.cue:7:6
   }
   p: (_|_){
     // [eval] p: invalid operation +true (+ bool):
diff --git a/cue/testdata/basicrewrite/016_comparison.txtar b/cue/testdata/basicrewrite/016_comparison.txtar
index 11ef3b3..686c4c1 100644
--- a/cue/testdata/basicrewrite/016_comparison.txtar
+++ b/cue/testdata/basicrewrite/016_comparison.txtar
@@ -40,6 +40,7 @@
 Errors:
 err: invalid operands 2 and "s" to '==' (type int and string):
     ./in.cue:9:6
+    ./in.cue:9:11
 
 Result:
 (_|_){
@@ -54,5 +55,6 @@
   err: (_|_){
     // [eval] err: invalid operands 2 and "s" to '==' (type int and string):
     //     ./in.cue:9:6
+    //     ./in.cue:9:11
   }
 }
diff --git a/cue/testdata/builtins/all.txtar b/cue/testdata/builtins/all.txtar
index b6c5c20..4b9cb62 100644
--- a/cue/testdata/builtins/all.txtar
+++ b/cue/testdata/builtins/all.txtar
@@ -22,6 +22,7 @@
 Errors:
 fatalArg.x: invalid operands "eee" and 'eee' to '+' (type string and bytes):
     ./in.cue:3:12
+    ./in.cue:3:20
 0.a: undefined field c:
     ./in.cue:9:20
 
@@ -33,6 +34,7 @@
     x: (_|_){
       // [eval] fatalArg.x: invalid operands "eee" and 'eee' to '+' (type string and bytes):
       //     ./in.cue:3:12
+      //     ./in.cue:3:20
     }
   }
   fatalChild: (_|_){
diff --git a/cue/testdata/builtins/incomplete.txtar b/cue/testdata/builtins/incomplete.txtar
index 3d3eea9..85765b6 100644
--- a/cue/testdata/builtins/incomplete.txtar
+++ b/cue/testdata/builtins/incomplete.txtar
@@ -95,6 +95,7 @@
     ./in.cue:77:8
 badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
     ./in.cue:83:8
+    ./in.cue:84:8
 
 Result:
 (_|_){
@@ -194,10 +195,12 @@
       transformed: (_|_){
         // [incomplete] incompleteArgStringList.#a.transformed: non-concrete value string in operand to +:
         //     ./in.cue:58:22
+        //     ./in.cue:57:9
       }
       joined: (_|_){
         // [incomplete] 0: non-concrete value string in operand to +:
         //     ./in.cue:58:22
+        //     ./in.cue:57:9
       }
     }
   }
@@ -216,15 +219,18 @@
     x: (_|_){
       // [incomplete] incompleteListError.x: non-concrete value _ in operand to +:
       //     ./in.cue:70:8
+      //     ./in.cue:71:5
     }
     y: (_){ _ }
     decimal: (_|_){
       // [incomplete] incompleteListError.x: non-concrete value _ in operand to +:
       //     ./in.cue:70:8
+      //     ./in.cue:71:5
     }
     str: (_|_){
       // [incomplete] incompleteListError.x: non-concrete value _ in operand to +:
       //     ./in.cue:70:8
+      //     ./in.cue:71:5
     }
   }
   badListType: (_|_){
@@ -244,15 +250,18 @@
     x: (_|_){
       // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
       //     ./in.cue:83:8
+      //     ./in.cue:84:8
     }
     y: (string){ "foo" }
     decimal: (_|_){
       // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
       //     ./in.cue:83:8
+      //     ./in.cue:84:8
     }
     str: (_|_){
       // [eval] badListError.x: invalid operands 2 and "foo" to '+' (type int and string):
       //     ./in.cue:83:8
+      //     ./in.cue:84:8
     }
   }
 }
diff --git a/cue/testdata/builtins/issue299.txtar b/cue/testdata/builtins/issue299.txtar
index d6d66ce..104f4dd 100644
--- a/cue/testdata/builtins/issue299.txtar
+++ b/cue/testdata/builtins/issue299.txtar
@@ -5,13 +5,19 @@
 x: ["x","x"]
 -- out/eval --
 Errors:
-x: invalid value ["x","x"] (does not satisfy list.UniqueItems)
+x: invalid value ["x","x"] (does not satisfy list.UniqueItems):
+    ./in.cue:2:1
+    ./in.cue:3:1
+    ./in.cue:4:1
 
 Result:
 (_|_){
   // [eval]
   x: (_|_){
-    // [eval] x: invalid value ["x","x"] (does not satisfy list.UniqueItems)
+    // [eval] x: invalid value ["x","x"] (does not satisfy list.UniqueItems):
+    //     ./in.cue:2:1
+    //     ./in.cue:3:1
+    //     ./in.cue:4:1
     0: (string){ "x" }
     1: (string){ "x" }
   }
diff --git a/cue/testdata/builtins/validators.txtar b/cue/testdata/builtins/validators.txtar
index 541297b..aa0ae60 100644
--- a/cue/testdata/builtins/validators.txtar
+++ b/cue/testdata/builtins/validators.txtar
@@ -125,6 +125,8 @@
       kv: (_|_){
         // [incomplete] incompleteError2.MyType.kv: invalid value {} (does not satisfy struct.MinFields(1)): len(fields) < MinFields(1) (0 < 1):
         //     ./in.cue:22:17
+        //     ./in.cue:21:13
+        //     ./in.cue:22:13
         //     ./in.cue:22:34
       }
     }
@@ -148,6 +150,8 @@
       kv: (_|_){
         // [incomplete] violation.#MyType.kv: invalid value {} (does not satisfy struct.MinFields(1)): len(fields) < MinFields(1) (0 < 1):
         //     ./in.cue:49:17
+        //     ./in.cue:48:13
+        //     ./in.cue:49:13
         //     ./in.cue:49:34
       }
     }
diff --git a/cue/testdata/comprehensions/errors.txtar b/cue/testdata/comprehensions/errors.txtar
index 85fa787..6262163 100644
--- a/cue/testdata/comprehensions/errors.txtar
+++ b/cue/testdata/comprehensions/errors.txtar
@@ -48,6 +48,7 @@
   userError: (_|_){
     // [incomplete] userError: non-concrete value string in operand to !=:
     //     ./in.cue:21:8
+    //     ./in.cue:20:5
     a: (string){ string }
   }
 }
diff --git a/cue/testdata/comprehensions/iferror.txtar b/cue/testdata/comprehensions/iferror.txtar
index ee6ee4d..ced1064 100644
--- a/cue/testdata/comprehensions/iferror.txtar
+++ b/cue/testdata/comprehensions/iferror.txtar
@@ -114,6 +114,7 @@
 Errors:
 wrongConcreteType: cannot use 2 (type int) as type bool:
     ./in.cue:4:2
+    ./in.cue:1:9
 wrongType: cannot use int (type int) as type bool:
     ./in.cue:10:2
 
@@ -127,6 +128,7 @@
   wrongConcreteType: (_|_){
     // [eval] wrongConcreteType: cannot use 2 (type int) as type bool:
     //     ./in.cue:4:2
+    //     ./in.cue:1:9
   }
   wrongType: (_|_){
     // [eval] wrongType: cannot use int (type int) as type bool:
diff --git a/cue/testdata/cycle/023_reentrance.txtar b/cue/testdata/cycle/023_reentrance.txtar
index a063692..628cb26 100644
--- a/cue/testdata/cycle/023_reentrance.txtar
+++ b/cue/testdata/cycle/023_reentrance.txtar
@@ -91,15 +91,21 @@
     out: (_|_){
       // [incomplete] non-concrete value int in operand to >=:
       //     ./in.cue:7:5
+      //     ./in.cue:3:32
+      //     ./in.cue:5:2
       // non-concrete value int in operand to <:
       //     ./in.cue:10:5
+      //     ./in.cue:3:32
+      //     ./in.cue:5:2
     }
   }
   fib: (_|_){
     // [incomplete] fib: non-concrete value int in operand to >=:
     //     ./in.cue:7:5
+    //     ./in.cue:5:2
     // fib: non-concrete value int in operand to <:
     //     ./in.cue:10:5
+    //     ./in.cue:5:2
     n: (int){ int }
   }
   fib2: (int){ 1 }
diff --git a/cue/testdata/disjunctions/elimination.txtar b/cue/testdata/disjunctions/elimination.txtar
index 9b7ebf7..73dc032 100644
--- a/cue/testdata/disjunctions/elimination.txtar
+++ b/cue/testdata/disjunctions/elimination.txtar
@@ -264,7 +264,9 @@
                 a: (_|_){
                   // [incomplete] nestedNonMonotonic.incomplete.a.n1.p1.x.a: invalid value {c:1} (does not satisfy struct.MinFields(2)): len(fields) < MinFields(2) (1 < 2):
                   //     ./in.cue:84:13
+                  //     ./in.cue:84:10
                   //     ./in.cue:84:30
+                  //     ./in.cue:85:10
                   c: (int){ 1 }
                 }
               }, (null){ null }) }
@@ -274,6 +276,8 @@
                 a: (_|_){
                   // [incomplete] nestedNonMonotonic.incomplete.a.n1.p2.x.a: invalid value {c:1} (does not satisfy struct.MinFields(2)): len(fields) < MinFields(2) (1 < 2):
                   //     ./in.cue:90:13
+                  //     ./in.cue:89:10
+                  //     ./in.cue:90:10
                   //     ./in.cue:90:30
                   c: (int){ 1 }
                 }
@@ -287,7 +291,9 @@
                   b: (_|_){
                     // [incomplete] nestedNonMonotonic.incomplete.a.n2.p1.x.a.b: invalid value {c:1} (does not satisfy struct.MinFields(2)): len(fields) < MinFields(2) (1 < 2):
                     //     ./in.cue:94:16
+                    //     ./in.cue:94:13
                     //     ./in.cue:94:33
+                    //     ./in.cue:95:13
                     c: (int){ 1 }
                   }
                 }
@@ -299,6 +305,8 @@
                   b: (_|_){
                     // [incomplete] nestedNonMonotonic.incomplete.a.n2.p2.x.a.b: invalid value {c:1} (does not satisfy struct.MinFields(2)): len(fields) < MinFields(2) (1 < 2):
                     //     ./in.cue:100:16
+                    //     ./in.cue:99:13
+                    //     ./in.cue:100:13
                     //     ./in.cue:100:33
                     c: (int){ 1 }
                   }
@@ -314,7 +322,10 @@
                 a: (_|_){
                   // [incomplete] nestedNonMonotonic.incomplete.b.n1.p1.x.a: invalid value {c:1,d:1} (does not satisfy struct.MinFields(3)): len(fields) < MinFields(3) (2 < 3):
                   //     ./in.cue:105:13
+                  //     ./in.cue:105:10
                   //     ./in.cue:105:30
+                  //     ./in.cue:106:10
+                  //     ./in.cue:107:10
                   c: (int){ 1 }
                   d: (int){ 1 }
                 }
@@ -325,7 +336,10 @@
                 a: (_|_){
                   // [incomplete] nestedNonMonotonic.incomplete.b.n1.p2.x.a: invalid value {c:1,d:1} (does not satisfy struct.MinFields(3)): len(fields) < MinFields(3) (2 < 3):
                   //     ./in.cue:112:13
+                  //     ./in.cue:111:10
+                  //     ./in.cue:112:10
                   //     ./in.cue:112:30
+                  //     ./in.cue:113:10
                   c: (int){ 1 }
                   d: (int){ 1 }
                 }
@@ -336,6 +350,9 @@
                 a: (_|_){
                   // [incomplete] nestedNonMonotonic.incomplete.b.n1.p3.x.a: invalid value {c:1,d:1} (does not satisfy struct.MinFields(3)): len(fields) < MinFields(3) (2 < 3):
                   //     ./in.cue:119:13
+                  //     ./in.cue:117:10
+                  //     ./in.cue:118:10
+                  //     ./in.cue:119:10
                   //     ./in.cue:119:30
                   c: (int){ 1 }
                   d: (int){ 1 }
@@ -350,6 +367,15 @@
                   b: (_|_){
                     // [incomplete] nestedNonMonotonic.incomplete.b.n2.p1.x.a.b: invalid value {c:1,d:1} (does not satisfy struct.MinFields(3)): len(fields) < MinFields(3) (2 < 3):
                     //     ./in.cue:137:16
+                    //     ./in.cue:123:13
+                    //     ./in.cue:124:13
+                    //     ./in.cue:125:13
+                    //     ./in.cue:129:13
+                    //     ./in.cue:130:13
+                    //     ./in.cue:131:13
+                    //     ./in.cue:135:13
+                    //     ./in.cue:136:13
+                    //     ./in.cue:137:13
                     //     ./in.cue:137:33
                     c: (int){ 1 }
                     d: (int){ 1 }
diff --git a/cue/testdata/disjunctions/errors.txtar b/cue/testdata/disjunctions/errors.txtar
index 1a1ba71..cddde7f 100644
--- a/cue/testdata/disjunctions/errors.txtar
+++ b/cue/testdata/disjunctions/errors.txtar
@@ -126,6 +126,7 @@
   explicitDefaultError: (_|_){
     // [incomplete] explicitDefaultError: non-concrete value string in operand to !=:
     //     ./in.cue:30:8
+    //     ./in.cue:28:5
     a: (string){ string }
   }
 }
diff --git a/cue/testdata/eval/bounds.txtar b/cue/testdata/eval/bounds.txtar
index 45fa97b..7607f88 100644
--- a/cue/testdata/eval/bounds.txtar
+++ b/cue/testdata/eval/bounds.txtar
@@ -119,27 +119,27 @@
     basic5: (bytes){ bytes }
     bne1: (_|_){
       // [incomplete] simplifyExpr.bne1: non-concrete value s for bound !=:
-      //     ./in.cue:55:11
+      //     ./in.cue:55:14
     }
     bne2: (_|_){
       // [incomplete] simplifyExpr.bne2: non-concrete value n for bound !=:
-      //     ./in.cue:56:11
+      //     ./in.cue:56:14
     }
     bne3: (_|_){
       // [incomplete] simplifyExpr.bne3: non-concrete value n for bound !=:
-      //     ./in.cue:57:11
+      //     ./in.cue:57:14
     }
     bne4: (_|_){
       // [incomplete] simplifyExpr.bne4: non-concrete value i for bound !=:
-      //     ./in.cue:58:11
+      //     ./in.cue:58:14
     }
     bne5: (_|_){
       // [incomplete] simplifyExpr.bne5: non-concrete value b for bound !=:
-      //     ./in.cue:59:11
+      //     ./in.cue:59:14
     }
     e1: (_|_){
       // [incomplete] simplifyExpr.e1: non-concrete value =~"foo" for bound <:
-      //     ./in.cue:61:9
+      //     ./in.cue:61:11
     }
     e2: (_|_){
       // [eval] simplifyExpr.e2: cannot use null for bound >:
diff --git a/cue/testdata/eval/incomplete.txtar b/cue/testdata/eval/incomplete.txtar
index c075991..6509e3f 100644
--- a/cue/testdata/eval/incomplete.txtar
+++ b/cue/testdata/eval/incomplete.txtar
@@ -24,6 +24,7 @@
   e1: (_|_){
     // [incomplete] e1: non-concrete value string in operand to +:
     //     ./in.cue:3:5
+    //     ./in.cue:1:1
   }
   e2: (string){ >"bar" }
   e3: (string){ "foo" }
@@ -31,6 +32,7 @@
   e4: (_|_){
     // [incomplete] e1: non-concrete value string in operand to +:
     //     ./in.cue:3:5
+    //     ./in.cue:1:1
   }
   e5: (_|_){
     // [cycle] cycle error
@@ -55,6 +57,7 @@
   okay: (_|_){
     // [incomplete] okay: non-concrete value >10 & int in operand to +:
     //     ./in.cue:18:7
+    //     ./in.cue:18:8
   }
 }
 -- out/compile --
diff --git a/cue/testdata/eval/resolve_basic.txtar b/cue/testdata/eval/resolve_basic.txtar
index 08be88e..0fc38bc 100644
--- a/cue/testdata/eval/resolve_basic.txtar
+++ b/cue/testdata/eval/resolve_basic.txtar
@@ -18,6 +18,7 @@
     y: (_|_){
       // [incomplete] d.y: non-concrete value _ in operand to +:
       //     ./in.cue:5:6
+      //     ./in.cue:4:3
     }
   }
   e: (struct){
diff --git a/cue/testdata/eval/selectors.txtar b/cue/testdata/eval/selectors.txtar
index 4fdc64e..cb0b141 100644
--- a/cue/testdata/eval/selectors.txtar
+++ b/cue/testdata/eval/selectors.txtar
@@ -25,6 +25,7 @@
     y: (_|_){
       // [incomplete] d.y: non-concrete value _ in operand to +:
       //     ./in.cue:5:6
+      //     ./in.cue:4:3
     }
   }
   e: (struct){
diff --git a/cue/testdata/fulleval/005_conditional_field.txtar b/cue/testdata/fulleval/005_conditional_field.txtar
index 5aeea2c..dd8943a 100644
--- a/cue/testdata/fulleval/005_conditional_field.txtar
+++ b/cue/testdata/fulleval/005_conditional_field.txtar
@@ -63,6 +63,7 @@
   d: (_|_){
     // [incomplete] d: non-concrete value int in operand to >:
     //     ./in.cue:14:5
+    //     ./in.cue:13:2
     a: (int){ int }
   }
   a: (string){ "foo" }
diff --git a/cue/testdata/fulleval/018_recursive_evaluation_within_list.txtar b/cue/testdata/fulleval/018_recursive_evaluation_within_list.txtar
index 79d2de4..9d0ca73 100644
--- a/cue/testdata/fulleval/018_recursive_evaluation_within_list.txtar
+++ b/cue/testdata/fulleval/018_recursive_evaluation_within_list.txtar
@@ -88,6 +88,7 @@
     d: (_|_){
       // [incomplete] b1.d: non-concrete value string in operand to +:
       //     ./in.cue:10:5
+      //     ./in.cue:11:2
     }
     c: (string){ string }
   }
diff --git a/cue/testdata/fulleval/020_complex_interaction_of_groundness.txtar b/cue/testdata/fulleval/020_complex_interaction_of_groundness.txtar
index 1b31de5..e0b4379 100644
--- a/cue/testdata/fulleval/020_complex_interaction_of_groundness.txtar
+++ b/cue/testdata/fulleval/020_complex_interaction_of_groundness.txtar
@@ -74,6 +74,8 @@
         s: (_|_){
           // [incomplete] a.b.c.s: non-concrete value string in operand to +:
           //     ./in.cue:4:34
+          //     ./in.cue:4:20
+          //     ./in.cue:5:10
         }
       }
     }
diff --git a/cue/testdata/fulleval/021_complex_groundness_2.txtar b/cue/testdata/fulleval/021_complex_groundness_2.txtar
index 50a9f11..582df99 100644
--- a/cue/testdata/fulleval/021_complex_groundness_2.txtar
+++ b/cue/testdata/fulleval/021_complex_groundness_2.txtar
@@ -85,8 +85,16 @@
       s: (_|_){
         // [incomplete] f1.res.s: non-concrete value string in operand to +:
         //     ./in.cue:5:25
+        //     ./in.cue:3:31
+        //     ./in.cue:5:11
+        //     ./in.cue:6:20
+        //     ./in.cue:7:10
         // f1.res.s: non-concrete value string in operand to +:
         //     ./in.cue:6:34
+        //     ./in.cue:3:31
+        //     ./in.cue:5:11
+        //     ./in.cue:6:20
+        //     ./in.cue:7:10
       }
     }
   }
@@ -97,8 +105,14 @@
         s: (_|_){
           // [incomplete] a.b.c.s: non-concrete value string in operand to +:
           //     ./in.cue:5:25
+          //     ./in.cue:5:11
+          //     ./in.cue:6:20
+          //     ./in.cue:7:10
           // a.b.c.s: non-concrete value string in operand to +:
           //     ./in.cue:6:34
+          //     ./in.cue:5:11
+          //     ./in.cue:6:20
+          //     ./in.cue:7:10
         }
       }
     }
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 150c352..ec3b2c0 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
@@ -65,18 +65,22 @@
   n4: (_|_){
     // [incomplete] n4: non-concrete value <4 in operand to +:
     //     ./in.cue:6:5
+    //     ./in.cue:18:6
   }
   n5: (_|_){
     // [incomplete] n5: non-concrete value <4 in operand to -:
     //     ./in.cue:7:5
+    //     ./in.cue:18:6
   }
   n6: (_|_){
     // [incomplete] n6: non-concrete value <4 in operand to *:
     //     ./in.cue:8:5
+    //     ./in.cue:18:6
   }
   n7: (_|_){
     // [incomplete] n7: non-concrete value <4 in operand to /:
     //     ./in.cue:9:5
+    //     ./in.cue:18:6
   }
   b1: (_|_){
     // [incomplete] b1: operand is of '!' not concrete (was bool):
diff --git a/cue/testdata/references/incomplete.txtar b/cue/testdata/references/incomplete.txtar
index e790969..b316a99 100644
--- a/cue/testdata/references/incomplete.txtar
+++ b/cue/testdata/references/incomplete.txtar
@@ -34,6 +34,7 @@
 Errors:
 incompleteIndex.a: invalid index top (invalid type _):
     ./in.cue:29:8
+    ./in.cue:29:12
 
 Result:
 (_|_){
@@ -78,6 +79,7 @@
     a: (_|_){
       // [eval] incompleteIndex.a: invalid index top (invalid type _):
       //     ./in.cue:29:8
+      //     ./in.cue:29:12
     }
   }
 }
diff --git a/cue/testdata/references/index.txtar b/cue/testdata/references/index.txtar
index 56d8e5f..2fbcbe6 100644
--- a/cue/testdata/references/index.txtar
+++ b/cue/testdata/references/index.txtar
@@ -43,14 +43,18 @@
     ./in.cue:17:20
 booleanIndex: invalid index true (invalid type bool):
     ./in.cue:18:15
+    ./in.cue:18:18
 indexOutOfBounds3: invalid list index 4 (out of bounds):
     ./in.cue:19:30
 negativeIndex: invalid index -1 (index must be non-negative):
     ./in.cue:20:16
+    ./in.cue:20:26
 varIndexTooLarge.a: index out of range [3] with length 3:
     ./in.cue:24:18
 varNegativeIndex.a: index n out of range [-1]:
     ./in.cue:29:8
+    ./in.cue:28:8
+    ./in.cue:29:18
 
 Result:
 (_|_){
@@ -87,6 +91,7 @@
   booleanIndex: (_|_){
     // [eval] booleanIndex: invalid index true (invalid type bool):
     //     ./in.cue:18:15
+    //     ./in.cue:18:18
   }
   indexOutOfBounds3: (_|_){
     // [eval] indexOutOfBounds3: invalid list index 4 (out of bounds):
@@ -95,6 +100,7 @@
   negativeIndex: (_|_){
     // [eval] negativeIndex: invalid index -1 (index must be non-negative):
     //     ./in.cue:20:16
+    //     ./in.cue:20:26
   }
   varIndexTooLarge: (_|_){
     // [eval]
@@ -110,6 +116,8 @@
     a: (_|_){
       // [eval] varNegativeIndex.a: index n out of range [-1]:
       //     ./in.cue:29:8
+      //     ./in.cue:28:8
+      //     ./in.cue:29:18
     }
   }
 }
diff --git a/cue/testdata/resolve/016_index.txtar b/cue/testdata/resolve/016_index.txtar
index 296f877..b18c5ee 100644
--- a/cue/testdata/resolve/016_index.txtar
+++ b/cue/testdata/resolve/016_index.txtar
@@ -103,16 +103,19 @@
     ./in.cue:7:5
 e3: invalid index true (invalid type bool):
     ./in.cue:8:5
+    ./in.cue:8:8
 e4: index out of range [3] with length 3:
     ./in.cue:9:15
 e5: invalid index -1 (index must be non-negative):
     ./in.cue:10:5
+    ./in.cue:10:15
 e6: invalid list index 1 (out of bounds):
     ./in.cue:11:16
 e7: index out of range [3] with length 3:
     ./in.cue:12:15
 e8: invalid index -1 (index must be non-negative):
     ./in.cue:13:5
+    ./in.cue:13:15
 e9: invalid list index 1 (out of bounds):
     ./in.cue:14:16
 
@@ -142,6 +145,7 @@
   e3: (_|_){
     // [eval] e3: invalid index true (invalid type bool):
     //     ./in.cue:8:5
+    //     ./in.cue:8:8
   }
   e4: (_|_){
     // [eval] e4: index out of range [3] with length 3:
@@ -150,6 +154,7 @@
   e5: (_|_){
     // [eval] e5: invalid index -1 (index must be non-negative):
     //     ./in.cue:10:5
+    //     ./in.cue:10:15
   }
   e6: (_|_){
     // [eval] e6: invalid list index 1 (out of bounds):
@@ -162,6 +167,7 @@
   e8: (_|_){
     // [eval] e8: invalid index -1 (index must be non-negative):
     //     ./in.cue:13:5
+    //     ./in.cue:13:15
   }
   e9: (_|_){
     // [eval] e9: invalid list index 1 (out of bounds):
diff --git a/cue/testdata/resolve/018_slice.txtar b/cue/testdata/resolve/018_slice.txtar
index 775ba8f..813199f 100644
--- a/cue/testdata/resolve/018_slice.txtar
+++ b/cue/testdata/resolve/018_slice.txtar
@@ -65,8 +65,10 @@
     ./in.cue:7:5
 e6: cannot use "" (type string) as type int in slice index:
     ./in.cue:8:5
+    ./in.cue:8:9
 e7: cannot use "9" (type string) as type int in slice index:
     ./in.cue:9:5
+    ./in.cue:9:10
 
 Result:
 (_|_){
@@ -98,9 +100,11 @@
   e6: (_|_){
     // [eval] e6: cannot use "" (type string) as type int in slice index:
     //     ./in.cue:8:5
+    //     ./in.cue:8:9
   }
   e7: (_|_){
     // [eval] e7: cannot use "9" (type string) as type int in slice index:
     //     ./in.cue:9:5
+    //     ./in.cue:9:10
   }
 }
diff --git a/cue/testdata/resolve/024_structs.txtar b/cue/testdata/resolve/024_structs.txtar
index 7d16014..39912a2 100644
--- a/cue/testdata/resolve/024_structs.txtar
+++ b/cue/testdata/resolve/024_structs.txtar
@@ -55,6 +55,7 @@
     d: (_|_){
       // [incomplete] t.d: non-concrete value number in operand to *:
       //     ./in.cue:3:19
+      //     ./in.cue:3:5
     }
   }
   ti: (struct){
@@ -62,6 +63,8 @@
     d: (_|_){
       // [incomplete] ti.d: non-concrete value int in operand to *:
       //     ./in.cue:3:19
+      //     ./in.cue:3:5
+      //     ./in.cue:4:10
     }
   }
 }
diff --git a/cue/testdata/resolve/039_reference_to_root.txtar b/cue/testdata/resolve/039_reference_to_root.txtar
index 7736c0f..8bceff1 100644
--- a/cue/testdata/resolve/039_reference_to_root.txtar
+++ b/cue/testdata/resolve/039_reference_to_root.txtar
@@ -87,6 +87,7 @@
     d: (_|_){
       // [incomplete] c.d: non-concrete value int in operand to +:
       //     ./in.cue:4:5
+      //     ./in.cue:1:5
     }
   }
   x: (struct){
@@ -94,6 +95,7 @@
     c: (_|_){
       // [incomplete] x.c: non-concrete value int in operand to +:
       //     ./in.cue:8:5
+      //     ./in.cue:7:2
     }
   }
   y: (struct){
@@ -105,6 +107,7 @@
     c: (_|_){
       // [incomplete] v.c: non-concrete value int in operand to +:
       //     ./in.cue:16:5
+      //     ./in.cue:15:2
     }
   }
   w: (struct){
@@ -112,6 +115,7 @@
     c: (_|_){
       // [incomplete] w.c: non-concrete value int in operand to +:
       //     ./in.cue:16:5
+      //     ./in.cue:15:2
     }
   }
   wp: (struct){
@@ -119,6 +123,7 @@
     c: (_|_){
       // [incomplete] wp.c: non-concrete value int in operand to +:
       //     ./in.cue:16:5
+      //     ./in.cue:15:2
     }
   }
 }
diff --git a/cue/testdata/scalars/embed.txtar b/cue/testdata/scalars/embed.txtar
index fe0424f..9774e1a 100644
--- a/cue/testdata/scalars/embed.txtar
+++ b/cue/testdata/scalars/embed.txtar
@@ -166,6 +166,7 @@
       out: (_|_){
         // [incomplete] outPattern.sum.out: non-concrete value int in operand to +:
         //     ./in.cue:44:14
+        //     ./in.cue:45:9
       }
       #a: (int){ int }
       #b: (int){ int }
@@ -180,6 +181,7 @@
     sum: (_|_){
       // [incomplete] arithmetic.sum: non-concrete value int in operand to +:
       //     ./in.cue:53:9
+      //     ./in.cue:54:9
       #a: (int){ int }
       #b: (int){ int }
     }
diff --git a/internal/core/adt/adt.go b/internal/core/adt/adt.go
index 7c670a4..e77df49 100644
--- a/internal/core/adt/adt.go
+++ b/internal/core/adt/adt.go
@@ -54,7 +54,7 @@
 	default:
 		// Unknown type.
 		v = ctx.NewErrf(
-			"could not evaluate expression %s of type %T", ctx.Str(c.Expr()), c)
+			"could not evaluate expression %s of type %T", c.Expr(), c)
 	}
 
 	return ToVertex(v)
diff --git a/internal/core/adt/binop.go b/internal/core/adt/binop.go
index 4c87a49..e1d22c1 100644
--- a/internal/core/adt/binop.go
+++ b/internal/core/adt/binop.go
@@ -31,13 +31,13 @@
 	if left.Concreteness() > Concrete {
 		return &Bottom{
 			Code: IncompleteError,
-			Err:  c.Newf(msg, c.Str(left), op),
+			Err:  c.Newf(msg, left, op),
 		}
 	}
 	if right.Concreteness() > Concrete {
 		return &Bottom{
 			Code: IncompleteError,
-			Err:  c.Newf(msg, c.Str(right), op),
+			Err:  c.Newf(msg, right, op),
 		}
 	}
 
@@ -290,7 +290,7 @@
 	}
 
 	return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)",
-		c.Str(left), c.Str(right), op, left.Kind(), right.Kind())
+		left, right, op, left.Kind(), right.Kind())
 }
 
 func cmpTonode(c *OpContext, op Op, r int) Value {
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index 8163714..4c3b6d0 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -26,7 +26,6 @@
 
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
-	"cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/token"
 )
 
@@ -274,8 +273,9 @@
 
 func (c *OpContext) concreteIsPossible(op Op, x Expr) bool {
 	if !AssertConcreteIsPossible(op, x) {
-		c.AddErrf("invalid operand %s ('%s' requires concrete value)",
-			c.Str(x), op)
+		// No need to take position of expression.
+		c.AddErr(c.NewPosf(token.NoPos,
+			"invalid operand %s ('%s' requires concrete value)", x, op))
 		return false
 	}
 	return true
@@ -306,18 +306,6 @@
 }
 
 func (c *OpContext) addErrf(code ErrorCode, pos token.Pos, msg string, args ...interface{}) {
-	for i, a := range args {
-		switch x := a.(type) {
-		case Node:
-			args[i] = c.Str(x)
-		case ast.Node:
-			b, _ := format.Node(x)
-			args[i] = string(b)
-		case Feature:
-			args[i] = x.SelectorString(c.Runtime)
-		}
-	}
-
 	err := c.NewPosf(pos, msg, args...)
 	c.addErr(code, err)
 }
@@ -453,17 +441,17 @@
 // msg is used to mention the context in which an error occurred, if any.
 func (c *OpContext) Concrete(env *Environment, x Expr, msg interface{}) (result Value, complete bool) {
 
-	v, complete := c.Evaluate(env, x)
+	w, complete := c.Evaluate(env, x)
 
-	v, ok := c.getDefault(v)
+	w, ok := c.getDefault(w)
 	if !ok {
-		return v, false
+		return w, false
 	}
-	v = Unwrap(v)
+	v := Unwrap(w)
 
 	if !IsConcrete(v) {
 		complete = false
-		b := c.NewErrf("non-concrete value %v in operand to %s", c.Str(v), msg)
+		b := c.NewErrf("non-concrete value %v in operand to %s", w, msg)
 		b.Code = IncompleteError
 		v = b
 	}
@@ -504,7 +492,7 @@
 
 	if d.NumDefaults != 1 {
 		c.addErrf(IncompleteError, c.pos(),
-			"unresolved disjunction %s (type %s)", c.Str(d), d.Kind())
+			"unresolved disjunction %s (type %s)", d, d.Kind())
 		return nil, false
 	}
 	return c.getDefault(d.Values[0])
@@ -752,7 +740,7 @@
 		}
 
 	case nil:
-		// c.addErrf(IncompleteError, pos, "incomplete value %s", c.Str(x))
+		// c.addErrf(IncompleteError, pos, "incomplete value %s", x)
 		// return nil
 
 	case *Bottom:
@@ -847,10 +835,9 @@
 		return
 	}
 	if !IsConcrete(v) && v.Kind()&k != 0 {
-		c.addErrf(IncompleteError, pos(v),
-			"incomplete %s value '%s'", k, c.Str(v))
+		c.addErrf(IncompleteError, pos(v), "incomplete %s value '%s'", k, v)
 	} else {
-		c.AddErrf("cannot use %s (type %s) as type %s", c.Str(v), v.Kind(), k)
+		c.AddErrf("cannot use %s (type %s) as type %s", v, v.Kind(), k)
 	}
 }
 
@@ -864,10 +851,9 @@
 	}
 	if !IsConcrete(v) && v.Kind()&k != 0 {
 		c.addErrf(IncompleteError, pos(v),
-			"incomplete %s value '%s' in as", k, c.Str(v), as)
+			"incomplete %s value '%s' in as", k, v, as)
 	} else {
-		c.AddErrf("cannot use %s (type %s) as type %s in %v",
-			c.Str(v), v.Kind(), k, as)
+		c.AddErrf("cannot use %s (type %s) as type %s in %v", v, v.Kind(), k, as)
 	}
 }
 
@@ -912,11 +898,10 @@
 		switch orig.(type) {
 		case *ForClause:
 			c.addErrf(IncompleteError, pos(x),
-				"cannot range over %s (incomplete)",
-				c.Str(x))
+				"cannot range over %s (incomplete)", x)
 		default:
 			c.addErrf(IncompleteError, pos(x),
-				"%s undefined (%s is incomplete)", c.Str(orig), c.Str(x))
+				"%s undefined (%s is incomplete)", orig, x)
 		}
 		return emptyNode
 
@@ -937,12 +922,10 @@
 			switch orig.(type) {
 			case *ForClause:
 				c.addErrf(IncompleteError, pos(x),
-					"cannot range over %s (incomplete type %s)",
-					c.Str(x), kind)
+					"cannot range over %s (incomplete type %s)", x, kind)
 			default:
 				c.addErrf(IncompleteError, pos(x),
-					"%s undefined as %s is incomplete (type %s)",
-					c.Str(orig), c.Str(x), kind)
+					"%s undefined as %s is incomplete (type %s)", orig, x, kind)
 			}
 			return emptyNode
 
@@ -1108,7 +1091,7 @@
 
 	default:
 		c.addErrf(IncompleteError, c.pos(),
-			"non-concrete value %s (type %s)", c.Str(v), v.Kind())
+			"non-concrete value %s (type %s)", v, v.Kind())
 	}
 	return ""
 }
diff --git a/internal/core/adt/errors.go b/internal/core/adt/errors.go
index 1b20606..c990092 100644
--- a/internal/core/adt/errors.go
+++ b/internal/core/adt/errors.go
@@ -34,6 +34,7 @@
 import (
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
+	cueformat "cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/token"
 )
 
@@ -260,20 +261,38 @@
 	return c.NewPosf(c.pos(), format, args...)
 }
 
+func appendNodePositions(a []token.Pos, n Node) []token.Pos {
+	if p := pos(n); p != token.NoPos {
+		a = append(a, p)
+	} else if v, ok := n.(*Vertex); ok {
+		for _, c := range v.Conjuncts {
+			a = appendNodePositions(a, c.x)
+		}
+	}
+	return a
+}
+
 func (c *OpContext) NewPosf(p token.Pos, format string, args ...interface{}) *ValueError {
 	var a []token.Pos
 	if len(c.positions) > 0 {
 		a = make([]token.Pos, 0, len(c.positions))
 		for _, n := range c.positions {
-			if p := pos(n); p != token.NoPos {
+			a = appendNodePositions(a, n)
+		}
+	}
+	for i, arg := range args {
+		switch x := arg.(type) {
+		case Node:
+			a = appendNodePositions(a, x)
+			args[i] = c.Str(x)
+		case ast.Node:
+			b, _ := cueformat.Node(x)
+			if p := x.Pos(); p != token.NoPos {
 				a = append(a, p)
-			} else if v, ok := n.(*Vertex); ok {
-				for _, c := range v.Conjuncts {
-					if p := pos(c.x); p != token.NoPos {
-						a = append(a, p)
-					}
-				}
 			}
+			args[i] = string(b)
+		case Feature:
+			args[i] = x.SelectorString(c.Runtime)
 		}
 	}
 	return &ValueError{
diff --git a/internal/core/adt/eval.go b/internal/core/adt/eval.go
index d2086cb..fa00b20 100644
--- a/internal/core/adt/eval.go
+++ b/internal/core/adt/eval.go
@@ -974,12 +974,11 @@
 
 	var err *ValueError
 	if k1 == k2 {
-		err = ctx.NewPosf(token.NoPos,
-			"conflicting values %s and %s", ctx.Str(v1), ctx.Str(v2))
+		err = ctx.NewPosf(token.NoPos, "conflicting values %s and %s", v1, v2)
 	} else {
 		err = ctx.NewPosf(token.NoPos,
 			"conflicting values %s and %s (mismatched types %s and %s)",
-			ctx.Str(v1), ctx.Str(v2), k1, k2)
+			v1, v2, k1, k2)
 	}
 
 	err.AddPosition(v1)
@@ -1005,7 +1004,7 @@
 		} else {
 			n.addErr(ctx.Newf(
 				"conflicting value %s (mismatched types %s and %s)",
-				ctx.Str(v), n.kind, k))
+				v, n.kind, k))
 		}
 	}
 
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 648b74d..2e44ce8 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -477,13 +477,12 @@
 			mask |= NullKind
 		}
 		if k&mask != 0 {
-			ctx.addErrf(IncompleteError, ctx.pos(),
-				"non-concrete value %s for bound %s", ctx.Str(x.Expr), x.Op)
+			ctx.addErrf(IncompleteError, token.NoPos, // TODO(errors): use ctx.pos()?
+				"non-concrete value %s for bound %s", x.Expr, x.Op)
 			return nil
 		}
 		err := ctx.NewPosf(pos(x.Expr),
-			"invalid value %s (type %s) for bound %s",
-			ctx.Str(v), k, x.Op)
+			"invalid value %s (type %s) for bound %s", v, k, x.Op)
 		return &Bottom{Err: err}
 	}
 
@@ -565,8 +564,8 @@
 	}
 	if v.Concreteness() > Concrete {
 		// TODO(errors): analyze dependencies of x.Expr to get positions.
-		ctx.addErrf(IncompleteError, ctx.pos(),
-			"non-concrete value %s for bound %s", ctx.Str(x.Expr), x.Op)
+		ctx.addErrf(IncompleteError, token.NoPos, // TODO(errors): use ctx.pos()?
+			"non-concrete value %s for bound %s", x.Expr, x.Op)
 		return nil
 	}
 	return &BoundValue{x.Src, x.Op, v}
@@ -616,8 +615,7 @@
 		}
 		// TODO(errors): use "invalid value %v (not an %s)" if x is a
 		// predeclared identifier such as `int`.
-		err := c.Newf("invalid value %v (out of bound %s)",
-			c.Str(y), c.Str(x))
+		err := c.Newf("invalid value %v (out of bound %s)", y, x)
 		err.AddPosition(y)
 		return &Bottom{Src: c.src, Err: err, Code: EvalError}
 
@@ -934,8 +932,7 @@
 
 	switch v := v.(type) {
 	case nil:
-		c.addErrf(IncompleteError, c.pos(),
-			"non-concrete slice subject %s", c.Str(x.X))
+		c.addErrf(IncompleteError, c.pos(), "non-concrete slice subject %s", x.X)
 		return nil
 	case *Vertex:
 		if !v.IsList() {
@@ -997,7 +994,7 @@
 	if isError(v) {
 		return v
 	}
-	return c.NewErrf("cannot slice %v (type %s)", c.Str(v), v.Kind())
+	return c.NewErrf("cannot slice %v (type %s)", v, v.Kind())
 }
 
 // An Interpolation is a string interpolation.
@@ -1097,10 +1094,10 @@
 	}
 	if k&expectedKind != BottomKind {
 		c.addErrf(IncompleteError, pos(x.X),
-			"operand %s of '%s' not concrete (was %s)", c.Str(x.X), op, k)
+			"operand %s of '%s' not concrete (was %s)", x.X, op, k)
 		return nil
 	}
-	return c.NewErrf("invalid operation %s (%s %s)", c.Str(x), op, k)
+	return c.NewErrf("invalid operation %s (%s %s)", x, op, k)
 }
 
 // BinaryExpr is a binary expression.
@@ -1244,8 +1241,7 @@
 		// value to be called with zero arguments.
 		switch {
 		case f.Src != nil:
-			c.addErrf(0, pos(x.Fun),
-				"cannot call previously called validator %s", c.Str(x.Fun))
+			c.AddErrf("cannot call previously called validator %s", x.Fun)
 
 		case f.Builtin.IsValidator(len(x.Args)):
 			v := *f
@@ -1257,8 +1253,7 @@
 		}
 
 	default:
-		c.addErrf(0, pos(x.Fun), "cannot call non-function %s (type %s)",
-			c.Str(x.Fun), kind(fun))
+		c.AddErrf("cannot call non-function %s (type %s)", x.Fun, kind(fun))
 		return nil
 	}
 	args := []Value{}
@@ -1269,7 +1264,7 @@
 			// There SHOULD be an error in the context. If not, we generate
 			// one.
 			c.Assertf(pos(x.Fun), c.HasErr(),
-				"argument %d to function %s is incomplete", i, c.Str(x.Fun))
+				"argument %d to function %s is incomplete", i, x.Fun)
 
 		case *Bottom:
 			// TODO(errors): consider adding an argument index for this errors.
@@ -1488,7 +1483,7 @@
 		buf.WriteString(")")
 	}
 
-	vErr := c.NewPosf(src, "invalid value %s (does not satisfy %s)", c.Str(args[0]), buf.String())
+	vErr := c.NewPosf(src, "invalid value %s (does not satisfy %s)", args[0], buf.String())
 	vErr.err = err
 
 	for _, v := range args {
diff --git a/internal/core/adt/feature.go b/internal/core/adt/feature.go
index 29585c5..2df3656 100644
--- a/internal/core/adt/feature.go
+++ b/internal/core/adt/feature.go
@@ -196,7 +196,7 @@
 			if src == nil {
 				src = v
 			}
-			c.AddErrf("invalid index %v: %v", c.Str(src), err)
+			c.AddErrf("invalid index %v: %v", src, err)
 			return InvalidLabel
 		}
 		if i < 0 {
@@ -204,11 +204,10 @@
 			case nil, *Num, *UnaryExpr:
 				// If the value is a constant, we know it is always an error.
 				// UnaryExpr is an approximation for a constant value here.
-				c.AddErrf("invalid index %s (index must be non-negative)",
-					c.Str(x))
+				c.AddErrf("invalid index %s (index must be non-negative)", x)
 			default:
 				// Use a different message is it is the result of evaluation.
-				c.AddErrf("index %s out of range [%s]", c.Str(src), c.Str(x))
+				c.AddErrf("index %s out of range [%s]", src, x)
 			}
 			return InvalidLabel
 		}
@@ -224,8 +223,7 @@
 
 	default:
 		if src != nil {
-			c.AddErrf("invalid index %s (invalid type %v)",
-				c.Str(src), v.Kind())
+			c.AddErrf("invalid index %s (invalid type %v)", src, v.Kind())
 		} else {
 			c.AddErrf("invalid index type %v", v.Kind())
 		}
diff --git a/internal/core/adt/simplify.go b/internal/core/adt/simplify.go
index 4bf0876..0d1d76b 100644
--- a/internal/core/adt/simplify.go
+++ b/internal/core/adt/simplify.go
@@ -154,7 +154,7 @@
 			fallthrough
 
 		case d.Negative:
-			return ctx.NewErrf("incompatible bounds %v and %v", ctx.Str(x), ctx.Str(y))
+			return ctx.NewErrf("incompatible bounds %v and %v", x, y)
 		}
 
 	case x.Op == NotEqualOp:
diff --git a/internal/core/compile/builtin.go b/internal/core/compile/builtin.go
index feb7ade..a36531f 100644
--- a/internal/core/compile/builtin.go
+++ b/internal/core/compile/builtin.go
@@ -69,7 +69,7 @@
 			if k&supportedByLen == adt.BottomKind {
 				return c.NewErrf("invalid argument type %v", k)
 			}
-			b := c.NewErrf("incomplete argument %s (type %v)", c.Str(v), k)
+			b := c.NewErrf("incomplete argument %s (type %v)", v, k)
 			b.Code = adt.IncompleteError
 			return b
 		}
diff --git a/internal/core/subsume/vertex.go b/internal/core/subsume/vertex.go
index 3d183f8..cb9d7c2 100644
--- a/internal/core/subsume/vertex.go
+++ b/internal/core/subsume/vertex.go
@@ -52,7 +52,7 @@
 
 	case *adt.ListMarker:
 		if !y.IsList() {
-			s.errf("list does not subsume %s (type %s)", ctx.Str(y), y.Kind())
+			s.errf("list does not subsume %s (type %s)", y, y.Kind())
 			return false
 		}
 		if !s.listVertices(x, y) {
@@ -157,8 +157,7 @@
 		s.gt = a
 		s.lt = y
 
-		s.errf("field %s not present in %s",
-			f.SelectorString(ctx), ctx.Str(y))
+		s.errf("field %s not present in %s", f, y)
 		return false
 	}
 
@@ -195,8 +194,7 @@
 			if s.Profile.IgnoreClosedness {
 				continue
 			}
-			s.errf("field %s not allowed in closed struct",
-				f.SelectorString(ctx))
+			s.errf("field %s not allowed in closed struct", f)
 			return false
 		}
 
diff --git a/internal/core/validate/validate.go b/internal/core/validate/validate.go
index 1af60c4..4b1fdbe 100644
--- a/internal/core/validate/validate.go
+++ b/internal/core/validate/validate.go
@@ -92,7 +92,7 @@
 			x := x.Value()
 			v.add(&adt.Bottom{
 				Code: adt.IncompleteError,
-				Err:  v.ctx.Newf("incomplete value %v", v.ctx.Str(x)),
+				Err:  v.ctx.Newf("incomplete value %v", x),
 			})
 		}
 	}
diff --git a/internal/core/validate/validate_test.go b/internal/core/validate/validate_test.go
index 9d395a2..1e56edb 100644
--- a/internal/core/validate/validate_test.go
+++ b/internal/core/validate/validate_test.go
@@ -100,7 +100,7 @@
 		y: 2 + x
 		x: string
 		`,
-		out: "incomplete\ny: non-concrete value string in operand to +:\n    test:2:6",
+		out: "incomplete\ny: non-concrete value string in operand to +:\n    test:2:6\n    test:3:3",
 	}, {
 		desc: "allowed incomplete cycle",
 		in: `
diff --git a/pkg/struct/testdata/gen.txtar b/pkg/struct/testdata/gen.txtar
index a73f573..0476429 100644
--- a/pkg/struct/testdata/gen.txtar
+++ b/pkg/struct/testdata/gen.txtar
@@ -15,6 +15,7 @@
     ./in.cue:3:27
 t4: invalid value {a:1} (does not satisfy struct.MaxFields(0)):
     ./in.cue:6:5
+    ./in.cue:6:1
     ./in.cue:6:22
 
 Result: