From a3f1a0f96b6a2c990290299cc3624b73b2923427 Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Fri, 23 Aug 2024 09:54:48 +0200 Subject: [PATCH] internal/core/adt: fix bug that ignores field type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit evalv3 would ignore the field type of a referenced arc if it was only partially processed. This change ensures that this is checked. Revisit this change once cyclic handling is completed for evalv3. Signed-off-by: Marcel van Lohuizen Change-Id: I4681256aa6bee78a4f12f596dde0450c9180be8e Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1199917 Unity-Result: CUE porcuepine Reviewed-by: Daniel Martí TryBot-Result: CUEcueckoo Reviewed-by: Matthew Sackman --- cue/testdata/cycle/compbottomnofinal.txtar | 161 +++++++++------ cue/testdata/eval/issue2146.txtar | 220 ++++----------------- cue/testdata/references/optional.txtar | 38 +--- internal/core/adt/unify.go | 12 +- 4 files changed, 150 insertions(+), 281 deletions(-) diff --git a/cue/testdata/cycle/compbottomnofinal.txtar b/cue/testdata/cycle/compbottomnofinal.txtar index 4e066b11a..d2f70a6cf 100644 --- a/cue/testdata/cycle/compbottomnofinal.txtar +++ b/cue/testdata/cycle/compbottomnofinal.txtar @@ -382,8 +382,10 @@ Disjuncts: 215 #userHostPort: (string){ "^(:(?P\\d+))?$" } p1: (struct){ #Y: (_|_){ - // [incomplete] small.p1.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:28:7 + // [incomplete] small.p1.#Y: undefined field: port: + // ./in.cue:28:50 + // small.p1.#X.port: cyclic reference to field port: + // ./in.cue:31:4 } #X: (_|_){ // [incomplete] small.p1.#X.port: cyclic reference to field port: @@ -396,8 +398,8 @@ Disjuncts: 215 // ./in.cue:42:4 } #Y: (_|_){ - // [incomplete] small.p2.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:50:7 + // [incomplete] small.p2.#Y: undefined field: port: + // ./in.cue:50:50 } } } @@ -405,8 +407,10 @@ Disjuncts: 215 #userHostPort: (string){ "^(:(?P\\d+))?$" } p1: (struct){ #Y: (_|_){ - // [incomplete] medium.p1.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:58:7 + // [incomplete] medium.p1.#Y: undefined field: port: + // ./in.cue:58:50 + // medium.p1.#X.port: cyclic reference to field port: + // ./in.cue:70:4 } Y: (struct){ } @@ -417,8 +421,10 @@ Disjuncts: 215 } p2: (struct){ #Y: (_|_){ - // [incomplete] medium.p2.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:80:7 + // [incomplete] medium.p2.#Y: undefined field: port: + // ./in.cue:80:50 + // medium.p2.#X.port: cyclic reference to field port: + // ./in.cue:86:4 } #X: (_|_){ // [incomplete] medium.p2.#X.port: cyclic reference to field port: @@ -431,8 +437,10 @@ Disjuncts: 215 Y: (struct){ } #Y: (_|_){ - // [incomplete] medium.p3.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:108:7 + // [incomplete] medium.p3.#Y: undefined field: port: + // ./in.cue:108:50 + // medium.p3.#X.port: cyclic reference to field port: + // ./in.cue:114:4 } #X: (_|_){ // [incomplete] medium.p3.#X.port: cyclic reference to field port: @@ -447,8 +455,10 @@ Disjuncts: 215 // ./in.cue:134:4 } #Y: (_|_){ - // [incomplete] medium.p4.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:142:7 + // [incomplete] medium.p4.#X.port: cyclic reference to field port: + // ./in.cue:134:4 + // medium.p4.#Y: undefined field: port: + // ./in.cue:142:50 } } p5: (struct){ @@ -457,8 +467,8 @@ Disjuncts: 215 // ./in.cue:150:4 } #Y: (_|_){ - // [incomplete] medium.p5.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:158:7 + // [incomplete] medium.p5.#Y: undefined field: port: + // ./in.cue:158:50 } Y: (struct){ } @@ -471,8 +481,8 @@ Disjuncts: 215 Y: (struct){ } #Y: (_|_){ - // [incomplete] medium.p6.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: - // ./in.cue:186:7 + // [incomplete] medium.p6.#Y: undefined field: port: + // ./in.cue:186:50 } } } @@ -486,6 +496,8 @@ Disjuncts: 215 X: (_|_){ // [incomplete] large.p1.X: undefined field: port: // ./in.cue:199:33 + // large.p1.#X.port: cyclic reference to field port: + // ./in.cue:211:4 } #X: (_|_){ // [incomplete] large.p1.#X.port: cyclic reference to field port: @@ -494,14 +506,16 @@ Disjuncts: 215 host: (string){ "mod.test" } } #Y: (_|_){ - // [incomplete] large.p1.X: undefined field: port: - // ./in.cue:199:33 + // [incomplete] large.p1.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: + // ./in.cue:231:7 } } p2: (struct){ X: (_|_){ // [incomplete] large.p2.X: undefined field: port: // ./in.cue:235:33 + // large.p2.#X.port: cyclic reference to field port: + // ./in.cue:252:4 } Y: (struct){ userinfo: (string){ "user" } @@ -522,6 +536,8 @@ Disjuncts: 215 X: (_|_){ // [incomplete] large.p3.X: undefined field: port: // ./in.cue:276:33 + // large.p3.#X.port: cyclic reference to field port: + // ./in.cue:288:4 } #X: (_|_){ // [incomplete] large.p3.#X.port: cyclic reference to field port: @@ -542,6 +558,8 @@ Disjuncts: 215 X: (_|_){ // [incomplete] large.p4.X: undefined field: port: // ./in.cue:317:33 + // large.p4.#X.port: cyclic reference to field port: + // ./in.cue:329:4 } #X: (_|_){ // [incomplete] large.p4.#X.port: cyclic reference to field port: @@ -564,7 +582,7 @@ Disjuncts: 215 diff old new --- old +++ new -@@ -1,35 +1,34 @@ +@@ -1,35 +1,36 @@ (struct){ minimal: (struct){ a: (_|_){ @@ -602,8 +620,10 @@ diff old new - // [cycle] small.p2.#Y: cycle with field #X.port: - // ./in.cue:50:47 + #Y: (_|_){ -+ // [incomplete] small.p1.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:28:7 ++ // [incomplete] small.p1.#Y: undefined field: port: ++ // ./in.cue:28:50 ++ // small.p1.#X.port: cyclic reference to field port: ++ // ./in.cue:31:4 + } + #X: (_|_){ + // [incomplete] small.p1.#X.port: cyclic reference to field port: @@ -616,12 +636,12 @@ diff old new + // ./in.cue:42:4 + } + #Y: (_|_){ -+ // [incomplete] small.p2.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:50:7 ++ // [incomplete] small.p2.#Y: undefined field: port: ++ // ./in.cue:50:50 } } } -@@ -36,65 +35,61 @@ +@@ -36,65 +37,69 @@ medium: (struct){ #userHostPort: (string){ "^(:(?P\\d+))?$" } p1: (struct){ @@ -652,31 +672,11 @@ diff old new - Y: (_|_){ - // [cycle] medium.p3.#X: cycle with field Y.port: - // ./in.cue:114:7 -- } -- #Y: (_|_){ -- // [incomplete] medium.p3.#Y: undefined field: port: -- // ./in.cue:108:47 -- } -- #X: (_|_){ -- // [cycle] medium.p3.#X: cycle with field Y.port: -- // ./in.cue:114:7 -- } -- } -- p4: (struct){ -- Y: (_|_){ -- // [cycle] medium.p4.#X: cycle with field Y.port: -- // ./in.cue:134:7 -- } -- #X: (_|_){ -- // [cycle] medium.p4.#X: cycle with field Y.port: -- // ./in.cue:134:7 -- } -- #Y: (_|_){ -- // [cycle] medium.p4.#X: cycle with field Y.port: -- // ./in.cue:134:7 + #Y: (_|_){ -+ // [incomplete] medium.p1.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:58:7 ++ // [incomplete] medium.p1.#Y: undefined field: port: ++ // ./in.cue:58:50 ++ // medium.p1.#X.port: cyclic reference to field port: ++ // ./in.cue:70:4 + } + Y: (struct){ + } @@ -687,8 +687,10 @@ diff old new + } + p2: (struct){ + #Y: (_|_){ -+ // [incomplete] medium.p2.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:80:7 ++ // [incomplete] medium.p2.#Y: undefined field: port: ++ // ./in.cue:80:50 ++ // medium.p2.#X.port: cyclic reference to field port: ++ // ./in.cue:86:4 + } + #X: (_|_){ + // [incomplete] medium.p2.#X.port: cyclic reference to field port: @@ -699,10 +701,31 @@ diff old new + } + p3: (struct){ + Y: (struct){ -+ } -+ #Y: (_|_){ -+ // [incomplete] medium.p3.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:108:7 + } + #Y: (_|_){ + // [incomplete] medium.p3.#Y: undefined field: port: +- // ./in.cue:108:47 +- } +- #X: (_|_){ +- // [cycle] medium.p3.#X: cycle with field Y.port: +- // ./in.cue:114:7 +- } +- } +- p4: (struct){ +- Y: (_|_){ +- // [cycle] medium.p4.#X: cycle with field Y.port: +- // ./in.cue:134:7 +- } +- #X: (_|_){ +- // [cycle] medium.p4.#X: cycle with field Y.port: +- // ./in.cue:134:7 +- } +- #Y: (_|_){ +- // [cycle] medium.p4.#X: cycle with field Y.port: +- // ./in.cue:134:7 ++ // ./in.cue:108:50 ++ // medium.p3.#X.port: cyclic reference to field port: ++ // ./in.cue:114:4 + } + #X: (_|_){ + // [incomplete] medium.p3.#X.port: cyclic reference to field port: @@ -717,8 +740,10 @@ diff old new + // ./in.cue:134:4 + } + #Y: (_|_){ -+ // [incomplete] medium.p4.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:142:7 ++ // [incomplete] medium.p4.#X.port: cyclic reference to field port: ++ // ./in.cue:134:4 ++ // medium.p4.#Y: undefined field: port: ++ // ./in.cue:142:50 } } p5: (struct){ @@ -733,12 +758,12 @@ diff old new + // ./in.cue:150:4 + } + #Y: (_|_){ -+ // [incomplete] medium.p5.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:158:7 ++ // [incomplete] medium.p5.#Y: undefined field: port: ++ // ./in.cue:158:50 } Y: (struct){ } -@@ -101,14 +96,14 @@ +@@ -101,14 +106,14 @@ } p6: (struct){ #X: (_|_){ @@ -756,12 +781,12 @@ diff old new + Y: (struct){ + } + #Y: (_|_){ -+ // [incomplete] medium.p6.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: -+ // ./in.cue:186:7 ++ // [incomplete] medium.p6.#Y: undefined field: port: ++ // ./in.cue:186:50 } } } -@@ -115,81 +110,83 @@ +@@ -115,81 +120,91 @@ large: (struct){ #userHostPort: (string){ "^((?P[[:alnum:]]*)@)?(?P[[:alnum:].]+)(:(?P\\d+))?$" } p1: (struct){ @@ -847,6 +872,8 @@ diff old new + X: (_|_){ + // [incomplete] large.p1.X: undefined field: port: + // ./in.cue:199:33 ++ // large.p1.#X.port: cyclic reference to field port: ++ // ./in.cue:211:4 + } + #X: (_|_){ + // [incomplete] large.p1.#X.port: cyclic reference to field port: @@ -855,14 +882,16 @@ diff old new + host: (string){ "mod.test" } + } + #Y: (_|_){ -+ // [incomplete] large.p1.X: undefined field: port: -+ // ./in.cue:199:33 ++ // [incomplete] large.p1.#Y: error in call to regexp.FindNamedSubmatch: non-concrete value _: ++ // ./in.cue:231:7 + } + } + p2: (struct){ + X: (_|_){ + // [incomplete] large.p2.X: undefined field: port: + // ./in.cue:235:33 ++ // large.p2.#X.port: cyclic reference to field port: ++ // ./in.cue:252:4 + } + Y: (struct){ + userinfo: (string){ "user" } @@ -883,6 +912,8 @@ diff old new + X: (_|_){ + // [incomplete] large.p3.X: undefined field: port: + // ./in.cue:276:33 ++ // large.p3.#X.port: cyclic reference to field port: ++ // ./in.cue:288:4 + } + #X: (_|_){ + // [incomplete] large.p3.#X.port: cyclic reference to field port: @@ -903,6 +934,8 @@ diff old new + X: (_|_){ + // [incomplete] large.p4.X: undefined field: port: + // ./in.cue:317:33 ++ // large.p4.#X.port: cyclic reference to field port: ++ // ./in.cue:329:4 + } + #X: (_|_){ + // [incomplete] large.p4.#X.port: cyclic reference to field port: diff --git a/cue/testdata/eval/issue2146.txtar b/cue/testdata/eval/issue2146.txtar index c17f70e3a..7108f1977 100644 --- a/cue/testdata/eval/issue2146.txtar +++ b/cue/testdata/eval/issue2146.txtar @@ -58,11 +58,10 @@ Disjuncts: 189 // ./in.cue:10:8 } y: (int){ 1 } - let list#1 = (_|_){ - // [user] + let list#1 = (#list){ 0: (_|_){ - // [user] explicit error (_|_ literal) in source: - // ./in.cue:10:8 + // [incomplete] p1.#A.list.0: cannot reference optional field: x: + // ./in.cue:6:15 } 1: (int){ 1 } } @@ -73,8 +72,14 @@ Disjuncts: 189 x?: (int){ int } y?: (int){ int } let list#1 = (#list){ - 0: (int){ int } - 1: (int){ int } + 0: (_|_){ + // [incomplete] p1.#A.list.0: cannot reference optional field: x: + // ./in.cue:6:15 + } + 1: (_|_){ + // [incomplete] p1.#A.list.1: cannot reference optional field: y: + // ./in.cue:6:18 + } } all: (#list){ } @@ -84,7 +89,10 @@ Disjuncts: 189 y?: (int){ int } let list#1 = (#list){ 0: (int){ 3 } - 1: (int){ int } + 1: (_|_){ + // [incomplete] p1.a.list.1: cannot reference optional field: y: + // ./in.cue:6:18 + } } all: (#list){ 0: (int){ 3 } @@ -95,7 +103,10 @@ Disjuncts: 189 y?: (int){ int } let list#1 = (#list){ 0: (int){ 3 } - 1: (int){ int } + 1: (_|_){ + // [incomplete] p1.b.list.1: cannot reference optional field: y: + // ./in.cue:6:18 + } } all: (#list){ 0: (int){ 3 } @@ -109,11 +120,10 @@ Disjuncts: 189 // ./in.cue:27:8 } y: (int){ 1 } - let list#2 = (_|_){ - // [user] + let list#2 = (#list){ 0: (_|_){ - // [user] explicit error (_|_ literal) in source: - // ./in.cue:27:8 + // [incomplete] p2.#A.list.0: cannot reference optional field: x: + // ./in.cue:23:15 } 1: (int){ 1 } } @@ -124,8 +134,14 @@ Disjuncts: 189 x?: (int){ int } y?: (int){ int } let list#2 = (#list){ - 0: (int){ int } - 1: (int){ int } + 0: (_|_){ + // [incomplete] p2.#A.list.0: cannot reference optional field: x: + // ./in.cue:23:15 + } + 1: (_|_){ + // [incomplete] p2.#A.list.1: cannot reference optional field: y: + // ./in.cue:23:18 + } } all: (#list){ } @@ -160,184 +176,32 @@ Disjuncts: 189 diff old new --- old +++ new -@@ -6,53 +6,45 @@ - // ./in.cue:10:8 - } - y: (int){ 1 } -- let list#1 = (#list){ -- 0: (_|_){ -- // [incomplete] p1.#A.list.0: cannot reference optional field: x: -- // ./in.cue:6:15 -- } -- 1: (int){ 1 } -- } -- all: (#list){ -- 0: (int){ 1 } -- } -- }, (#struct){ -- x?: (int){ int } -- y?: (int){ int } -- let list#1 = (#list){ -- 0: (_|_){ -- // [incomplete] p1.#A.list.0: cannot reference optional field: x: -- // ./in.cue:6:15 -- } -- 1: (_|_){ -- // [incomplete] p1.#A.list.1: cannot reference optional field: y: -- // ./in.cue:6:18 -- } -- } -- all: (#list){ -- } -- }) } -- a: (#struct){ -- x: (int){ 3 } -- y?: (int){ int } -- let list#1 = (#list){ -- 0: (int){ 3 } -- 1: (_|_){ -- // [incomplete] p1.a.list.1: cannot reference optional field: y: -- // ./in.cue:6:18 -- } -- } -- all: (#list){ -- 0: (int){ 3 } -- } -- } -- b: (#struct){ -- x: (int){ 3 } -- y?: (int){ int } +@@ -49,10 +49,13 @@ + b: (#struct){ + x: (int){ 3 } + y?: (int){ int } - let list#1multi = [ - 〈1;x〉, - 〈1;y〉, - ] -+ let list#1 = (_|_){ -+ // [user] -+ 0: (_|_){ -+ // [user] explicit error (_|_ literal) in source: -+ // ./in.cue:10:8 -+ } -+ 1: (int){ 1 } -+ } -+ all: (#list){ -+ 0: (int){ 1 } -+ } -+ }, (#struct){ -+ x?: (int){ int } -+ y?: (int){ int } -+ let list#1 = (#list){ -+ 0: (int){ int } -+ 1: (int){ int } -+ } -+ all: (#list){ -+ } -+ }) } -+ a: (#struct){ -+ x: (int){ 3 } -+ y?: (int){ int } -+ let list#1 = (#list){ -+ 0: (int){ 3 } -+ 1: (int){ int } -+ } -+ all: (#list){ -+ 0: (int){ 3 } -+ } -+ } -+ b: (#struct){ -+ x: (int){ 3 } -+ y?: (int){ int } + let list#1 = (#list){ + 0: (int){ 3 } -+ 1: (int){ int } ++ 1: (_|_){ ++ // [incomplete] p1.b.list.1: cannot reference optional field: y: ++ // ./in.cue:6:18 ++ } + } all: (#list){ 0: (int){ 3 } } -@@ -65,51 +57,46 @@ - // ./in.cue:27:8 - } - y: (int){ 1 } -- let list#2 = (#list){ -- 0: (_|_){ -- // [incomplete] p2.#A.list.0: cannot reference optional field: x: -- // ./in.cue:23:15 -- } -- 1: (int){ 1 } -- } -- all: (#list){ -- 0: (int){ 1 } -- } -- }, (#struct){ -- x?: (int){ int } -- y?: (int){ int } -- let list#2 = (#list){ -- 0: (_|_){ -- // [incomplete] p2.#A.list.0: cannot reference optional field: x: -- // ./in.cue:23:15 -- } -- 1: (_|_){ -- // [incomplete] p2.#A.list.1: cannot reference optional field: y: -- // ./in.cue:23:18 -- } -- } -- all: (#list){ -- } -- }) } -- a: (#struct){ -- x: (int){ 3 } -- y: (int){ 2 } -- let list#2 = (#list){ -- 0: (int){ 3 } -- 1: (int){ 2 } -- } -- all: (#list){ -- 0: (int){ 3 } -- 1: (int){ 2 } -- } -- } -- b: (#struct){ -- x: (int){ 3 } -- y: (int){ 2 } +@@ -106,10 +109,10 @@ + b: (#struct){ + x: (int){ 3 } + y: (int){ 2 } - let list#2multi = [ - 〈1;x〉, - 〈1;y〉, - ] -+ let list#2 = (_|_){ -+ // [user] -+ 0: (_|_){ -+ // [user] explicit error (_|_ literal) in source: -+ // ./in.cue:27:8 -+ } -+ 1: (int){ 1 } -+ } -+ all: (#list){ -+ 0: (int){ 1 } -+ } -+ }, (#struct){ -+ x?: (int){ int } -+ y?: (int){ int } -+ let list#2 = (#list){ -+ 0: (int){ int } -+ 1: (int){ int } -+ } -+ all: (#list){ -+ } -+ }) } -+ a: (#struct){ -+ x: (int){ 3 } -+ y: (int){ 2 } -+ let list#2 = (#list){ -+ 0: (int){ 3 } -+ 1: (int){ 2 } -+ } -+ all: (#list){ -+ 0: (int){ 3 } -+ 1: (int){ 2 } -+ } -+ } -+ b: (#struct){ -+ x: (int){ 3 } -+ y: (int){ 2 } + let list#2 = (#list){ + 0: (int){ 3 } + 1: (int){ 2 } diff --git a/cue/testdata/references/optional.txtar b/cue/testdata/references/optional.txtar index 3f8a7fda8..9a14fb5b1 100644 --- a/cue/testdata/references/optional.txtar +++ b/cue/testdata/references/optional.txtar @@ -19,42 +19,6 @@ Retain: 1 Unifications: 9 Conjuncts: 9 Disjuncts: 10 --- out/evalalpha -- -(struct){ - t1: (struct){ - a: (struct){ - foo?: (int){ int } - b: (_|_){ - // [incomplete] t1.a.b: cannot reference optional field: foo: - // ./in.cue:4:5 - } - } - } - t2: (struct){ - a: (struct){ - b: (int){ int } - foo?: (int){ int } - } - } -} --- diff/-out/evalalpha<==>+out/eval -- -diff old new ---- old -+++ new -@@ -10,10 +10,7 @@ - } - t2: (struct){ - a: (struct){ -- b: (_|_){ -- // [incomplete] t2.a.b: cannot reference optional field: foo: -- // ./in.cue:7:5 -- } -+ b: (int){ int } - foo?: (int){ int } - } - } --- diff/todo/p1 -- -t2: reference resolves to optional field. -- out/eval -- (struct){ t1: (struct){ @@ -91,4 +55,4 @@ t2: reference resolves to optional field. foo?: int } } -} \ No newline at end of file +} diff --git a/internal/core/adt/unify.go b/internal/core/adt/unify.go index a22c2d2e8..5045b18a2 100644 --- a/internal/core/adt/unify.go +++ b/internal/core/adt/unify.go @@ -687,7 +687,7 @@ func (v *Vertex) lookup(c *OpContext, pos token.Pos, f Feature, flags combinedFl break } } - arcState.completeNodeTasks(attemptOnly) + arcState.completeNodeTasks(yield) // Child nodes, if pending and derived from a comprehension, may // still cause this arc to become not pending. @@ -701,11 +701,18 @@ func (v *Vertex) lookup(c *OpContext, pos token.Pos, f Feature, flags combinedFl switch runMode { case ignore, attemptOnly: + // TODO(cycle): ideally, we should be able to require that the + // arcType be known at this point, but that does not seem to work. + // Revisit once we have the structural cycle detection in place. + // TODO: should we avoid notifying ArcPending vertices here? if task != nil { arcState.addNotify2(task.node.node, task.id) } - return arcReturn + if arc.ArcType == ArcPending { + return arcReturn + } + goto handleArcType case yield: arcState.process(needs, yield) @@ -718,6 +725,7 @@ func (v *Vertex) lookup(c *OpContext, pos token.Pos, f Feature, flags combinedFl } } +handleArcType: switch arc.ArcType { case ArcMember: return arcReturn