cue icon indicating copy to clipboard operation
cue copied to clipboard

regression where selecting a disallowed field from an inline struct no longer fails

Open verdverm opened this issue 3 months ago • 7 comments

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

verdverm avatar Sep 24 '25 21:09 verdverm

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 avatar Sep 24 '25 21:09 imax9000

@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.

mvdan avatar Sep 25 '25 08:09 mvdan

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

mvdan avatar Sep 25 '25 08:09 mvdan

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.

mvdan avatar Sep 29 '25 09:09 mvdan

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.

mvdan avatar Oct 17 '25 10:10 mvdan

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

mpvl avatar Oct 17 '25 13:10 mpvl

Related: https://github.com/cue-lang/cue/issues/2236

mvdan avatar Dec 09 '25 09:12 mvdan