internal/core/eval: add more positions for closedness errors
- Keep track of position stack in context.
- Copy the positions on first use.
Change-Id: Ibaab87557353484d62277cd08f7f2f7fc619f9c9
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6682
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/cue/testdata/definitions/026_combined_definitions.txtar b/cue/testdata/definitions/026_combined_definitions.txtar
index 3cea8a6..5da4669 100644
--- a/cue/testdata/definitions/026_combined_definitions.txtar
+++ b/cue/testdata/definitions/026_combined_definitions.txtar
@@ -118,8 +118,15 @@
}
-- out/eval --
Errors:
-#D4.env: field `b` not allowed
-d1.env: field `c` not allowed
+#D4.env: field `b` not allowed:
+ ./in.cue:27:7
+ ./in.cue:30:1
+ ./in.cue:30:6
+d1.env: field `c` not allowed:
+ ./in.cue:2:1
+ ./in.cue:3:7
+ ./in.cue:4:7
+ ./in.cue:9:17
Result:
(_|_){
@@ -137,7 +144,11 @@
d1: (_|_){
// [eval]
env: (_|_){
- // [eval] d1.env: field `c` not allowed
+ // [eval] d1.env: field `c` not allowed:
+ // ./in.cue:2:1
+ // ./in.cue:3:7
+ // ./in.cue:4:7
+ // ./in.cue:9:17
a: (string){ "A" }
b: (string){ "B" }
c: (string){ "C" }
@@ -160,7 +171,10 @@
#D4: (_|_){
// [eval]
env: (_|_){
- // [eval] #D4.env: field `b` not allowed
+ // [eval] #D4.env: field `b` not allowed:
+ // ./in.cue:27:7
+ // ./in.cue:30:1
+ // ./in.cue:30:6
a: (int){ int }
b: (int){ int }
}
diff --git a/cue/testdata/definitions/032_definitions_with_embedding.txtar b/cue/testdata/definitions/032_definitions_with_embedding.txtar
index a60d57a..8727fdf 100644
--- a/cue/testdata/definitions/032_definitions_with_embedding.txtar
+++ b/cue/testdata/definitions/032_definitions_with_embedding.txtar
@@ -81,7 +81,11 @@
}
-- out/eval --
Errors:
-#e1.a: field `d` not allowed
+#e1.a: field `d` not allowed:
+ ./in.cue:1:1
+ ./in.cue:2:5
+ ./in.cue:7:5
+ ./in.cue:12:15
Result:
(_|_){
@@ -101,7 +105,11 @@
#e1: (_|_){
// [eval]
a: (_|_){
- // [eval] #e1.a: field `d` not allowed
+ // [eval] #e1.a: field `d` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:2:5
+ // ./in.cue:7:5
+ // ./in.cue:12:15
b: (int){ int }
c: (int){ int }
d: (int){ 4 }
diff --git "a/cue/testdata/definitions/033_Issue_\043153.txtar" "b/cue/testdata/definitions/033_Issue_\043153.txtar"
index 3b99002..d1bee19 100644
--- "a/cue/testdata/definitions/033_Issue_\043153.txtar"
+++ "b/cue/testdata/definitions/033_Issue_\043153.txtar"
@@ -62,7 +62,11 @@
}
-- out/eval --
Errors:
-listOfCloseds.0: field `b` not allowed
+listOfCloseds.0: field `b` not allowed:
+ ./in.cue:5:1
+ ./in.cue:5:10
+ ./in.cue:14:18
+ ./in.cue:15:20
Result:
(_|_){
@@ -70,7 +74,11 @@
listOfCloseds: (_|_){
// [eval]
0: (_|_){
- // [eval] listOfCloseds.0: field `b` not allowed
+ // [eval] listOfCloseds.0: field `b` not allowed:
+ // ./in.cue:5:1
+ // ./in.cue:5:10
+ // ./in.cue:14:18
+ // ./in.cue:15:20
a: (int){ |(*(int){ 0 }, (int){ int }) }
b: (int){ 2 }
}
@@ -79,7 +87,7 @@
listOfCloseds: (list){
}
}
- #Closed: (struct){
+ #Closed: (#struct){
a: (int){ |(*(int){ 0 }, (int){ int }) }
}
Junk: (struct){
diff --git a/cue/testdata/definitions/037_closing_with_comprehensions.txtar b/cue/testdata/definitions/037_closing_with_comprehensions.txtar
index 25fc9b3..e71e890 100644
--- a/cue/testdata/definitions/037_closing_with_comprehensions.txtar
+++ b/cue/testdata/definitions/037_closing_with_comprehensions.txtar
@@ -105,8 +105,15 @@
-- out/eval --
Errors:
#D: cannot mix bulk optional fields with dynamic fields, embeddings, or comprehensions within the same struct
-#E: field `f3` not allowed
-a: field `f3` not allowed
+#E: field `f3` not allowed:
+ ./in.cue:1:1
+ ./in.cue:1:5
+ ./in.cue:27:10
+ ./in.cue:28:24
+a: field `f3` not allowed:
+ ./in.cue:1:1
+ ./in.cue:1:5
+ ./in.cue:4:10
Result:
(_|_){
@@ -126,13 +133,20 @@
f1: (int){ int }
}
#E: (_|_){
- // [eval] #E: field `f3` not allowed
+ // [eval] #E: field `f3` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:1:5
+ // ./in.cue:27:10
+ // ./in.cue:28:24
f1: (int){ int }
f2: (int){ int }
f3: (int){ int }
}
a: (_|_){
- // [eval] a: field `f3` not allowed
+ // [eval] a: field `f3` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:1:5
+ // ./in.cue:4:10
f1: (int){ int }
f2: (int){ int }
f3: (int){ int }
diff --git a/cue/testdata/definitions/037_conjunction_of_optional_sets.txtar b/cue/testdata/definitions/037_conjunction_of_optional_sets.txtar
index d3a9543..db72e06 100644
--- a/cue/testdata/definitions/037_conjunction_of_optional_sets.txtar
+++ b/cue/testdata/definitions/037_conjunction_of_optional_sets.txtar
@@ -52,8 +52,19 @@
}
-- out/eval --
Errors:
-c: field `aaa` not allowed
-d: field `aaa` not allowed
+c: field `aaa` not allowed:
+ ./in.cue:1:1
+ ./in.cue:1:5
+ ./in.cue:4:1
+ ./in.cue:4:5
+ ./in.cue:9:10
+d: field `aaa` not allowed:
+ ./in.cue:1:1
+ ./in.cue:1:5
+ ./in.cue:4:1
+ ./in.cue:4:5
+ ./in.cue:11:5
+ ./in.cue:12:9
Result:
(_|_){
@@ -65,13 +76,24 @@
#C: (#struct){
}
c: (_|_){
- // [eval] c: field `aaa` not allowed
+ // [eval] c: field `aaa` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:1:5
+ // ./in.cue:4:1
+ // ./in.cue:4:5
+ // ./in.cue:9:10
aaa: (int){ 3 }
}
#D: (#struct){
}
d: (_|_){
- // [eval] d: field `aaa` not allowed
+ // [eval] d: field `aaa` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:1:5
+ // ./in.cue:4:1
+ // ./in.cue:4:5
+ // ./in.cue:11:5
+ // ./in.cue:12:9
aaa: (int){ 3 }
}
}
diff --git a/cue/testdata/definitions/038_continue_recursive_closing_for_optionals.txtar b/cue/testdata/definitions/038_continue_recursive_closing_for_optionals.txtar
index 9c16b91..af888a7 100644
--- a/cue/testdata/definitions/038_continue_recursive_closing_for_optionals.txtar
+++ b/cue/testdata/definitions/038_continue_recursive_closing_for_optionals.txtar
@@ -38,7 +38,10 @@
}
-- out/eval --
Errors:
-a.v: field `b` not allowed
+a.v: field `b` not allowed:
+ ./in.cue:1:1
+ ./in.cue:2:12
+ ./in.cue:5:5
Result:
(_|_){
@@ -48,7 +51,10 @@
a: (_|_){
// [eval]
v: (_|_){
- // [eval] a.v: field `b` not allowed
+ // [eval] a.v: field `b` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:2:12
+ // ./in.cue:5:5
b: (int){ int }
a: (int){ int }
}
diff --git a/cue/testdata/eval/closed_disjunction.txtar b/cue/testdata/eval/closed_disjunction.txtar
index 3691706..78e6872 100644
--- a/cue/testdata/eval/closed_disjunction.txtar
+++ b/cue/testdata/eval/closed_disjunction.txtar
@@ -15,7 +15,10 @@
}
-- out/eval --
Errors:
-b: field `d` not allowed
+b: field `d` not allowed:
+ ./in.cue:1:5
+ ./in.cue:3:35
+ ./in.cue:11:9
Result:
(_|_){
@@ -27,7 +30,10 @@
c: (int){ 3 }
}
b: (_|_){
- // [eval] b: field `d` not allowed
+ // [eval] b: field `d` not allowed:
+ // ./in.cue:1:5
+ // ./in.cue:3:35
+ // ./in.cue:11:9
c: (int){ 3 }
d: (int){ 4 }
}
diff --git a/cue/testdata/eval/closedness.txtar b/cue/testdata/eval/closedness.txtar
index 8371eca..2023486 100644
--- a/cue/testdata/eval/closedness.txtar
+++ b/cue/testdata/eval/closedness.txtar
@@ -18,7 +18,11 @@
}
-- out/eval --
Errors:
-a.q: field `e` not allowed
+a.q: field `e` not allowed:
+ ./in.cue:1:1
+ ./in.cue:1:5
+ ./in.cue:6:8
+ ./in.cue:13:8
Result:
(_|_){
@@ -37,7 +41,11 @@
// [eval]
b: (int){ 3 }
q: (_|_){
- // [eval] a.q: field `e` not allowed
+ // [eval] a.q: field `e` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:1:5
+ // ./in.cue:6:8
+ // ./in.cue:13:8
c: (int){ 2 }
d: (int){ int }
e: (int){ 43 }
diff --git a/cue/testdata/fulleval/035_optionals_with_label_filters.txtar b/cue/testdata/fulleval/035_optionals_with_label_filters.txtar
index 40eda99..9204d37 100644
--- a/cue/testdata/fulleval/035_optionals_with_label_filters.txtar
+++ b/cue/testdata/fulleval/035_optionals_with_label_filters.txtar
@@ -102,9 +102,21 @@
}
-- out/eval --
Errors:
-jobs1: field `foo1` not allowed
-jobs3: field `fooTest1` not allowed
-jobs3.fooTest1: field `cmd` not allowed
+jobs1: field `foo1` not allowed:
+ ./in.cue:6:8
+ ./in.cue:7:2
+ ./in.cue:9:2
+ ./in.cue:16:8
+jobs3: field `fooTest1` not allowed:
+ ./in.cue:6:8
+ ./in.cue:7:2
+ ./in.cue:9:2
+ ./in.cue:22:8
+ ./in.cue:23:8
+jobs3.fooTest1: field `cmd` not allowed:
+ ./in.cue:2:7
+ ./in.cue:6:8
+ ./in.cue:23:18
jobs2.fooTest.name: invalid value "badName" (out of bound =~"^test"):
./in.cue:9:22
@@ -124,7 +136,11 @@
}
}
jobs1: (_|_){
- // [eval] jobs1: field `foo1` not allowed
+ // [eval] jobs1: field `foo1` not allowed:
+ // ./in.cue:6:8
+ // ./in.cue:7:2
+ // ./in.cue:9:2
+ // ./in.cue:16:8
foo1: (#struct){
}
}
@@ -140,9 +156,17 @@
}
}
jobs3: (_|_){
- // [eval] jobs3: field `fooTest1` not allowed
+ // [eval] jobs3: field `fooTest1` not allowed:
+ // ./in.cue:6:8
+ // ./in.cue:7:2
+ // ./in.cue:9:2
+ // ./in.cue:22:8
+ // ./in.cue:23:8
fooTest1: (_|_){
- // [eval] jobs3.fooTest1: field `cmd` not allowed
+ // [eval] jobs3.fooTest1: field `cmd` not allowed:
+ // ./in.cue:2:7
+ // ./in.cue:6:8
+ // ./in.cue:23:18
name: (string){ "badName" }
cmd: (string){ string }
}
diff --git a/cue/testdata/resolve/025_definitions.txtar b/cue/testdata/resolve/025_definitions.txtar
index d2eab90..9bb0833 100644
--- a/cue/testdata/resolve/025_definitions.txtar
+++ b/cue/testdata/resolve/025_definitions.txtar
@@ -119,8 +119,14 @@
}
-- out/eval --
Errors:
-foo: field `feild` not allowed
-foo1.recursive: field `feild` not allowed
+foo: field `feild` not allowed:
+ ./in.cue:1:1
+ ./in.cue:1:7
+ ./in.cue:13:6
+foo1.recursive: field `feild` not allowed:
+ ./in.cue:1:1
+ ./in.cue:3:13
+ ./in.cue:18:13
Result:
(_|_){
@@ -136,7 +142,10 @@
field2: (string){ string }
}
foo: (_|_){
- // [eval] foo: field `feild` not allowed
+ // [eval] foo: field `feild` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:1:7
+ // ./in.cue:13:6
field: (int){ int }
recursive: (#struct){
field: (string){ string }
@@ -147,7 +156,10 @@
// [eval]
field: (int){ 2 }
recursive: (_|_){
- // [eval] foo1.recursive: field `feild` not allowed
+ // [eval] foo1.recursive: field `feild` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:3:13
+ // ./in.cue:18:13
field: (string){ string }
feild: (int){ 2 }
}
diff --git a/cue/testdata/resolve/029_non-closed_definition_carries_over_closedness_to_enclosed_template.txtar b/cue/testdata/resolve/029_non-closed_definition_carries_over_closedness_to_enclosed_template.txtar
index 09f65bf..69be4d5 100644
--- a/cue/testdata/resolve/029_non-closed_definition_carries_over_closedness_to_enclosed_template.txtar
+++ b/cue/testdata/resolve/029_non-closed_definition_carries_over_closedness_to_enclosed_template.txtar
@@ -104,9 +104,18 @@
}
-- out/eval --
Errors:
-a.v: field `b` not allowed
-b.w: field `c` not allowed
-c.w.0: field `d` not allowed
+a.v: field `b` not allowed:
+ ./in.cue:1:1
+ ./in.cue:2:12
+ ./in.cue:5:5
+b.w: field `c` not allowed:
+ ./in.cue:7:1
+ ./in.cue:8:23
+ ./in.cue:11:5
+c.w.0: field `d` not allowed:
+ ./in.cue:13:1
+ ./in.cue:14:13
+ ./in.cue:17:6
Result:
(_|_){
@@ -116,7 +125,10 @@
a: (_|_){
// [eval]
v: (_|_){
- // [eval] a.v: field `b` not allowed
+ // [eval] a.v: field `b` not allowed:
+ // ./in.cue:1:1
+ // ./in.cue:2:12
+ // ./in.cue:5:5
b: (int){ int }
a: (int){ int }
}
@@ -126,7 +138,10 @@
b: (_|_){
// [eval]
w: (_|_){
- // [eval] b.w: field `c` not allowed
+ // [eval] b.w: field `c` not allowed:
+ // ./in.cue:7:1
+ // ./in.cue:8:23
+ // ./in.cue:11:5
c: (int){ int }
b: (int){ int }
}
@@ -138,7 +153,10 @@
w: (_|_){
// [eval]
0: (_|_){
- // [eval] c.w.0: field `d` not allowed
+ // [eval] c.w.0: field `d` not allowed:
+ // ./in.cue:13:1
+ // ./in.cue:14:13
+ // ./in.cue:17:6
d: (int){ int }
a: (int){ int }
}
diff --git a/cue/testdata/resolve/030_definitions_with_disjunctions.txtar b/cue/testdata/resolve/030_definitions_with_disjunctions.txtar
index 84774b6..d21c4cd 100644
--- a/cue/testdata/resolve/030_definitions_with_disjunctions.txtar
+++ b/cue/testdata/resolve/030_definitions_with_disjunctions.txtar
@@ -61,7 +61,10 @@
}
-- out/eval --
Errors:
-bar: field `c` not allowed
+bar: field `c` not allowed:
+ ./in.cue:1:7
+ ./in.cue:5:2
+ ./in.cue:12:6
Result:
(_|_){
@@ -78,7 +81,10 @@
a: (int){ 1 }
}
bar: (_|_){
- // [eval] bar: field `c` not allowed
+ // [eval] bar: field `c` not allowed:
+ // ./in.cue:1:7
+ // ./in.cue:5:2
+ // ./in.cue:12:6
field: (int){ int }
c: (int){ 2 }
b: (int){ 2 }
diff --git a/cue/testdata/resolve/035_excluded_embedding_from_closing.txtar b/cue/testdata/resolve/035_excluded_embedding_from_closing.txtar
index 4aa95d6..e719e07 100644
--- a/cue/testdata/resolve/035_excluded_embedding_from_closing.txtar
+++ b/cue/testdata/resolve/035_excluded_embedding_from_closing.txtar
@@ -64,8 +64,14 @@
}
-- out/eval --
Errors:
-V.b: field `extra` not allowed
-V.c: field `e` not allowed
+V.b: field `extra` not allowed:
+ ./in.cue:1:5
+ ./in.cue:6:10
+ ./in.cue:11:5
+V.c: field `e` not allowed:
+ ./in.cue:1:5
+ ./in.cue:4:6
+ ./in.cue:10:5
Result:
(_|_){
@@ -84,7 +90,10 @@
V: (_|_){
// [eval]
c: (_|_){
- // [eval] V.c: field `e` not allowed
+ // [eval] V.c: field `e` not allowed:
+ // ./in.cue:1:5
+ // ./in.cue:4:6
+ // ./in.cue:10:5
d: (int){ int }
e: (int){ int }
}
@@ -92,7 +101,10 @@
c: (int){ int }
}
b: (_|_){
- // [eval] V.b: field `extra` not allowed
+ // [eval] V.b: field `extra` not allowed:
+ // ./in.cue:1:5
+ // ./in.cue:6:10
+ // ./in.cue:11:5
open: (int){ int }
extra: (int){ int }
}
diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go
index 9d8f3af..1a26c5c 100644
--- a/internal/core/adt/context.go
+++ b/internal/core/adt/context.go
@@ -107,9 +107,10 @@
type OpContext struct {
config
- e *Environment
- src ast.Node
- errs *Bottom
+ e *Environment
+ src ast.Node
+ errs *Bottom
+ positions []Node // keep track of error positions
// vertex is used to determine the path location in case of error. Turning
// this into a stack could also allow determining the cyclic path for
diff --git a/internal/core/adt/errors.go b/internal/core/adt/errors.go
index ec73924..4736dbb 100644
--- a/internal/core/adt/errors.go
+++ b/internal/core/adt/errors.go
@@ -203,9 +203,10 @@
// A valueError is returned as a result of evaluating a value.
type valueError struct {
- r Runtime
- v *Vertex
- pos []token.Pos
+ r Runtime
+ v *Vertex
+ pos token.Pos
+ auxpos []token.Pos
errors.Message
}
@@ -213,17 +214,45 @@
return c.vertex
}
+// MarkPositions marks the current position stack.
+func (c *OpContext) MarkPositions() int {
+ return len(c.positions)
+}
+
+// ReleasePositions sets the position state to one from a call to MarkPositions.
+func (c *OpContext) ReleasePositions(p int) {
+ c.positions = c.positions[:p]
+}
+
+func (c *OpContext) AddPosition(n Node) {
+ c.positions = append(c.positions, n)
+}
+
func (c *OpContext) Newf(format string, args ...interface{}) *valueError {
return c.NewPosf(c.pos(), format, args...)
}
-func (c *OpContext) NewPosf(pos token.Pos, format string, args ...interface{}) *valueError {
+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 = 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)
+ }
+ }
+ }
+ }
+ }
return &valueError{
- r: c.Runtime,
- v: c.errNode(),
- // TODO: leave ni if it can be derived from the source and save an
- // allocation.
- pos: []token.Pos{pos},
+ r: c.Runtime,
+ v: c.errNode(),
+ pos: p,
+ auxpos: a,
Message: errors.NewMessage(format, args),
}
}
@@ -233,15 +262,11 @@
}
func (e *valueError) Position() token.Pos {
- if len(e.pos) == 0 {
- // TODO: retrieve from source
- return token.NoPos
- }
- return e.pos[0]
+ return e.pos
}
-func (e *valueError) InputPositions() []token.Pos {
- return e.pos
+func (e *valueError) InputPositions() (a []token.Pos) {
+ return e.auxpos
}
func (e *valueError) Path() (a []string) {
diff --git a/internal/core/eval/closed.go b/internal/core/eval/closed.go
index 80f6ed8..5c9ae62 100644
--- a/internal/core/eval/closed.go
+++ b/internal/core/eval/closed.go
@@ -114,7 +114,7 @@
// Acceptor. This could be implemented as an allocation-free wrapper type around
// a Disjunction. This will require a bit more API cleaning, though.
func newDisjunctionAcceptor(x *adt.Disjunction) adt.Acceptor {
- tree := &CloseDef{}
+ tree := &CloseDef{Src: x}
sets := []fieldSet{}
for _, d := range x.Values {
if a, _ := d.Closed.(*acceptor); a != nil {
@@ -142,6 +142,7 @@
// of the set of allowed fields in that case and the embeddings will not add
// any value.
type CloseDef struct {
+ Src adt.Node // for error reporting
ID uint32
IsAnd bool
List []*CloseDef
@@ -216,7 +217,7 @@
return buf[0]
}
- return &CloseDef{ID: c.ID, IsAnd: c.IsAnd, List: buf[:k]}
+ return &CloseDef{Src: c.Src, ID: c.ID, IsAnd: c.IsAnd, List: buf[:k]}
}
// UpdateReplace is called after evaluating a conjunct at the top of the arc
@@ -313,6 +314,8 @@
// "and" semantics of conjunctions. It generates an error if a field is not
// allowed.
func (n *acceptor) verifyArcAllowed(ctx *adt.OpContext, f adt.Feature) *adt.Bottom {
+ defer ctx.ReleasePositions(ctx.MarkPositions())
+
// TODO(perf): this will also generate a message for f == 0. Do something
// more clever and only generate this when it is a user error.
filter := f.IsString() || f == adt.InvalidLabel
@@ -367,5 +370,20 @@
}
}
}
+ collectPositions(ctx, n.tree)
+ for _, o := range n.fields {
+ if o.pos != nil {
+ ctx.AddPosition(o.pos)
+ }
+ }
return false
}
+
+func collectPositions(ctx *adt.OpContext, c *CloseDef) {
+ if c.Src != nil {
+ ctx.AddPosition(c.Src)
+ }
+ for _, x := range c.List {
+ collectPositions(ctx, x)
+ }
+}
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 7cbe503..a912c4b 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -188,6 +188,7 @@
Parent: v.Parent,
Value: &adt.StructMarker{},
Arcs: arcs,
+ Closed: result.Closed,
}
}
// *v = save // DO NOT ADD.
@@ -551,6 +552,34 @@
}
}
+ n.updateClosedInfo()
+
+ if cyclic := n.hasCycle && !n.hasNonCycle; cyclic {
+ n.node.Value = adt.CombineErrors(nil, n.node.Value, &adt.Bottom{
+ Code: adt.StructuralCycleError,
+ Err: ctx.Newf("structural cycle"),
+ Value: n.node.Value,
+ // TODO: probably, this should have the referenced arc.
+ })
+ } else {
+ // Visit arcs recursively to validate and compute error.
+ for _, a := range n.node.Arcs {
+ // Call UpdateStatus here to be absolutely sure the status is set
+ // correctly and that we are not regressing.
+ n.node.UpdateStatus(adt.EvaluatingArcs)
+ n.eval.Unify(ctx, a, adt.Finalized)
+ if err, _ := a.Value.(*adt.Bottom); err != nil {
+ n.node.AddChildError(err)
+ }
+ }
+ }
+
+ n.node.UpdateStatus(adt.Finalized)
+}
+
+func (n *nodeContext) updateClosedInfo() {
+ ctx := n.ctx
+
var c *CloseDef
if a, _ := n.node.Closed.(*acceptor); a != nil {
c = a.tree
@@ -559,7 +588,7 @@
updated := updateClosed(c, n.replace)
if updated == nil && n.needClose {
- updated = &CloseDef{}
+ updated = &CloseDef{Src: n.node}
}
// TODO retrieve from env.
@@ -584,8 +613,6 @@
n.node.Closed = m
}
- cyclic := n.hasCycle && !n.hasNonCycle
-
// Visit arcs recursively to validate and compute error.
for _, a := range n.node.Arcs {
if updated != nil {
@@ -605,27 +632,7 @@
// or at least don't record recursive error.
// continue
}
- // Call UpdateStatus here to be absolutely sure the status is set
- // correctly and that we are not regressing.
- if !cyclic {
- n.node.UpdateStatus(adt.EvaluatingArcs)
- n.eval.Unify(ctx, a, adt.Finalized)
- if err, _ := a.Value.(*adt.Bottom); err != nil {
- n.node.AddChildError(err)
- }
- }
}
-
- if cyclic {
- n.node.Value = adt.CombineErrors(nil, n.node.Value, &adt.Bottom{
- Code: adt.StructuralCycleError,
- Err: ctx.Newf("structural cycle"),
- Value: n.node.Value,
- // TODO: probably, this should have the referenced arc.
- })
- }
-
- n.node.UpdateStatus(adt.Finalized)
}
// TODO: this is now a sentinel. Use a user-facing error that traces where
@@ -1112,7 +1119,7 @@
current, n.newClose = n.newClose, current
if current == nil {
- current = &CloseDef{ID: id}
+ current = &CloseDef{ID: id, Src: arc}
}
n.addAnd(current)
}
@@ -1315,7 +1322,7 @@
var hasOther, hasBulk adt.Node
- opt := fieldSet{env: childEnv}
+ opt := fieldSet{pos: s, env: childEnv}
for _, d := range s.Decls {
switch x := d.(type) {
@@ -1352,7 +1359,7 @@
current, n.newClose = n.newClose, current
if current == nil {
- current = &CloseDef{ID: id} // TODO: isClosed?
+ current = &CloseDef{Src: s, ID: id} // TODO: isClosed?
} else {
// n.needClose = true
}
@@ -1678,7 +1685,7 @@
continue
}
- f := fieldSet{env: l.env}
+ f := fieldSet{pos: l.list, env: l.env}
f.AddEllipsis(c, l.elipsis)
n.optionals = append(n.optionals, f)
diff --git a/internal/core/eval/optionals.go b/internal/core/eval/optionals.go
index 6b17a8d..3dc1884 100644
--- a/internal/core/eval/optionals.go
+++ b/internal/core/eval/optionals.go
@@ -21,6 +21,8 @@
// fieldSet represents the fields for a single struct literal, along
// the constraints of fields that may be added.
type fieldSet struct {
+ pos adt.Node
+
// TODO: look at consecutive identical environments to figure out
// what belongs to same definition?
env *adt.Environment