evalv3: (x & y).z pattern error at current master #3685
Description
I'm opening this as a separate issue because I don't know if the root cause is the same as in #3672. This reproducer only has 25 lines of code and works with 0.11.1 with both evaluators but not with v3 with current master. It seems to be triggered by the (x & y).z
pattern.
What version of CUE are you using (cue version
)?
Current build of master branch (latest commit "internal/core/adt: fix constraints deduplication")
cue version v0.12.0-loisch
go version go1.22.1
-buildmode exe
-compiler gc
-ldflags -X cuelang.org/go/cmd/cue/cmd.version=v0.12.0-loisch
CGO_ENABLED 1
GOARCH arm64
GOOS darwin
vcs git
vcs.revision 053f47b1a8cdc2419d762aff33e479f8d737a606
vcs.time 2025-01-17T17:06:42Z
vcs.modified false
cue.lang.version v0.12.0
Does this issue reproduce with the latest stable release?
No. Works with old and new evaluator in release 0.11 .1.
What did you do?
Run testscript
# evalv2
env CUE_EXPERIMENT=evalv3=0
exec cue eval out.cue
cmp stdout stdout.golden
# evalv3
env CUE_EXPERIMENT=evalv3=1
exec cue eval out.cue
cmp stdout stdout.golden
-- out.cue --
import "strings"
#ConfigText: string | {UseSectionName: true}
#ConfigTextRule: #ConfigText | {Append: #ConfigText}
#FinDefSetOfConfigText: {
Only: #ConfigTextRule
let str = (#orgForConfigText & {source: Only}).orgSyntax
orgSyntax: "Only \(str)"
}
#orgForConfigText: {
IN=source: #ConfigTextRule
orgSyntax: [
if IN.UseSectionName != _|_ {
"UseSectionName"
},
"Impossible: \(IN)"
][0]
}
works1: #FinDefSetOfConfigText & { Only: { UseSectionName: true } }
-- stdout.golden --
#ConfigText: string | {
UseSectionName: true
}
#ConfigTextRule: string | {
UseSectionName: true
} | {
Append: string | {
UseSectionName: true
}
}
#FinDefSetOfConfigText: {
let str = (#orgForConfigText & {
source: Only
}).orgSyntax
Only: string | {
UseSectionName: true
} | {
Append: string | {
UseSectionName: true
}
}
orgSyntax: "Only \(str)"
}
#orgForConfigText: {
source: string | {
UseSectionName: true
} | {
Append: string | {
UseSectionName: true
}
}
orgSyntax: [
if IN.UseSectionName != _|_ // explicit error (_|_ literal) in source
{
"UseSectionName"
}, "Impossible: \(IN)"][0]
}
works1: {
Only: {
UseSectionName: true
}
orgSyntax: "Only UseSectionName"
}
What did you expect to see?
A passing test
# evalv2 (0.069s)
# evalv3 (0.045s)
PASS
Interpretation / Fixes
There are two fixes for current master.
a) introducing a let-binding in the "caller" #FinDefSetOfConfigText
--- test-v3.cue 2025-01-18 17:44:55.378447729 +0100
+++ test-v3-fix1.cue 2025-01-18 17:43:21.693329534 +0100
@@ -7,7 +7,8 @@
#FinDefSetOfConfigText: {
Only: #ConfigTextRule
- let str = (#orgForConfigText & {source: Only}).orgSyntax
+ let org = (#orgForConfigText & {source: Only})
+ let str = org.orgSyntax
orgSyntax: "Only \(str)"
}
and b) not implicitly unifying source
with { UseSectionName: _ }
in the "callee" but instead creating a new value different from the passed argument and unifying that value:
--- test-v3.cue 2025-01-18 18:00:02.323823497 +0100
+++ test-v3-fix2.cue 2025-01-18 17:44:26.105990733 +0100
@@ -14,7 +14,7 @@
#orgForConfigText: {
IN=source: #ConfigTextRule
orgSyntax: [
- if IN.UseSectionName != _|_ {
+ if (IN & { UseSectionName: _}) != _|_ {
"UseSectionName"
},
"Impossible: \(IN)"
This shouldn't change anything for the let bindings in the caller but it does. I have absolutely no idea how the evaluator works so I'm only guessing. This problem could arise due to some kind of "call by reference" instead of "call by value" semantics of unification when using the (x & y).z pattern. It feels like a failed unification of source
in #orgForConfigText
is somehow "leaking" into to the caller (#FinDefOfConfigText
) but only if the (x & y).z pattern is used.
What did you see instead?
# evalv2 (0.529s)
# evalv3 (0.057s)
> env CUE_EXPERIMENT=evalv3=1
> exec cue eval out.cue
[stdout]
#ConfigText: string | {
UseSectionName: true
}
#ConfigTextRule: string | {
UseSectionName: true
} | {
Append: string | {
UseSectionName: true
}
}
#FinDefSetOfConfigText: {
Only: string | {
UseSectionName: true
} | {
Append: string | {
UseSectionName: true
}
}
orgSyntax: "Only "
}
#orgForConfigText: {
source: string | {
UseSectionName: true
} | {
Append: string | {
UseSectionName: true
}
}
orgSyntax: [
if IN.UseSectionName != _|_ // explicit error (_|_ literal) in source
{
"UseSectionName"
}, "Impossible: \(IN)"][0]
}
works1: {
Only: {
UseSectionName: true
}
orgSyntax: "Only "
}
> cmp stdout stdout.golden
diff stdout stdout.golden
--- stdout
+++ stdout.golden
@@ -9,6 +9,9 @@
}
}
#FinDefSetOfConfigText: {
+ let str = (#orgForConfigText & {
+ source: Only
+ }).orgSyntax
Only: string | {
UseSectionName: true
} | {
@@ -16,7 +19,7 @@
UseSectionName: true
}
}
- orgSyntax: "Only "
+ orgSyntax: "Only \(str)"
}
#orgForConfigText: {
source: string | {
@@ -36,5 +39,5 @@
Only: {
UseSectionName: true
}
- orgSyntax: "Only "
+ orgSyntax: "Only UseSectionName"
}
FAIL: ../let.txtar:9: stdout and stdout.golden differ
failed run