Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/core/adt: Environment.up nil vertex panic when calling Value.Expr #2911

Closed
sahroshan opened this issue Mar 5, 2024 · 3 comments
Closed

Comments

@sahroshan
Copy link

What version of CUE are you using (cue version)?

$ cue version
0.7.1

$ go version
go version go1.21.0 darwin/arm64

Does this issue reproduce with the latest stable release?

yes

What did you do?

  1. create a new cue file at my_cues/stack/stack.cue
// my_cues/stack/stack.cue

package stack
import "strings"

_ci: bool | *false @tag(ci,type=bool)

#aws_region_short: ["use1", "use2", "usw1", "usw2", "eun1", "euw1", "euw3", "euw2", "eus1", "euc1", "ane1", "ane2", "ane3", "aps1", "apse1", "apse2", "ae1", "ase1", "ase2", "ase3", "afs1", "mes1", "sae1", "cac1", "apne3"]
#seperator: "|"

AWSTemplateFormatVersion: "2010-09-09"
Description:              "Master CFT for deploying other  EKS related CFT templates"

Parameters: {
	MainStackName: {
		Type:           "String"
		Description:    "Enter name of the stack which hosts Pods"
		AllowedPattern: "^(aws|gcp)-(\(strings.Join(#aws_region_short, #seperator)))-([a-z0-9]{3,6})-([a-z0-9]{3,5})$"
		if !_ci {
			Default: =~"^(aws|gcp)-(\(strings.Join(#aws_region_short, #seperator)))-([a-z0-9]{3,6})-([a-z0-9]{3,5})$"
		}
	}
}
  1. write main.go to go through the cue and check for any expression.
// main.go

package main

import (
	"fmt"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
	"cuelang.org/go/cue/load"
)

func walkCue(val cue.Value, after func(cue.Value)) {
	fields, structErr := val.Fields()
	if structErr == nil {
		for fields.Next() {
			walkCue(fields.Value(), after)
		}
	}
	list, listErr := val.List()
	if listErr != nil {
		for list.Next() {
			walkCue(list.Value(), after)
		}
	}
	after(val)
}

func checkOperation(v cue.Value) {
	fmt.Println(v)
	op, _ := v.Expr()
	fmt.Println(op == cue.OrOp)
}

func loadCue(cueDirectory string) (cue.Value, error) {
	val := cue.Value{}
	context := cuecontext.New()
	conf := &load.Config{
		Dir:         cueDirectory,
		AllCUEFiles: true,
		DataFiles:   true,
	}
	entrypoints := []string{"stack/stack.cue"}
	buildInstances := load.Instances(entrypoints, conf)
	for _, bi := range buildInstances {
		if bi.Incomplete || bi.Err != nil {
			return val, fmt.Errorf("Error during loading entrypoints: %v", bi.Err)
		}
		val = context.BuildInstance(bi)
	}
	return val, nil
}

func main() {
	cueDirectory := "my_cues"
	val, err := loadCue(cueDirectory)
	if err != nil {
		fmt.Println(err)
		return
	}
	walkCue(val, checkOperation)
}
  1. the above code panics with below error
panic: assertion failed: Environment.up encountered a nil vertex

goroutine 1 [running]:
cuelang.org/go/internal/core/adt.(*OpContext).Assertf(0x140002de078?, {0x140003eafc0?, 0x140002de0b8?}, 0x20?, {0x104812e37?, 0x0?}, {0x0, 0x0, 0x0})
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:104 +0x9c
cuelang.org/go/internal/core/adt.(*Environment).up(0x140002de128?, 0x14000433e60, 0x49e4b10?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/composite.go:107 +0x5c
cuelang.org/go/internal/core/adt.(*OpContext).relNode(0x14000433e60, 0x0?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:326 +0x30
cuelang.org/go/internal/core/adt.(*FieldReference).resolve(0x140003e2d90, 0x1049e2db8?, 0x90?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/expr.go:703 +0x34
cuelang.org/go/internal/core/adt.(*OpContext).evalState(0x14000433e60, {0x1049e2db8, 0x140003e2d90}, 0x2)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:694 +0x170
cuelang.org/go/internal/core/adt.(*OpContext).value(0x1?, {0x1049e2db8?, 0x140003e2d90?}, 0xe0?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:641 +0x28
cuelang.org/go/internal/core/adt.(*CallExpr).evaluate(0x140003f3d70, 0x14000433e60, 0x70?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/expr.go:1462 +0x3c4
cuelang.org/go/internal/core/adt.(*OpContext).evalState(0x14000433e60, {0x1049e3438, 0x140003f3d70}, 0x2)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:690 +0x378
cuelang.org/go/internal/core/adt.(*OpContext).value(0x140002de640?, {0x1049e3438?, 0x140003f3d70?}, 0x3e?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:641 +0x28
cuelang.org/go/internal/core/adt.(*Interpolation).evaluate(0x140003f3d10, 0x14000433e60, 0x10?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/expr.go:1135 +0x88
cuelang.org/go/internal/core/adt.(*OpContext).evalState(0x14000433e60, {0x1049e3378, 0x140003f3d10}, 0x2)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:690 +0x378
cuelang.org/go/internal/core/adt.(*OpContext).evaluateRec(0x14000433e60, {0x140003f5720, {0x1049e01b0, 0x140003f3d10}, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...}}, ...)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/context.go:623 +0x14c
cuelang.org/go/internal/core/adt.(*nodeContext).evalExpr(0x14000417c00, {0x140003f5720, {0x1049e01b0, 0x140003f3d10}, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...}}, ...)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/eval.go:1579 +0x118
cuelang.org/go/internal/core/adt.(*nodeContext).addExprConjunct(0x14000417c00, {0x140003f5720, {0x1049e01b0, 0x140003f3d10}, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...}}, ...)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/eval.go:1520 +0x648
cuelang.org/go/internal/core/adt.(*nodeContext).insertConjuncts(0x14000417c00, 0x20?)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/eval.go:401 +0xc8
cuelang.org/go/internal/core/adt.(*OpContext).unify(0x14000433e60, 0x14000419a70, 0x5)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/eval.go:237 +0xc5c
cuelang.org/go/internal/core/adt.(*Vertex).Finalize(...)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/internal/core/adt/composite.go:650
cuelang.org/go/cue.manifest(...)
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/cue/context.go:477
cuelang.org/go/cue.remakeValue({0x140003c7fb0?, 0x140004183f0?, 0x0?}, 0x104419240?, {0x1049e3378?, 0x140003f3d10?})
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/cue/types.go:701 +0x1c4
cuelang.org/go/cue.Value.Expr({0x140003c7fb0?, 0x140004183f0?, 0x0?})
	/Users/roshan/.gvm/pkgsets/go1.21/global/pkg/mod/cuelang.org/go@v0.7.1/cue/types.go:2358 +0x3ac
main.checkOperation({0x140003c7fb0, 0x140004183f0, 0x0})

What did you expect to see?

expected to see false when checking the operation.

What did you see instead?

panic

below are the combinations i tried which failed

  1. go 1.20 and cue 0.7.1
  2. go 1.21 and cue 0.7.0
@sahroshan sahroshan added NeedsInvestigation Triage Requires triage/attention labels Mar 5, 2024
@mvdan mvdan added panic and removed Triage Requires triage/attention labels Mar 5, 2024
@sahroshan
Copy link
Author

sahroshan commented Mar 8, 2024

After trying out different cue. Below are the observation:

Given a simple cue

a: string
b: bool | *false
if !b {
    c: a
}

The above panic error occurs only when there is a if condition evaluating to true AND there is reference to a key outside the scope of if block.

In this case c: a causes a panic.

@mvdan any workaround till root cause is fixed would be highly appreciated as we are in middle of upgrading our tool's cue library.

@mvdan mvdan self-assigned this Mar 11, 2024
@mvdan mvdan changed the title panics in 0.7.1(go1.21) but worked in 0.4.3(go1.19) internal/core/adt: Environment.up nil vertex panic when calling Value.Expr Mar 18, 2024
@mvdan
Copy link
Member

mvdan commented Mar 18, 2024

Thank you for the detailed report and the reproducer! I believe that this is the same underlying bug as #2411, which did not have a reproducer but shows an extremely similar call stack, as well as #2619, which does have a reproducer as the OpenAPI encoder calls the same Expr method.

I will opt for closing the other two older issues in favor of this one given how you've managed to produce the smallest reproducer so far. Before we mark this bug as "fixed", I will verify that the fix also solves the other two issues, to double check that I didn'g get it wrong in terms of all three bugs having the same root cause.

Below is your reproducer in testscript form, after some extra reduction:

go mod tidy
go run main.go

-- main.go --
package main

import (
	"fmt"

	"cuelang.org/go/cue"
	"cuelang.org/go/cue/cuecontext"
)

func callExprs(val cue.Value) {
	if fields, err := val.Fields(); err == nil {
		for fields.Next() {
			callExprs(fields.Value())
		}
	}
	fmt.Println(val)
	op, vals := val.Expr()
	fmt.Println(op, vals)
}

func main() {
	ctx := cuecontext.New()
	val := ctx.CompileString(`
		a: string
		if true {
			c: a
		}
	`)
	if err := val.Err(); err != nil {
		panic(err)
	}
	callExprs(val)
}
-- go.mod --
module test

go 1.21

require cuelang.org/go v0.8.0

cueckoo pushed a commit that referenced this issue Mar 19, 2024
As part of the comprehension refactor, https://cuelang.org/cl/529517
reintroduced a Conjunct.Expr method, and switched from Elem to it
in multiple places including these two methods.

The commit message said that in these cases the comprehension values
are not appropriate or cannot occur anyway (confusing wording though?),
but the added tests in the last commit clearly show they can happen,
and when they do, using Expr to reach for their values causes
Environment.up panics.

Taking a look at the TestExpr example

    a: string
    if true {
        v: a
    }

the node with UpCounts is:

    {
        a: string
        if true {
            v: 〈1;a〉
        }
    }

The Expr method takes v.Lookup("v") as that is how the tests are set up.
It grabs the single conjunct for that vertex, the "if" comprehension,
and uses its environment along with its Expr to continue.

The environment corresponds to where the comprehension sits,
meaning it belongs to the root struct and has no parent environment,
whereas Expr gave us the underlying FieldReference it produces,
which ends up calling Environment.up with an UpCount of 1,
since the reference to "a" goes up one level due to the comprehension.

It seems like mixing Conjunct.Env with Conjunct.Expr is thus invalid
for the cases where the conjunct contains a comprehension.
In such cases, Env will contain the comprehension's outer environment,
but Expr will contain the comprehension's inner value,
so the Environment and UpCount are off-by-one.

Pairing Conjunct.Env with Conjunct.Elem instead fixes the UpCount panic,
as now they both correspond to the comprehension's outer environment.
It's not clear to me whether this is the right fix beyond solving
the immediate panic, though. Perhaps for comprehensions we _should_
use Expr to reach for the inner value, but then pair that inner value
with the inner Environment somehow.

Fixes #2911, possibly.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: Id992f79b1489dc731d8d0172ae5bca159c272587
cueckoo pushed a commit that referenced this issue Mar 21, 2024
Not doing so could result in bad dereferences.

Closes #2911

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Iff82628f3475c75b35e030569eb87e0363d66ac8
cueckoo pushed a commit that referenced this issue Mar 22, 2024
Not doing so could result in bad dereferences.

Closes #2911

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Ibecd4738115456d408f6c7eb505fd00c66e9d3f0
cueckoo pushed a commit that referenced this issue Mar 25, 2024
Not doing so could result in bad dereferences.

CL https://cuelang.org/cl/529517 introduced a way to
access the expression associated with a comprehension.

In cases where this expression was evaluated within the
original Environment of the Comprehension, this resulted
in a panic due to an unaligned Environment. The problem
was that this value expression needed to be evaluated
using an adjusted Environment that accounts for the
interstitial scopes introduced by the for and let clauses.
In general, these scopes cannot be added ahead of time,
as the associated values are not known before evaluation.

Like is done in other parts of the code, the new EnvExpr
method and function adjusts the Environment by inserting
dummy scopes that allow references in the expression that
reference values outside of the comprehension to be
resolved properly.

The old Expr method and function are retained for use cases
that only need to refer to the value expression without the
need to evaluate or resolve it.

Closes #2911

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Ibecd4738115456d408f6c7eb505fd00c66e9d3f0
@mvdan
Copy link
Member

mvdan commented Mar 26, 2024

@sahroshan @kajogo777 @shortwavedave please let us know if you still encounter this panic :)

cueckoo pushed a commit that referenced this issue Apr 3, 2024
Not doing so could result in bad dereferences.

CL https://cuelang.org/cl/529517 introduced a way to
access the expression associated with a comprehension.

In cases where this expression was evaluated within the
original Environment of the Comprehension, this resulted
in a panic due to an unaligned Environment. The problem
was that this value expression needed to be evaluated
using an adjusted Environment that accounts for the
interstitial scopes introduced by the for and let clauses.
In general, these scopes cannot be added ahead of time,
as the associated values are not known before evaluation.

Like is done in other parts of the code, the new EnvExpr
method and function adjusts the Environment by inserting
dummy scopes that allow references in the expression that
reference values outside of the comprehension to be
resolved properly.

The old Expr method and function are retained for use cases
that only need to refer to the value expression without the
need to evaluate or resolve it.

Closes #2911

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Ibecd4738115456d408f6c7eb505fd00c66e9d3f0
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1186144
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: CUEcueckoo <cueckoo@gmail.com>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
(cherry picked from commit 3e6a8be)
cueckoo pushed a commit that referenced this issue Apr 3, 2024
Not doing so could result in bad dereferences.

CL https://cuelang.org/cl/529517 introduced a way to
access the expression associated with a comprehension.

In cases where this expression was evaluated within the
original Environment of the Comprehension, this resulted
in a panic due to an unaligned Environment. The problem
was that this value expression needed to be evaluated
using an adjusted Environment that accounts for the
interstitial scopes introduced by the for and let clauses.
In general, these scopes cannot be added ahead of time,
as the associated values are not known before evaluation.

Like is done in other parts of the code, the new EnvExpr
method and function adjusts the Environment by inserting
dummy scopes that allow references in the expression that
reference values outside of the comprehension to be
resolved properly.

The old Expr method and function are retained for use cases
that only need to refer to the value expression without the
need to evaluate or resolve it.

Closes #2911

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Ibecd4738115456d408f6c7eb505fd00c66e9d3f0
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1186144
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: CUEcueckoo <cueckoo@gmail.com>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
(cherry picked from commit 3e6a8be)
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1190979
Reviewed-by: Paul Jolly <paul@myitcv.io>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: v0.8.1
Development

No branches or pull requests

2 participants