From f1d39f08e69a91e84a968a3dbf590199ea2775be Mon Sep 17 00:00:00 2001 From: Marcel van Lohuizen Date: Thu, 13 Feb 2025 08:11:37 +0100 Subject: [PATCH] internal/core/adt: pass and use OpContext to functions using Schema Builtins that have a pkg.Schema argument evaluate this argument as an expression, possibly unifying it with something else. This may result in a cycle. In order to detect the cycle, the cycle information obtained from using the argument must passed to further evaluation. We do this by making the Schema method call CallContext.Expr, which, in turn, uses EvaluatedKeepState to accumulate cycle information in the OpContext. The functions that are passed the OpContext then need to use adt.Unify instead of Value.Unify to be able to detect cycles. NOTE: this strongly hints that we need to change the public API to also be centered around an operation context. This has come up in various situations already. This change requires pkg/gen.go to also pass an OpContext for the generated stubs. We take a somewhat unusual approach here: rather than changing the functions to take an OpContext, we instead call a non-exported function with the OpContext added as the first argument whenever an Exported function with a pkg.Schema argument is found. The reason for this is that we currently piggyback on godoc to generate documentation for the builtins. Adding the OpContext in this signature would be confusing for the the user, as it should not be populated in CUE. Note some counter error were introduced by the new tests. The error messages have some additional or longer paths as a result of this change, which is either a benign change or desirable. Fixes #3649 (fixes derivative issues, was already closed) Signed-off-by: Marcel van Lohuizen Change-Id: I4b5a31aeb5501ec03915d069f8b92f56311124dd Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1208702 Unity-Result: CUE porcuepine TryBot-Result: CUEcueckoo Reviewed-by: Matthew Sackman --- cue/testdata/cycle/builtins.txtar | 1189 +++++++++++++++------ internal/core/adt/eval_test.go | 1 + internal/pkg/context.go | 7 +- pkg/encoding/json/manual.go | 5 + pkg/encoding/json/pkg.go | 2 +- pkg/encoding/json/testdata/gen.txtar | 2 +- pkg/encoding/yaml/manual.go | 17 + pkg/encoding/yaml/pkg.go | 4 +- pkg/encoding/yaml/testdata/gen.txtar | 6 +- pkg/encoding/yaml/testdata/validate.txtar | 2 +- pkg/gen.go | 22 +- pkg/list/list.go | 7 +- pkg/list/pkg.go | 2 +- 13 files changed, 912 insertions(+), 354 deletions(-) diff --git a/cue/testdata/cycle/builtins.txtar b/cue/testdata/cycle/builtins.txtar index f0c429955..7b494f211 100644 --- a/cue/testdata/cycle/builtins.txtar +++ b/cue/testdata/cycle/builtins.txtar @@ -134,8 +134,6 @@ issue3633: final: { #s: matchN(1, [matchN(1, [{a!: _}])]) } -- cycle.cue -- -import "encoding/yaml" - noCycle: t1: { _s _s: { @@ -169,6 +167,9 @@ issue3649: cycle: t1: { a: matchN(1, [#c]) } } +-- yamlcycle.cue -- +import "encoding/yaml" + yamlNoCycle: { // the validator is invoked recursively and also unifies with a concrete // value. This should nonetheless not result in a cycle error. @@ -189,34 +190,60 @@ selfCycle: t2: { c: matchN(1, [{d: c}]) c: {} } -// TODO: fix hang -// selfCycle: yamlVal: t1: { -// x: y: yaml.Validate(x) -// x: y: "{}" -// } -// selfCycle: yamlVal: t1: { -// x: y: yaml.Validate(x) -// x: y: "{}" -// } -// selfCycle: yamlFun: t1:{ -// x: y?: yaml.Validate("{}", x) -// } -// selfCycle: yamlFun: t2: { -// z: x & __no_sharing -// z: y: "{}" -// x: y: yaml.Validate("{}", x) -// } +selfCycle: yamlVal: t1: { + x: y: yaml.Validate(x) + x: y: "{}" +} +selfCycle: yamlVal: t2: { + // Duplicating validators can cause some tricky code paths, like finalizing + // a node while still in the middle of validation. + x: y: yaml.Validate(x) + x: y: "{}" + x: y: yaml.Validate(x) + x: y: "{}" +} +selfCycle: yamlFun: t1:{ + x: y?: yaml.Validate("{}", x) +} +selfCycle: yamlFun: t2: { + z: x & __no_sharing + z: y: "{}" + x: y: yaml.Validate("{}", x) +} +selfCycle: yamlValidatePartial: { + x: y: yaml.ValidatePartial(x) + x: y: "{}" +} +-- jsoncycle.cue -- +import "encoding/json" + +jsonCycle: t1: { + x: y: json.Validate(x) + x: y: "{}" +} +-- listmatchncycle.cue -- +import "list" + +listMatchN: structCycle: { + x: y: list.MatchN(1, x) + x: y: [{}] +} +listMatchN: ok: { + x: y?: list.MatchN(1, x) + z: x + z: y: [{}] +} -- todo/p1 -- issue3443.noCycle: fix hang -- out/evalalpha/stats -- -Leaks: 351 +Leaks: 410 Freed: 17 Reused: 17 -Allocs: 351 +Allocs: 410 Retain: 0 -Unifications: 310 -Conjuncts: 1216 +Unifications: 368 +Conjuncts: 1497 Disjuncts: 28 -- diff/-out/evalalpha/stats<==>+out/eval/stats -- diff old new @@ -224,66 +251,57 @@ diff old new +++ new @@ -1,9 +1,9 @@ -Leaks: 23 --Freed: 357 --Reused: 345 +-Freed: 414 +-Reused: 402 -Allocs: 35 --Retain: 94 -+Leaks: 351 +-Retain: 95 ++Leaks: 410 +Freed: 17 +Reused: 17 -+Allocs: 351 ++Allocs: 410 +Retain: 0 --Unifications: 352 --Conjuncts: 625 --Disjuncts: 441 -+Unifications: 310 -+Conjuncts: 1216 +-Unifications: 409 +-Conjuncts: 764 +-Disjuncts: 501 ++Unifications: 368 ++Conjuncts: 1497 +Disjuncts: 28 -- out/eval/stats -- Leaks: 23 -Freed: 357 -Reused: 345 +Freed: 414 +Reused: 402 Allocs: 35 -Retain: 94 +Retain: 95 -Unifications: 352 -Conjuncts: 625 -Disjuncts: 441 +Unifications: 409 +Conjuncts: 764 +Disjuncts: 501 -- out/evalalpha -- Errors: noCycle.t1.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 noCycle.t1.#x.#x: structural cycle: - ./cycle.cue:6:7 + ./cycle.cue:4:7 noCycle.t1._s.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 issue3649.cycle.t1.data.a: invalid value {b:"foo"} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:33:6 - ./cycle.cue:30:11 - ./cycle.cue:33:13 + ./cycle.cue:31:6 + ./cycle.cue:28:11 + ./cycle.cue:31:13 issue3649.cycle.t1.data.a.a: structural cycle: - ./cycle.cue:33:6 -selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 - ./cycle.cue:48:5 -selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 - ./cycle.cue:47:20 - ./cycle.cue:48:8 -selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 - ./cycle.cue:47:20 - ./cycle.cue:48:8 -selfCycle.t1.c.d.d.d: structural cycle: - ./cycle.cue:47:5 + ./cycle.cue:31:6 +jsonCycle.t1.x.y: invalid value "{}" (does not satisfy encoding/json.Validate): error in call to encoding/json.Validate: structural cycle: + ./jsoncycle.cue:4:8 + ./jsoncycle.cue:5:8 +listMatchN.structCycle.x.y: invalid value [{}] (does not satisfy list.MatchN): number of matched elements is 0: does not satisfy 1: + ./listmatchncycle.cue:4:8 + ./listmatchncycle.cue:4:20 + ./listmatchncycle.cue:5:8 issue3443.matchIf.#S: cannot call non-function matchIf (type struct): ./matchn.cue:16:7 issue3443.cycle1.cycle.s: invalid value {n:{n:_}} (does not satisfy matchN): 0 matched, expected 1: @@ -314,6 +332,37 @@ issue3443.cycle2.fail.#S.n.n: invalid value {n:{n:{n:_}}} (does not satisfy matc ./matchn.cue:55:43 issue3443.cycle2.fail.#S.n.n.n: structural cycle: ./matchn.cue:55:13 +selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:14:5 + ./yamlcycle.cue:14:12 + ./yamlcycle.cue:15:5 +selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:14:5 + ./yamlcycle.cue:14:12 + ./yamlcycle.cue:14:20 + ./yamlcycle.cue:15:8 +selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:14:5 + ./yamlcycle.cue:14:12 + ./yamlcycle.cue:14:20 + ./yamlcycle.cue:15:8 +selfCycle.t1.c.d.d.d: structural cycle: + ./yamlcycle.cue:14:5 +selfCycle.yamlVal.t1.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: + ./yamlcycle.cue:24:8 + ./yamlcycle.cue:25:8 +selfCycle.yamlVal.t2.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: + ./yamlcycle.cue:30:8 + ./yamlcycle.cue:31:8 + ./yamlcycle.cue:32:8 + ./yamlcycle.cue:33:8 +selfCycle.yamlFun.t2.x.y: error in call to encoding/yaml.Validate: structural cycle: + ./yamlcycle.cue:41:8 +selfCycle.yamlFun.t2.z.y: error in call to encoding/yaml.Validate: structural cycle: + ./yamlcycle.cue:41:8 +selfCycle.yamlValidatePartial.x.y: invalid value "{}" (does not satisfy encoding/yaml.ValidatePartial): error in call to encoding/yaml.ValidatePartial: structural cycle: + ./yamlcycle.cue:44:8 + ./yamlcycle.cue:45:8 Result: (_|_){ @@ -326,18 +375,18 @@ Result: // [eval] #x: (_|_){ // [eval] noCycle.t1._s.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 } } #x: (_|_){ // [eval] noCycle.t1.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 // noCycle.t1.#x.#x: structural cycle: - // ./cycle.cue:6:7 + // ./cycle.cue:4:7 } } } @@ -391,11 +440,11 @@ Result: // [eval] a: (_|_){ // [eval] issue3649.cycle.t1.data.a: invalid value {b:"foo"} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:33:6 - // ./cycle.cue:30:11 - // ./cycle.cue:33:13 + // ./cycle.cue:31:6 + // ./cycle.cue:28:11 + // ./cycle.cue:31:13 // issue3649.cycle.t1.data.a.a: structural cycle: - // ./cycle.cue:33:6 + // ./cycle.cue:31:6 b: (string){ "foo" } } b: (string){ string } @@ -410,46 +459,6 @@ Result: } } } - yamlNoCycle: (struct){ - #c: (#struct){ - b?: (string){ string } - a?: ((string|bytes)){ "encoding/yaml".Validate(yamlNoCycle.#c) } - } - data: (#struct){ - a: (string){ "{a: \"b: foo\"}" } - b?: (string){ string } - } - } - selfCycle: (_|_){ - // [eval] - t1: (_|_){ - // [eval] - c: (_|_){ - // [eval] selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:47:5 - // ./cycle.cue:47:12 - // ./cycle.cue:48:5 - // selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:47:5 - // ./cycle.cue:47:12 - // ./cycle.cue:47:20 - // ./cycle.cue:48:8 - // selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:47:5 - // ./cycle.cue:47:12 - // ./cycle.cue:47:20 - // ./cycle.cue:48:8 - // selfCycle.t1.c.d.d.d: structural cycle: - // ./cycle.cue:47:5 - d: (struct){ - } - } - } - t2: (struct){ - c: (struct){ - } - } - } builtinCyclePerm0: (struct){ X: (string){ "mod.test" } Y: (struct){ @@ -504,6 +513,48 @@ Result: host: (string){ "mod.test" } } } + jsonCycle: (_|_){ + // [structural cycle] + t1: (_|_){ + // [structural cycle] + x: (_|_){ + // [structural cycle] + y: (_|_){ + // [structural cycle] jsonCycle.t1.x.y: invalid value "{}" (does not satisfy encoding/json.Validate): error in call to encoding/json.Validate: structural cycle: + // ./jsoncycle.cue:4:8 + // ./jsoncycle.cue:5:8 + } + } + } + } + listMatchN: (_|_){ + // [eval] + structCycle: (_|_){ + // [eval] + x: (_|_){ + // [eval] + y: (_|_){ + // [eval] listMatchN.structCycle.x.y: invalid value [{}] (does not satisfy list.MatchN): number of matched elements is 0: does not satisfy 1: + // ./listmatchncycle.cue:4:8 + // ./listmatchncycle.cue:4:20 + // ./listmatchncycle.cue:5:8 + 0: (struct){ + } + } + } + } + ok: (struct){ + x: (struct){ + y?: (list){ list.MatchN(1, listMatchN.ok.x) } + } + z: (struct){ + y: (#list){ + 0: (struct){ + } + } + } + } + } issue3410: (struct){ _s: (struct){ #x: (_){ matchN(1, (#list){ @@ -659,52 +710,149 @@ Result: }) } } } + yamlNoCycle: (struct){ + #c: (#struct){ + b?: (string){ string } + a?: ((string|bytes)){ "encoding/yaml".Validate(yamlNoCycle.#c) } + } + data: (#struct){ + a: (string){ "{a: \"b: foo\"}" } + b?: (string){ string } + } + } + selfCycle: (_|_){ + // [eval] + t1: (_|_){ + // [eval] + c: (_|_){ + // [eval] selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:14:5 + // ./yamlcycle.cue:14:12 + // ./yamlcycle.cue:15:5 + // selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:14:5 + // ./yamlcycle.cue:14:12 + // ./yamlcycle.cue:14:20 + // ./yamlcycle.cue:15:8 + // selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:14:5 + // ./yamlcycle.cue:14:12 + // ./yamlcycle.cue:14:20 + // ./yamlcycle.cue:15:8 + // selfCycle.t1.c.d.d.d: structural cycle: + // ./yamlcycle.cue:14:5 + d: (struct){ + } + } + } + t2: (struct){ + c: (struct){ + } + } + yamlVal: (_|_){ + // [eval] + t1: (_|_){ + // [eval] + x: (_|_){ + // [eval] + y: (_|_){ + // [eval] selfCycle.yamlVal.t1.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: + // ./yamlcycle.cue:24:8 + // ./yamlcycle.cue:25:8 + } + } + } + t2: (_|_){ + // [eval] + x: (_|_){ + // [eval] + y: (_|_){ + // [eval] selfCycle.yamlVal.t2.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: + // ./yamlcycle.cue:30:8 + // ./yamlcycle.cue:31:8 + // ./yamlcycle.cue:32:8 + // ./yamlcycle.cue:33:8 + } + } + } + } + yamlFun: (_|_){ + // [eval] + t1: (struct){ + x: (struct){ + y?: (bool){ true } + } + } + t2: (_|_){ + // [eval] + z: (_|_){ + // [eval] + y: (_|_){ + // [eval] selfCycle.yamlFun.t2.z.y: error in call to encoding/yaml.Validate: structural cycle: + // ./yamlcycle.cue:41:8 + } + } + x: (_|_){ + // [eval] + y: (_|_){ + // [eval] selfCycle.yamlFun.t2.x.y: error in call to encoding/yaml.Validate: structural cycle: + // ./yamlcycle.cue:41:8 + } + } + } + } + yamlValidatePartial: (_|_){ + // [structural cycle] + x: (_|_){ + // [structural cycle] + y: (_|_){ + // [structural cycle] selfCycle.yamlValidatePartial.x.y: invalid value "{}" (does not satisfy encoding/yaml.ValidatePartial): error in call to encoding/yaml.ValidatePartial: structural cycle: + // ./yamlcycle.cue:44:8 + // ./yamlcycle.cue:45:8 + } + } + } + } } -- diff/-out/evalalpha<==>+out/eval -- diff old new --- old +++ new -@@ -3,18 +3,18 @@ - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 +@@ -3,24 +3,39 @@ + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 -noCycle.t1.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- ./cycle.cue:6:7 -- ./cycle.cue:6:14 -- ./cycle.cue:7:13 +- ./cycle.cue:4:7 +- ./cycle.cue:4:14 +- ./cycle.cue:5:13 +noCycle.t1.#x.#x: structural cycle: -+ ./cycle.cue:6:7 ++ ./cycle.cue:4:7 noCycle.t1._s.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 -noCycle.t1._s.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- ./cycle.cue:6:7 -- ./cycle.cue:6:14 -- ./cycle.cue:7:13 +- ./cycle.cue:4:7 +- ./cycle.cue:4:14 +- ./cycle.cue:5:13 +-jsonCycle.t1.x.y.y: structural cycle: +- ./jsoncycle.cue:4:22 +-listMatchN.structCycle.x.y.y: structural cycle: +- ./listmatchncycle.cue:4:23 +issue3649.cycle.t1.data.a: invalid value {b:"foo"} (does not satisfy matchN): 0 matched, expected 1: -+ ./cycle.cue:33:6 -+ ./cycle.cue:30:11 -+ ./cycle.cue:33:13 ++ ./cycle.cue:31:6 ++ ./cycle.cue:28:11 ++ ./cycle.cue:31:13 +issue3649.cycle.t1.data.a.a: structural cycle: -+ ./cycle.cue:33:6 - selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 -@@ -31,18 +31,20 @@ - ./cycle.cue:48:8 - selfCycle.t1.c.d.d.d: structural cycle: - ./cycle.cue:47:5 --selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- ./cycle.cue:53:5 -- ./cycle.cue:53:12 -- ./cycle.cue:54:5 --selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- ./cycle.cue:53:5 -- ./cycle.cue:53:12 -- ./cycle.cue:53:20 --selfCycle.t2.c.d.d: structural cycle: -- ./cycle.cue:53:5 ++ ./cycle.cue:31:6 ++jsonCycle.t1.x.y: invalid value "{}" (does not satisfy encoding/json.Validate): error in call to encoding/json.Validate: structural cycle: ++ ./jsoncycle.cue:4:8 ++ ./jsoncycle.cue:5:8 ++listMatchN.structCycle.x.y: invalid value [{}] (does not satisfy list.MatchN): number of matched elements is 0: does not satisfy 1: ++ ./listmatchncycle.cue:4:8 ++ ./listmatchncycle.cue:4:20 ++ ./listmatchncycle.cue:5:8 issue3443.matchIf.#S: cannot call non-function matchIf (type struct): ./matchn.cue:16:7 +issue3443.cycle1.cycle.s: invalid value {n:{n:_}} (does not satisfy matchN): 0 matched, expected 1: @@ -722,25 +870,67 @@ diff old new issue3443.cycle2.fail.#S: invalid value {n:{n:{n:_}}} (does not satisfy matchN): 0 matched, expected 1: ./matchn.cue:55:13 ./matchn.cue:55:20 -@@ -74,10 +76,6 @@ - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 +@@ -53,26 +68,21 @@ + ./yamlcycle.cue:15:8 + selfCycle.t1.c.d.d.d: structural cycle: + ./yamlcycle.cue:14:5 +-selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: +- ./yamlcycle.cue:20:5 +- ./yamlcycle.cue:20:12 +- ./yamlcycle.cue:21:5 +-selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: +- ./yamlcycle.cue:20:5 +- ./yamlcycle.cue:20:12 +- ./yamlcycle.cue:20:20 +-selfCycle.t2.c.d.d: structural cycle: +- ./yamlcycle.cue:20:5 +-selfCycle.yamlVal.t1.x.y.y: structural cycle: +- ./yamlcycle.cue:24:22 +-selfCycle.yamlVal.t2.x.y.y: structural cycle: +- ./yamlcycle.cue:32:22 +-selfCycle.yamlFun.t1.x.y.y: structural cycle: +- ./yamlcycle.cue:36:29 +-selfCycle.yamlFun.t2.x.y.y: structural cycle: +- ./yamlcycle.cue:41:28 +-selfCycle.yamlValidatePartial.x.y.y: structural cycle: +- ./yamlcycle.cue:44:29 ++selfCycle.yamlVal.t1.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: ++ ./yamlcycle.cue:24:8 ++ ./yamlcycle.cue:25:8 ++selfCycle.yamlVal.t2.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: ++ ./yamlcycle.cue:30:8 ++ ./yamlcycle.cue:31:8 ++ ./yamlcycle.cue:32:8 ++ ./yamlcycle.cue:33:8 ++selfCycle.yamlFun.t2.x.y: error in call to encoding/yaml.Validate: structural cycle: ++ ./yamlcycle.cue:41:8 ++selfCycle.yamlFun.t2.z.y: error in call to encoding/yaml.Validate: structural cycle: ++ ./yamlcycle.cue:41:8 ++selfCycle.yamlValidatePartial.x.y: invalid value "{}" (does not satisfy encoding/yaml.ValidatePartial): error in call to encoding/yaml.ValidatePartial: structural cycle: ++ ./yamlcycle.cue:44:8 ++ ./yamlcycle.cue:45:8 + + Result: + (_|_){ +@@ -88,10 +98,6 @@ + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 - // noCycle.t1._s.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- // ./cycle.cue:6:7 -- // ./cycle.cue:6:14 -- // ./cycle.cue:7:13 +- // ./cycle.cue:4:7 +- // ./cycle.cue:4:14 +- // ./cycle.cue:5:13 } } #x: (_|_){ -@@ -85,26 +83,25 @@ - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 +@@ -99,26 +105,25 @@ + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 - // noCycle.t1.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- // ./cycle.cue:6:7 -- // ./cycle.cue:6:14 -- // ./cycle.cue:7:13 +- // ./cycle.cue:4:7 +- // ./cycle.cue:4:14 +- // ./cycle.cue:5:13 - } - } - } @@ -753,7 +943,7 @@ diff old new - b: (string){ "foo" } - } + // noCycle.t1.#x.#x: structural cycle: -+ // ./cycle.cue:6:7 ++ // ./cycle.cue:4:7 + } + } + } @@ -775,7 +965,7 @@ diff old new } }) } } -@@ -111,7 +108,6 @@ +@@ -125,7 +130,6 @@ } t2: (struct){ x: (struct){ @@ -783,7 +973,7 @@ diff old new y: (struct){ a: (struct){ d: (struct){ -@@ -119,14 +115,15 @@ +@@ -133,14 +137,15 @@ } } } @@ -801,7 +991,7 @@ diff old new } }) } } -@@ -133,18 +130,27 @@ +@@ -147,18 +152,27 @@ } } } @@ -820,11 +1010,11 @@ diff old new + // [eval] + a: (_|_){ + // [eval] issue3649.cycle.t1.data.a: invalid value {b:"foo"} (does not satisfy matchN): 0 matched, expected 1: -+ // ./cycle.cue:33:6 -+ // ./cycle.cue:30:11 -+ // ./cycle.cue:33:13 ++ // ./cycle.cue:31:6 ++ // ./cycle.cue:28:11 ++ // ./cycle.cue:31:13 + // issue3649.cycle.t1.data.a.a: structural cycle: -+ // ./cycle.cue:33:6 ++ // ./cycle.cue:31:6 + b: (string){ "foo" } + } + b: (string){ string } @@ -837,39 +1027,40 @@ diff old new } }) } } -@@ -157,8 +163,8 @@ - a?: ((string|bytes)){ "encoding/yaml".Validate(yamlNoCycle.#c) } - } - data: (#struct){ -- b?: (string){ string } - a: (string){ "{a: \"b: foo\"}" } -+ b?: (string){ string } - } - } - selfCycle: (_|_){ -@@ -186,19 +192,8 @@ - } +@@ -224,8 +238,12 @@ + t1: (_|_){ + // [structural cycle] + x: (_|_){ +- // [structural cycle] jsonCycle.t1.x.y.y: structural cycle: +- // ./jsoncycle.cue:4:22 ++ // [structural cycle] ++ y: (_|_){ ++ // [structural cycle] jsonCycle.t1.x.y: invalid value "{}" (does not satisfy encoding/json.Validate): error in call to encoding/json.Validate: structural cycle: ++ // ./jsoncycle.cue:4:8 ++ // ./jsoncycle.cue:5:8 ++ } } } -- t2: (_|_){ -- // [eval] -- c: (_|_){ -- // [eval] selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- // ./cycle.cue:53:5 -- // ./cycle.cue:53:12 -- // ./cycle.cue:54:5 -- // selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: -- // ./cycle.cue:53:5 -- // ./cycle.cue:53:12 -- // ./cycle.cue:53:20 -- // selfCycle.t2.c.d.d: structural cycle: -- // ./cycle.cue:53:5 -+ t2: (struct){ -+ c: (struct){ + } +@@ -234,8 +252,15 @@ + structCycle: (_|_){ + // [eval] + x: (_|_){ +- // [eval] listMatchN.structCycle.x.y.y: structural cycle: +- // ./listmatchncycle.cue:4:23 ++ // [eval] ++ y: (_|_){ ++ // [eval] listMatchN.structCycle.x.y: invalid value [{}] (does not satisfy list.MatchN): number of matched elements is 0: does not satisfy 1: ++ // ./listmatchncycle.cue:4:8 ++ // ./listmatchncycle.cue:4:20 ++ // ./listmatchncycle.cue:5:8 ++ 0: (struct){ ++ } ++ } } } - } -@@ -259,20 +254,20 @@ + ok: (struct){ +@@ -253,20 +278,20 @@ issue3410: (struct){ _s: (struct){ #x: (_){ matchN(1, (#list){ @@ -898,7 +1089,7 @@ diff old new } }) } } -@@ -279,9 +274,9 @@ +@@ -273,9 +298,9 @@ issue3420: (struct){ matches1: (struct){ #S: (_){ matchN(1, (#list){ @@ -911,7 +1102,7 @@ diff old new } }) } s: (int){ 2 } -@@ -298,13 +293,13 @@ +@@ -292,13 +317,13 @@ } noCycle: (struct){ #S: (_){ matchN(1, (#list){ @@ -928,7 +1119,7 @@ diff old new n: (struct){ n: (_){ _ } } -@@ -313,29 +308,43 @@ +@@ -307,29 +332,43 @@ } noCycle2: (struct){ #S: (_){ matchN(1, (#list){ @@ -985,7 +1176,7 @@ diff old new n: (struct){ n: (_){ _ } } -@@ -388,11 +397,11 @@ +@@ -382,11 +421,11 @@ // ./matchn.cue:63:24 } #s: (_){ matchN(1, (#list){ @@ -999,52 +1190,149 @@ diff old new } }) } } +@@ -397,8 +436,8 @@ + a?: ((string|bytes)){ "encoding/yaml".Validate(yamlNoCycle.#c) } + } + data: (#struct){ +- b?: (string){ string } + a: (string){ "{a: \"b: foo\"}" } ++ b?: (string){ string } + } + } + selfCycle: (_|_){ +@@ -426,19 +465,8 @@ + } + } + } +- t2: (_|_){ +- // [eval] +- c: (_|_){ +- // [eval] selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: +- // ./yamlcycle.cue:20:5 +- // ./yamlcycle.cue:20:12 +- // ./yamlcycle.cue:21:5 +- // selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: +- // ./yamlcycle.cue:20:5 +- // ./yamlcycle.cue:20:12 +- // ./yamlcycle.cue:20:20 +- // selfCycle.t2.c.d.d: structural cycle: +- // ./yamlcycle.cue:20:5 ++ t2: (struct){ ++ c: (struct){ + } + } + yamlVal: (_|_){ +@@ -446,25 +474,33 @@ + t1: (_|_){ + // [eval] + x: (_|_){ +- // [eval] selfCycle.yamlVal.t1.x.y.y: structural cycle: +- // ./yamlcycle.cue:24:22 +- } +- } +- t2: (_|_){ +- // [eval] +- x: (_|_){ +- // [eval] selfCycle.yamlVal.t2.x.y.y: structural cycle: +- // ./yamlcycle.cue:32:22 ++ // [eval] ++ y: (_|_){ ++ // [eval] selfCycle.yamlVal.t1.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: ++ // ./yamlcycle.cue:24:8 ++ // ./yamlcycle.cue:25:8 ++ } ++ } ++ } ++ t2: (_|_){ ++ // [eval] ++ x: (_|_){ ++ // [eval] ++ y: (_|_){ ++ // [eval] selfCycle.yamlVal.t2.x.y: invalid value "{}" (does not satisfy encoding/yaml.Validate): error in call to encoding/yaml.Validate: structural cycle: ++ // ./yamlcycle.cue:30:8 ++ // ./yamlcycle.cue:31:8 ++ // ./yamlcycle.cue:32:8 ++ // ./yamlcycle.cue:33:8 ++ } + } + } + } + yamlFun: (_|_){ + // [eval] +- t1: (_|_){ +- // [structural cycle] +- x: (_|_){ +- // [structural cycle] selfCycle.yamlFun.t1.x.y.y: structural cycle: +- // ./yamlcycle.cue:36:29 ++ t1: (struct){ ++ x: (struct){ ++ y?: (bool){ true } + } + } + t2: (_|_){ +@@ -472,13 +508,16 @@ + z: (_|_){ + // [eval] + y: (_|_){ +- // [eval] selfCycle.yamlFun.t2.x.y.y: structural cycle: +- // ./yamlcycle.cue:41:28 +- } +- } +- x: (_|_){ +- // [eval] selfCycle.yamlFun.t2.x.y.y: structural cycle: +- // ./yamlcycle.cue:41:28 ++ // [eval] selfCycle.yamlFun.t2.z.y: error in call to encoding/yaml.Validate: structural cycle: ++ // ./yamlcycle.cue:41:8 ++ } ++ } ++ x: (_|_){ ++ // [eval] ++ y: (_|_){ ++ // [eval] selfCycle.yamlFun.t2.x.y: error in call to encoding/yaml.Validate: structural cycle: ++ // ./yamlcycle.cue:41:8 ++ } + } + } + } +@@ -485,8 +524,12 @@ + yamlValidatePartial: (_|_){ + // [structural cycle] + x: (_|_){ +- // [structural cycle] selfCycle.yamlValidatePartial.x.y.y: structural cycle: +- // ./yamlcycle.cue:44:29 ++ // [structural cycle] ++ y: (_|_){ ++ // [structural cycle] selfCycle.yamlValidatePartial.x.y: invalid value "{}" (does not satisfy encoding/yaml.ValidatePartial): error in call to encoding/yaml.ValidatePartial: structural cycle: ++ // ./yamlcycle.cue:44:8 ++ // ./yamlcycle.cue:45:8 ++ } + } + } + } -- diff/todo/p2 -- issue3443: Sort out differences in reporting of cycles. -- out/eval -- Errors: noCycle.t1.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 noCycle.t1.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 noCycle.t1._s.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 noCycle.t1._s.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:6:7 - ./cycle.cue:6:14 - ./cycle.cue:7:13 -selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 - ./cycle.cue:48:5 -selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 - ./cycle.cue:47:20 - ./cycle.cue:48:8 -selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:47:5 - ./cycle.cue:47:12 - ./cycle.cue:47:20 - ./cycle.cue:48:8 -selfCycle.t1.c.d.d.d: structural cycle: - ./cycle.cue:47:5 -selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:53:5 - ./cycle.cue:53:12 - ./cycle.cue:54:5 -selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - ./cycle.cue:53:5 - ./cycle.cue:53:12 - ./cycle.cue:53:20 -selfCycle.t2.c.d.d: structural cycle: - ./cycle.cue:53:5 + ./cycle.cue:4:7 + ./cycle.cue:4:14 + ./cycle.cue:5:13 +jsonCycle.t1.x.y.y: structural cycle: + ./jsoncycle.cue:4:22 +listMatchN.structCycle.x.y.y: structural cycle: + ./listmatchncycle.cue:4:23 issue3443.matchIf.#S: cannot call non-function matchIf (type struct): ./matchn.cue:16:7 issue3443.cycle2.fail.#S: invalid value {n:{n:{n:_}}} (does not satisfy matchN): 0 matched, expected 1: @@ -1063,6 +1351,42 @@ issue3443.cycle2.fail.#S.n.n: invalid value {n:{n:{n:_}}} (does not satisfy matc ./matchn.cue:55:43 issue3443.cycle2.fail.#S.n.n.n: structural cycle: ./matchn.cue:55:13 +selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:14:5 + ./yamlcycle.cue:14:12 + ./yamlcycle.cue:15:5 +selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:14:5 + ./yamlcycle.cue:14:12 + ./yamlcycle.cue:14:20 + ./yamlcycle.cue:15:8 +selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:14:5 + ./yamlcycle.cue:14:12 + ./yamlcycle.cue:14:20 + ./yamlcycle.cue:15:8 +selfCycle.t1.c.d.d.d: structural cycle: + ./yamlcycle.cue:14:5 +selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:20:5 + ./yamlcycle.cue:20:12 + ./yamlcycle.cue:21:5 +selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: + ./yamlcycle.cue:20:5 + ./yamlcycle.cue:20:12 + ./yamlcycle.cue:20:20 +selfCycle.t2.c.d.d: structural cycle: + ./yamlcycle.cue:20:5 +selfCycle.yamlVal.t1.x.y.y: structural cycle: + ./yamlcycle.cue:24:22 +selfCycle.yamlVal.t2.x.y.y: structural cycle: + ./yamlcycle.cue:32:22 +selfCycle.yamlFun.t1.x.y.y: structural cycle: + ./yamlcycle.cue:36:29 +selfCycle.yamlFun.t2.x.y.y: structural cycle: + ./yamlcycle.cue:41:28 +selfCycle.yamlValidatePartial.x.y.y: structural cycle: + ./yamlcycle.cue:44:29 Result: (_|_){ @@ -1075,24 +1399,24 @@ Result: // [eval] #x: (_|_){ // [eval] noCycle.t1._s.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 // noCycle.t1._s.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 } } #x: (_|_){ // [eval] noCycle.t1.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 // noCycle.t1.#x.#x: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:6:7 - // ./cycle.cue:6:14 - // ./cycle.cue:7:13 + // ./cycle.cue:4:7 + // ./cycle.cue:4:14 + // ./cycle.cue:5:13 } } } @@ -1155,57 +1479,6 @@ Result: } } } - yamlNoCycle: (struct){ - #c: (#struct){ - b?: (string){ string } - a?: ((string|bytes)){ "encoding/yaml".Validate(yamlNoCycle.#c) } - } - data: (#struct){ - b?: (string){ string } - a: (string){ "{a: \"b: foo\"}" } - } - } - selfCycle: (_|_){ - // [eval] - t1: (_|_){ - // [eval] - c: (_|_){ - // [eval] selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:47:5 - // ./cycle.cue:47:12 - // ./cycle.cue:48:5 - // selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:47:5 - // ./cycle.cue:47:12 - // ./cycle.cue:47:20 - // ./cycle.cue:48:8 - // selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:47:5 - // ./cycle.cue:47:12 - // ./cycle.cue:47:20 - // ./cycle.cue:48:8 - // selfCycle.t1.c.d.d.d: structural cycle: - // ./cycle.cue:47:5 - d: (struct){ - } - } - } - t2: (_|_){ - // [eval] - c: (_|_){ - // [eval] selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:53:5 - // ./cycle.cue:53:12 - // ./cycle.cue:54:5 - // selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: - // ./cycle.cue:53:5 - // ./cycle.cue:53:12 - // ./cycle.cue:53:20 - // selfCycle.t2.c.d.d: structural cycle: - // ./cycle.cue:53:5 - } - } - } builtinCyclePerm0: (struct){ X: (string){ "mod.test" } Y: (struct){ @@ -1260,6 +1533,37 @@ Result: host: (string){ "mod.test" } } } + jsonCycle: (_|_){ + // [structural cycle] + t1: (_|_){ + // [structural cycle] + x: (_|_){ + // [structural cycle] jsonCycle.t1.x.y.y: structural cycle: + // ./jsoncycle.cue:4:22 + } + } + } + listMatchN: (_|_){ + // [eval] + structCycle: (_|_){ + // [eval] + x: (_|_){ + // [eval] listMatchN.structCycle.x.y.y: structural cycle: + // ./listmatchncycle.cue:4:23 + } + } + ok: (struct){ + x: (struct){ + y?: (list){ list.MatchN(1, listMatchN.ok.x) } + } + z: (struct){ + y: (#list){ + 0: (struct){ + } + } + } + } + } issue3410: (struct){ _s: (struct){ #x: (_){ matchN(1, (#list){ @@ -1401,6 +1705,105 @@ Result: }) } } } + yamlNoCycle: (struct){ + #c: (#struct){ + b?: (string){ string } + a?: ((string|bytes)){ "encoding/yaml".Validate(yamlNoCycle.#c) } + } + data: (#struct){ + b?: (string){ string } + a: (string){ "{a: \"b: foo\"}" } + } + } + selfCycle: (_|_){ + // [eval] + t1: (_|_){ + // [eval] + c: (_|_){ + // [eval] selfCycle.t1.c: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:14:5 + // ./yamlcycle.cue:14:12 + // ./yamlcycle.cue:15:5 + // selfCycle.t1.c.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:14:5 + // ./yamlcycle.cue:14:12 + // ./yamlcycle.cue:14:20 + // ./yamlcycle.cue:15:8 + // selfCycle.t1.c.d.d: invalid value {d:{}} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:14:5 + // ./yamlcycle.cue:14:12 + // ./yamlcycle.cue:14:20 + // ./yamlcycle.cue:15:8 + // selfCycle.t1.c.d.d.d: structural cycle: + // ./yamlcycle.cue:14:5 + d: (struct){ + } + } + } + t2: (_|_){ + // [eval] + c: (_|_){ + // [eval] selfCycle.t2.c: invalid value {} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:20:5 + // ./yamlcycle.cue:20:12 + // ./yamlcycle.cue:21:5 + // selfCycle.t2.c.d: invalid value {} (does not satisfy matchN): 0 matched, expected 1: + // ./yamlcycle.cue:20:5 + // ./yamlcycle.cue:20:12 + // ./yamlcycle.cue:20:20 + // selfCycle.t2.c.d.d: structural cycle: + // ./yamlcycle.cue:20:5 + } + } + yamlVal: (_|_){ + // [eval] + t1: (_|_){ + // [eval] + x: (_|_){ + // [eval] selfCycle.yamlVal.t1.x.y.y: structural cycle: + // ./yamlcycle.cue:24:22 + } + } + t2: (_|_){ + // [eval] + x: (_|_){ + // [eval] selfCycle.yamlVal.t2.x.y.y: structural cycle: + // ./yamlcycle.cue:32:22 + } + } + } + yamlFun: (_|_){ + // [eval] + t1: (_|_){ + // [structural cycle] + x: (_|_){ + // [structural cycle] selfCycle.yamlFun.t1.x.y.y: structural cycle: + // ./yamlcycle.cue:36:29 + } + } + t2: (_|_){ + // [eval] + z: (_|_){ + // [eval] + y: (_|_){ + // [eval] selfCycle.yamlFun.t2.x.y.y: structural cycle: + // ./yamlcycle.cue:41:28 + } + } + x: (_|_){ + // [eval] selfCycle.yamlFun.t2.x.y.y: structural cycle: + // ./yamlcycle.cue:41:28 + } + } + } + yamlValidatePartial: (_|_){ + // [structural cycle] + x: (_|_){ + // [structural cycle] selfCycle.yamlValidatePartial.x.y.y: structural cycle: + // ./yamlcycle.cue:44:29 + } + } + } } -- out/compile -- --- cycle.cue @@ -1478,38 +1881,6 @@ Result: } } } - yamlNoCycle: { - #c: { - b?: string - a?: 〈import;"encoding/yaml"〉.Validate(〈1;#c〉) - } - data: 〈0;#c〉 - data: { - a: "{a: \"b: foo\"}" - } - } - selfCycle: { - t1: { - c: matchN(1, [ - { - d: 〈2;c〉 - }, - ]) - c: { - d: {} - } - } - } - selfCycle: { - t2: { - c: matchN(1, [ - { - d: 〈2;c〉 - }, - ]) - c: {} - } - } } --- in.cue { @@ -1562,6 +1933,47 @@ Result: } } } +--- jsoncycle.cue +{ + jsonCycle: { + t1: { + x: { + y: 〈import;"encoding/json"〉.Validate(〈1;x〉) + } + x: { + y: "{}" + } + } + } +} +--- listmatchncycle.cue +{ + listMatchN: { + structCycle: { + x: { + y: 〈import;list〉.MatchN(1, 〈1;x〉) + } + x: { + y: [ + {}, + ] + } + } + } + listMatchN: { + ok: { + x: { + y?: 〈import;list〉.MatchN(1, 〈1;x〉) + } + z: 〈0;x〉 + z: { + y: [ + {}, + ] + } + } + } +} --- matchn.cue { issue3410: { @@ -1662,3 +2074,100 @@ Result: } } } +--- yamlcycle.cue +{ + yamlNoCycle: { + #c: { + b?: string + a?: 〈import;"encoding/yaml"〉.Validate(〈1;#c〉) + } + data: 〈0;#c〉 + data: { + a: "{a: \"b: foo\"}" + } + } + selfCycle: { + t1: { + c: matchN(1, [ + { + d: 〈2;c〉 + }, + ]) + c: { + d: {} + } + } + } + selfCycle: { + t2: { + c: matchN(1, [ + { + d: 〈2;c〉 + }, + ]) + c: {} + } + } + selfCycle: { + yamlVal: { + t1: { + x: { + y: 〈import;"encoding/yaml"〉.Validate(〈1;x〉) + } + x: { + y: "{}" + } + } + } + } + selfCycle: { + yamlVal: { + t2: { + x: { + y: 〈import;"encoding/yaml"〉.Validate(〈1;x〉) + } + x: { + y: "{}" + } + x: { + y: 〈import;"encoding/yaml"〉.Validate(〈1;x〉) + } + x: { + y: "{}" + } + } + } + } + selfCycle: { + yamlFun: { + t1: { + x: { + y?: 〈import;"encoding/yaml"〉.Validate("{}", 〈1;x〉) + } + } + } + } + selfCycle: { + yamlFun: { + t2: { + z: (〈0;x〉 & _|_(no sharing)) + z: { + y: "{}" + } + x: { + y: 〈import;"encoding/yaml"〉.Validate("{}", 〈1;x〉) + } + } + } + } + selfCycle: { + yamlValidatePartial: { + x: { + y: 〈import;"encoding/yaml"〉.ValidatePartial(〈1;x〉) + } + x: { + y: "{}" + } + } + } +} diff --git a/internal/core/adt/eval_test.go b/internal/core/adt/eval_test.go index 159a4009a..1bf1b6234 100644 --- a/internal/core/adt/eval_test.go +++ b/internal/core/adt/eval_test.go @@ -76,6 +76,7 @@ var needFix = map[string]string{ // counter errors. // TODO: These counters should all go to zero. var skipDebugDepErrors = map[string]int{ + "cycle/builtins": 3, "disjunctions/elimination": 4, "eval/notify": 3, } diff --git a/internal/pkg/context.go b/internal/pkg/context.go index 374903843..ff0315a92 100644 --- a/internal/pkg/context.go +++ b/internal/pkg/context.go @@ -51,8 +51,13 @@ func (c *CallCtxt) Do() bool { } // Schema returns the ith argument as is, without converting it to a cue.Value. +// +// TODO: Schema should use CallContext.Expr to capture cycle information. +// However, this only makes sense if functions also use the same OpContext for +// further evaluation. We should enforce as we port the old calls. func (c *CallCtxt) Schema(i int) Schema { - return value.Make(c.ctx, c.args[i]) + v := c.Expr(i) + return value.Make(c.ctx, v) } // Value returns a finalized cue.Value for the ith argument. diff --git a/pkg/encoding/json/manual.go b/pkg/encoding/json/manual.go index c33d4f0bc..54368c1f5 100644 --- a/pkg/encoding/json/manual.go +++ b/pkg/encoding/json/manual.go @@ -134,6 +134,11 @@ func Unmarshal(b []byte) (ast.Expr, error) { // specified by v. func Validate(b []byte, v pkg.Schema) (bool, error) { c := value.OpContext(v) + return validate(c, b, v) +} + +// validate is the actual implementation of Validate. +func validate(c *adt.OpContext, b []byte, v pkg.Schema) (bool, error) { if !json.Valid(b) { return false, fmt.Errorf("json: invalid JSON") } diff --git a/pkg/encoding/json/pkg.go b/pkg/encoding/json/pkg.go index 189709db1..a8e6d1d3b 100644 --- a/pkg/encoding/json/pkg.go +++ b/pkg/encoding/json/pkg.go @@ -123,7 +123,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { b, v := c.Bytes(0), c.Schema(1) if c.Do() { - c.Ret, c.Err = Validate(b, v) + c.Ret, c.Err = validate(c.OpContext(), b, v) } }, }}, diff --git a/pkg/encoding/json/testdata/gen.txtar b/pkg/encoding/json/testdata/gen.txtar index fac2d77b0..aeba97a14 100644 --- a/pkg/encoding/json/testdata/gen.txtar +++ b/pkg/encoding/json/testdata/gen.txtar @@ -116,7 +116,7 @@ validate: { schema: { a: <3 } - result: _|_ // validate.t2.result: error in call to encoding/json.Validate: validate.t2.schema.a: invalid value 10 (out of bound <3) + result: _|_ // validate.t2.result: error in call to encoding/json.Validate: validate.t2.result.a: invalid value 10 (out of bound <3) } disjunctionRequired: { str: *"{\"a\":10}" | string diff --git a/pkg/encoding/yaml/manual.go b/pkg/encoding/yaml/manual.go index 71d0bd3ae..5bf3db396 100644 --- a/pkg/encoding/yaml/manual.go +++ b/pkg/encoding/yaml/manual.go @@ -20,6 +20,7 @@ import ( "cuelang.org/go/cue" "cuelang.org/go/cue/ast" + "cuelang.org/go/internal/core/adt" cueyaml "cuelang.org/go/internal/encoding/yaml" "cuelang.org/go/internal/pkg" "cuelang.org/go/internal/value" @@ -87,15 +88,31 @@ func UnmarshalStream(data []byte) (ast.Expr, error) { // Validate validates YAML and confirms it is an instance of schema. // If the YAML source is a stream, every object must match v. func Validate(b []byte, v pkg.Schema) (bool, error) { + // This function is left for Go documentation. The package entry calls + // cueyaml.Validate directly, passing it the call context. + ctx := value.OpContext(v) return cueyaml.Validate(ctx, b, v) } +// validate is the actual implementation of Validate. +func validate(c *adt.OpContext, b []byte, v pkg.Schema) (bool, error) { + return cueyaml.Validate(c, b, v) +} + // ValidatePartial validates YAML and confirms it matches the constraints // specified by v using unification. This means that b must be consistent with, // but does not have to be an instance of v. If the YAML source is a stream, // every object must match v. func ValidatePartial(b []byte, v pkg.Schema) (bool, error) { + // This function is left for Go documentation. The package entry calls + // cueyaml.ValidatePartial directly, passing it the call context. + ctx := value.OpContext(v) return cueyaml.ValidatePartial(ctx, b, v) } + +// validatePartial is the actual implementation of ValidatePartial. +func validatePartial(c *adt.OpContext, b []byte, v pkg.Schema) (bool, error) { + return cueyaml.ValidatePartial(c, b, v) +} diff --git a/pkg/encoding/yaml/pkg.go b/pkg/encoding/yaml/pkg.go index d1a31f150..4c117d447 100644 --- a/pkg/encoding/yaml/pkg.go +++ b/pkg/encoding/yaml/pkg.go @@ -73,7 +73,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { b, v := c.Bytes(0), c.Schema(1) if c.Do() { - c.Ret, c.Err = Validate(b, v) + c.Ret, c.Err = validate(c.OpContext(), b, v) } }, }, { @@ -87,7 +87,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { b, v := c.Bytes(0), c.Schema(1) if c.Do() { - c.Ret, c.Err = ValidatePartial(b, v) + c.Ret, c.Err = validatePartial(c.OpContext(), b, v) } }, }}, diff --git a/pkg/encoding/yaml/testdata/gen.txtar b/pkg/encoding/yaml/testdata/gen.txtar index 1843ced0b..f5b1a551c 100644 --- a/pkg/encoding/yaml/testdata/gen.txtar +++ b/pkg/encoding/yaml/testdata/gen.txtar @@ -51,10 +51,10 @@ unmarshalTrailingInput.invalid: error in call to encoding/yaml.Unmarshal: expect ./in.cue:28:11 Result: -t1: _|_ // t1: error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3) +t1: _|_ // t1: error in call to encoding/yaml.Validate: t1.a: invalid value 4 (out of bound <3) t2: true -t3: _|_ // t3: error in call to encoding/yaml.Validate: b: incomplete value int -t4: _|_ // t4: error in call to encoding/yaml.ValidatePartial: a: invalid value 4 (out of bound <3) +t3: _|_ // t3: error in call to encoding/yaml.Validate: t3.b: incomplete value int +t4: _|_ // t4: error in call to encoding/yaml.ValidatePartial: t4.a: invalid value 4 (out of bound <3) t5: true t6: true t7: """ diff --git a/pkg/encoding/yaml/testdata/validate.txtar b/pkg/encoding/yaml/testdata/validate.txtar index 918f42827..f21abd615 100644 --- a/pkg/encoding/yaml/testdata/validate.txtar +++ b/pkg/encoding/yaml/testdata/validate.txtar @@ -79,7 +79,7 @@ validate: { // TODO: fix this test: the second disjunct should be eliminated, so there // should not be a concreteness error. t1: { - ok1: _|_ // validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate): validate.t1.ok1: error in call to encoding/yaml.Validate: incomplete value {a:2} | {a:2,b!:int} + ok1: _|_ // validate.t1.ok1: invalid value "a: 2" (does not satisfy encoding/yaml.Validate): validate.t1.ok1: error in call to encoding/yaml.Validate: validate.t1.ok1: incomplete value {a:2} | {a:2,b!:int} ok2: "a: 2" ok3: "a: 2" } diff --git a/pkg/gen.go b/pkg/gen.go index ff39d1fd3..d96acab98 100644 --- a/pkg/gen.go +++ b/pkg/gen.go @@ -338,12 +338,17 @@ func (g *generator) genFunc(fn *types.Func) { fmt.Fprintf(g.w, "Name: %q,\n", fn.Name()) + needCallContext := false args := []string{} vals := []string{} kind := []string{} for i := 0; i < params.Len(); i++ { param := params.At(i) - methodName := g.callCtxtGetter(param.Type()) + typ := param.Type() + if typ.String() == "cuelang.org/go/internal/pkg.Schema" { + needCallContext = true + } + methodName := g.callCtxtGetter(typ) argKind := g.adtKind(param.Type()) vals = append(vals, fmt.Sprintf("c.%s(%d)", methodName, len(args))) args = append(args, param.Name()) @@ -368,6 +373,17 @@ func (g *generator) genFunc(fn *types.Func) { init = fmt.Sprintf("%s := %s", argList, valList) } + name := fn.Name() + if needCallContext { + argList = "c.OpContext(), " + argList + + // Main function is used for Godoc documentation. Once we have proper + // CUE function signatures, we can remove these stubs. + // NOTE: this will not work for scripts that are not cased. But this + // is intended to be a temporary situation anyway. + name = strings.ToLower(name[:1]) + name[1:] + } + fmt.Fprintf(g.w, "Func: func(c *pkg.CallCtxt) {") defer fmt.Fprintln(g.w, "},") fmt.Fprintln(g.w) @@ -377,9 +393,9 @@ func (g *generator) genFunc(fn *types.Func) { fmt.Fprintln(g.w, "if c.Do() {") defer fmt.Fprintln(g.w, "}") if results.Len() == 1 { - fmt.Fprintf(g.w, "c.Ret = %s(%s)", fn.Name(), argList) + fmt.Fprintf(g.w, "c.Ret = %s(%s)", name, argList) } else { - fmt.Fprintf(g.w, "c.Ret, c.Err = %s(%s)", fn.Name(), argList) + fmt.Fprintf(g.w, "c.Ret, c.Err = %s(%s)", name, argList) } } diff --git a/pkg/list/list.go b/pkg/list/list.go index 8e7ee79d7..e42162feb 100644 --- a/pkg/list/list.go +++ b/pkg/list/list.go @@ -333,6 +333,11 @@ func Contains(a []cue.Value, v cue.Value) bool { // Likewise, "matchValue" will usually be a non-concrete value. func MatchN(list []cue.Value, n pkg.Schema, matchValue pkg.Schema) (bool, error) { c := value.OpContext(n) + return matchN(c, list, n, matchValue) +} + +// matchN is the actual implementation of MatchN. +func matchN(c *adt.OpContext, list []cue.Value, n pkg.Schema, matchValue pkg.Schema) (bool, error) { var nmatch int64 for _, w := range list { vx := adt.Unify(c, value.Vertex(matchValue), value.Vertex(w)) @@ -342,7 +347,7 @@ func MatchN(list []cue.Value, n pkg.Schema, matchValue pkg.Schema) (bool, error) } } - ctx := value.Context(n) + ctx := value.Context(c) if err := n.Unify(ctx.Encode(nmatch)).Err(); err != nil { return false, pkg.ValidationError{B: &adt.Bottom{ diff --git a/pkg/list/pkg.go b/pkg/list/pkg.go index b58dc3a48..0cc45bf8d 100644 --- a/pkg/list/pkg.go +++ b/pkg/list/pkg.go @@ -167,7 +167,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { list, n, matchValue := c.List(0), c.Schema(1), c.Schema(2) if c.Do() { - c.Ret, c.Err = MatchN(list, n, matchValue) + c.Ret, c.Err = matchN(c.OpContext(), list, n, matchValue) } }, }, { -- 2.51.2