regression where selecting a disallowed field from an inline struct no longer fails
What version of CUE are you using (cue version)?
$ cue version 0.14.1 / playground
Does this issue reproduce with the latest stable release?
What did you do?
a: (close({}) & {v:1}).v
Note that the following works as expected
a: close({}) & {v:1}
What did you expect to see?
a.v: field not allowed:
What did you see instead?
1
The struct doesn't even have to be empty. It's likely that evaluator just takes the shortcut of (_ & {x: y}).x -> y, disregarding the possibility that _ & {x: y} might result in _|_ instead of a struct.
@imax9000 is correct that this is unrelated to empty structs. Below is a reproducer using a definition and a separate struct, for extra clarity:
# evalv2
env CUE_EXPERIMENT=evalv3=0
exec cue-v0.14.0 export in.cue
# evalv3
env CUE_EXPERIMENT=evalv3=1
exec cue export in.cue
-- in.cue --
#Schema: {
allowed?: int
}
data: {
disallowed: 123
}
output: (#Schema & data).disallowed
# evalv2 (0.012s)
> env CUE_EXPERIMENT=evalv3=0
> exec cue-v0.14.0 export in.cue
[stdout]
{
"data": {
"disallowed": 123
},
"output": 123
}
# evalv3 (0.008s)
> env CUE_EXPERIMENT=evalv3=1
> exec cue export in.cue
[stdout]
{
"data": {
"disallowed": 123
},
"output": 123
}
The old evaluator as of v0.14.0 also shows this behavior. However, going all the way back to v0.4.3 showed a field not allowed error, so I did a bisect and ended up at https://review.gerrithub.io/c/cue-lang/cue/+/546924 from late 2022.
That patch was for fixing https://github.com/cue-lang/cue/issues/2163, a cannot add field X: already used error, and unrelated to closedness.
From looking at the CUE on its own, I think this is simply a bug. https://github.com/cue-lang/cue/issues/3715 already tracks whether it should be possible to select a non-bottom field from a bottom struct, but as far as I can tell, this case is much simpler. The unification of #Schema & data does not result in a non-bottom disallowed field, so whatever 3715 ends up deciding, I don't think this should work.
And the fact that this is a regression seems to support that this should be an error.
In the meantime, the workaround is to separate the unification into another field, such that it's not inline:
#Schema: {
allowed?: int
}
data: {
disallowed: 123
}
_unified: (#Schema & data)
output: _unified.disallowed
Briefly spoke with @mpvl and we both agree that this should fail. The fix from late 2022 was probably a bit too agressive in dealing with inline structs. The workaround via a hidden field shown in https://github.com/cue-lang/cue/issues/4102#issuecomment-3332831251 is OK for now, but we will investigate how to fix this.
I've left a potential fix for @mpvl at https://cue.gerrithub.io/c/cue-lang/cue/+/1224395. It's not complete, as one test fails, and I'm not sure it's entirely correct.
The problem is that closedness is typically checked after finalization. But we do not finalize inline structs. So we will never know, basically. So that needs quite a bit of instrumentation to make that work. Maybe an evalv4 thing.
A lot relies on not finalizing inline structs nowadays, so there are actually a lot of bad breakages. Not saying that this is not addressable, but hopefully the workaround is sufficient for now
Related: https://github.com/cue-lang/cue/issues/2236