Skip to content

Commit

Permalink
internal/core/adt: introduce ArcNotPresent
Browse files Browse the repository at this point in the history
ArcVoid's use is currently overloaded. The new
evaluator more strictly distinguishes between it.

ArcVoid is renamed to ArcPending, signifying that
it is still unknown which type the arc has and that
more work needs to be done to figure out. This
can be the case for comprehensions.

ArcNonExisting represents an arc that is known to
not exist. These may exist and linger after processing
as it is not always possible to remove an arc from
processing.

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Ie090cae5e6bf5c664f863b1be491cb9ed4696d44
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/551885
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>
Unity-Result: CUEcueckoo <cueckoo@cuelang.org>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
  • Loading branch information
mpvl committed Mar 31, 2023
1 parent 166ac88 commit e3a7fcf
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 26 deletions.
2 changes: 1 addition & 1 deletion cue/testdata/cycle/compbottom2.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ Disjuncts: 205
foo: (string){ "" }
}
a: (struct){
bar: (_|_){
bar*: (_|_){
// [cycle] mutual.mutualCycleFail.a: cycle with field b.foo:
// ./mutual.cue:9:10
}
Expand Down
8 changes: 4 additions & 4 deletions cue/testdata/cycle/compbottomnofinal.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ Disjuncts: 230
port: (string){ "" }
}
b: (struct){
port: (_|_){
port*: (_|_){
// [cycle] minimal.b: cycle with field a.port:
// ./in.cue:18:6
}
Expand Down Expand Up @@ -518,7 +518,7 @@ Disjuncts: 230
Y: (struct){
userinfo: (string){ "user" }
host: (string){ "mod.test" }
port: (_|_){
port*: (_|_){
// [cycle] large.p2.Y: undefined field: port:
// ./in.cue:267:10
}
Expand All @@ -544,7 +544,7 @@ Disjuncts: 230
Y: (struct){
userinfo: (string){ "user" }
host: (string){ "mod.test" }
port: (_|_){
port*: (_|_){
// [cycle] large.p3.Y: undefined field: port:
// ./in.cue:308:10
}
Expand All @@ -570,7 +570,7 @@ Disjuncts: 230
Y: (struct){
userinfo: (string){ "user" }
host: (string){ "mod.test" }
port: (_|_){
port*: (_|_){
// [cycle] large.p4.Y: undefined field: port:
// ./in.cue:351:10
}
Expand Down
36 changes: 27 additions & 9 deletions internal/core/adt/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ type Vertex struct {
// Used for cycle detection.
IsDynamic bool

// hasVoidArc is set if this Vertex has a void arc (e.g. for comprehensions)
hasVoidArc bool
// hasPendingArc is set if this Vertex has a void arc (e.g. for comprehensions)
hasPendingArc bool

// ArcType indicates the level of optionality of this arc.
ArcType ArcType
Expand Down Expand Up @@ -275,17 +275,29 @@ const (
// for foo in case it is defined.
ArcOptional

// ArcPending means that it is not known yet whether an arc exists and that
// its conjuncts need to be processed to find out. This happens when an arc
// is provisionally added as part of a comprehension, but when this
// comprehension has not yet yielded any results.
ArcPending

// ArcNotPresent indicates that this arc is not present and, unlike
// ArcPending, needs no further processing.
ArcNotPresent

// TODO: define a type for optional arcs. This will be needed for pulling
// in optional fields into the Vertex, which, in turn, is needed for
// structure sharing, among other things.
// We could also define types for required fields and potentially lets.

// ArcVoid means that an arc does not exist. This happens when an arc
// is provisionally added as part of a comprehension, but when this
// comprehension has not yet yielded any results.
ArcVoid
)

// definitelyExists reports whether an arc is a constraint or member arc.
// TODO: we should check that users of this call ensure there are no
// ArcPendings.
func (v *Vertex) definitelyExists() bool {
return v.ArcType < ArcPending
}

// ConstraintFromToken converts a given AST constraint token to the
// corresponding ArcType.
func ConstraintFromToken(t token.Token) ArcType {
Expand Down Expand Up @@ -318,6 +330,12 @@ func (a ArcType) Suffix() string {
return "?"
case ArcRequired:
return "!"

// For debugging internal state. This is not CUE syntax.
case ArcPending:
return "*"
case ArcNotPresent:
return "-"
}
return ""
}
Expand Down Expand Up @@ -850,8 +868,8 @@ func (v *Vertex) GetArc(c *OpContext, f Feature, t ArcType) (arc *Vertex, isNew
}
arc = &Vertex{Parent: v, Label: f, ArcType: t}
v.Arcs = append(v.Arcs, arc)
if t == ArcVoid {
v.hasVoidArc = true
if t == ArcPending {
v.hasPendingArc = true
}
return arc, true
}
Expand Down
4 changes: 2 additions & 2 deletions internal/core/adt/comprehension.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (n *nodeContext) insertComprehension(
}

conjunct := MakeConjunct(env, c, ci)
n.node.state.insertFieldUnchecked(f.Label, ArcVoid, conjunct)
n.node.state.insertFieldUnchecked(f.Label, ArcPending, conjunct)
fields = append(fields, f)
// TODO: adjust ci to embed?

Expand All @@ -203,7 +203,7 @@ func (n *nodeContext) insertComprehension(
}

conjunct := MakeConjunct(env, c, ci)
n.node.state.insertFieldUnchecked(f.Label, ArcVoid, conjunct)
n.node.state.insertFieldUnchecked(f.Label, ArcPending, conjunct)
fields = append(fields, f)

default:
Expand Down
14 changes: 7 additions & 7 deletions internal/core/adt/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ func (c *OpContext) evaluate(v *Vertex, r Resolver, state vertexStatus) Value {
// Use node itself to allow for cycle detection.
c.unify(v, state)

if v.ArcType == ArcVoid {
if v.ArcType == ArcPending {
if v.status == evaluating {
for ; v.Parent != nil && v.ArcType == ArcVoid; v = v.Parent {
for ; v.Parent != nil && v.ArcType == ArcPending; v = v.Parent {
}
err := c.Newf("cycle with field %v", r)
b := &Bottom{Code: CycleError, Err: err}
Expand Down Expand Up @@ -492,7 +492,7 @@ func (n *nodeContext) postDisjunct(state vertexStatus) {

switch err := n.getErr(); {
case err != nil:
if err.Code < IncompleteError && n.node.ArcType == ArcVoid {
if err.Code < IncompleteError && n.node.ArcType == ArcPending {
n.node.ArcType = ArcMember
}
n.node.BaseValue = err
Expand Down Expand Up @@ -754,7 +754,7 @@ func (n *nodeContext) completeArcs(state vertexStatus) {
if state <= conjuncts &&
// Is allowed to go one step back. See Vertex.UpdateStatus.
n.node.status <= state+1 &&
(!n.node.hasVoidArc || n.node.ArcType == ArcMember) {
(!n.node.hasPendingArc || n.node.ArcType == ArcMember) {

n.node.updateStatus(conjuncts)
return
Expand All @@ -772,11 +772,11 @@ func (n *nodeContext) completeArcs(state vertexStatus) {
// correctly and that we are not regressing.
n.node.updateStatus(evaluatingArcs)

wasVoid := a.ArcType == ArcVoid
wasVoid := a.ArcType == ArcPending

ctx.unify(a, finalized)

if a.ArcType == ArcVoid {
if a.ArcType == ArcPending {
continue
}

Expand Down Expand Up @@ -1744,7 +1744,7 @@ func (n *nodeContext) addValueConjunct(env *Environment, v Value, id CloseInfo)
n.node.Structs = append(n.node.Structs, x.Structs...)

for _, a := range x.Arcs {
if a.ArcType == ArcVoid {
if !a.definitelyExists() {
continue
}
// TODO(errors): report error when this is a regular field.
Expand Down
4 changes: 3 additions & 1 deletion internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -1882,7 +1882,7 @@ func (x *ForClause) yield(s *compState) {
}

c.unify(a, partial)
if a.ArcType == ArcVoid {
if !a.definitelyExists() {
continue
}

Expand All @@ -1894,13 +1894,15 @@ func (x *ForClause) yield(s *compState) {
// processing, eluding the deallocation step.
status: finalized,
IsDynamic: true,
ArcType: ArcMember,
}

if x.Value != InvalidLabel {
b := &Vertex{
Label: x.Value,
BaseValue: a,
IsDynamic: true,
ArcType: ArcPending,
}
n.Arcs = append(n.Arcs, b)
}
Expand Down
3 changes: 3 additions & 0 deletions internal/core/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ func (w *printer) node(n adt.Node) {
}

for _, a := range x.Arcs {
if a.ArcType == adt.ArcNotPresent {
continue
}
if a.Label.IsLet() {
w.string("\n")
w.string("let ")
Expand Down
4 changes: 2 additions & 2 deletions internal/core/export/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ type conjuncts struct {
func (c *conjuncts) getField(label adt.Feature) field {
f, ok := c.fields[label]
if !ok {
f.arcType = adt.ArcVoid
f.arcType = adt.ArcNotPresent
}
return f
}
Expand Down Expand Up @@ -385,7 +385,7 @@ func (e *conjuncts) addExpr(env *adt.Environment, src *adt.Vertex, x adt.Elem, i

for _, d := range x.Decls {
var label adt.Feature
t := adt.ArcVoid
t := adt.ArcNotPresent
switch f := d.(type) {
case *adt.Field:
label = f.Label
Expand Down

0 comments on commit e3a7fcf

Please sign in to comment.