diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go index 9a4e14d4e78..9e0d16c0381 100644 --- a/cmd/cue/cmd/common.go +++ b/cmd/cue/cmd/common.go @@ -20,6 +20,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "github.com/spf13/pflag" @@ -33,6 +34,7 @@ import ( "cuelang.org/go/cue/load" "cuelang.org/go/cue/parser" "cuelang.org/go/cue/token" + "cuelang.org/go/internal/core/adt" "cuelang.org/go/internal/encoding" "cuelang.org/go/internal/filetypes" "cuelang.org/go/internal/value" @@ -539,6 +541,8 @@ func parseArgs(cmd *Command, args []string, cfg *config) (p *buildPlan, err erro return nil, err } + adt.DebugSort, _ = strconv.Atoi(os.Getenv("CUE_DEBUG_SORT_ARCS")) + builds := loadFromArgs(cmd, args, cfg.loadCfg) if builds == nil { return nil, errors.Newf(token.NoPos, "invalid args") diff --git a/cue/types.go b/cue/types.go index 0499065535d..12664f1f094 100644 --- a/cue/types.go +++ b/cue/types.go @@ -1354,7 +1354,7 @@ func (v Value) structValOpts(ctx *adt.OpContext, o options) (s structValue, err } } - features := export.VertexFeatures(obj) + features := export.VertexFeatures(ctx, obj) k := 0 for _, f := range features { diff --git a/internal/core/adt/context.go b/internal/core/adt/context.go index bbcc7b9c10f..f974a40b4c6 100644 --- a/internal/core/adt/context.go +++ b/internal/core/adt/context.go @@ -20,6 +20,7 @@ import ( "os" "reflect" "regexp" + "sort" "strings" "github.com/cockroachdb/apd/v2" @@ -40,6 +41,44 @@ var Debug bool = os.Getenv("CUE_DEBUG") != "0" // 1: logging var Verbosity int +// DebugSort specifies that arcs be sorted consistently between implementations. +// 0: default +// 1: sort by Feature: this should be consistent between implementations where +// there is no change in the compiler and indexing code. +// 2: alphabetical +var DebugSort int + +func DebugSortArcs(c *OpContext, n *Vertex) { + if n.IsList() { + return + } + switch a := n.Arcs; DebugSort { + case 1: + sort.SliceStable(a, func(i, j int) bool { + return a[i].Label < a[j].Label + }) + case 2: + sort.SliceStable(a, func(i, j int) bool { + return a[i].Label.SelectorString(c.Runtime) < + a[j].Label.SelectorString(c.Runtime) + }) + } +} + +func DebugSortFields(c *OpContext, a []Feature) { + switch DebugSort { + case 1: + sort.SliceStable(a, func(i, j int) bool { + return a[i] < a[j] + }) + case 2: + sort.SliceStable(a, func(i, j int) bool { + return a[i].SelectorString(c.Runtime) < + a[j].SelectorString(c.Runtime) + }) + } +} + // Assert panics if the condition is false. Assert can be used to check for // conditions that are considers to break an internal variant or unexpected // condition, but that nonetheless probably will be handled correctly down the diff --git a/internal/core/adt/eval.go b/internal/core/adt/eval.go index d217e11adfc..9f8bb31e393 100644 --- a/internal/core/adt/eval.go +++ b/internal/core/adt/eval.go @@ -639,6 +639,9 @@ func (n *nodeContext) checkClosed(state VertexStatus) bool { } func (n *nodeContext) completeArcs(state VertexStatus) { + if DebugSort > 0 { + DebugSortArcs(n.ctx, n.node) + } if state <= AllArcs { n.node.UpdateStatus(AllArcs) diff --git a/internal/core/export/expr.go b/internal/core/export/expr.go index ae868c2bc76..c8d7a1290d5 100644 --- a/internal/core/export/expr.go +++ b/internal/core/export/expr.go @@ -163,13 +163,17 @@ func (x *exporter) mergeValues(label adt.Feature, src *adt.Vertex, a []conjunct, return fields[i] > fields[j] }) - m := sortArcs(extractFeatures(e.structs)) - sort.SliceStable(fields, func(i, j int) bool { - if m[fields[j]] == 0 { - return m[fields[i]] != 0 - } - return m[fields[i]] > m[fields[j]] - }) + if adt.DebugSort == 0 { + m := sortArcs(extractFeatures(e.structs)) + sort.SliceStable(fields, func(i, j int) bool { + if m[fields[j]] == 0 { + return m[fields[i]] != 0 + } + return m[fields[i]] > m[fields[j]] + }) + } else { + adt.DebugSortFields(e.ctx, fields) + } if len(e.fields) == 0 && !e.hasEllipsis { switch len(e.embed) + len(e.conjuncts) { diff --git a/internal/core/export/toposort.go b/internal/core/export/toposort.go index 4d7935a1889..7f68df97210 100644 --- a/internal/core/export/toposort.go +++ b/internal/core/export/toposort.go @@ -26,7 +26,7 @@ import ( // VertexFeatures returns the feature list of v. The list may include more // features than for which there are arcs and also includes features for // optional fields. It assumes the Structs fields is properly initialized. -func VertexFeatures(v *adt.Vertex) []adt.Feature { +func VertexFeatures(c *adt.OpContext, v *adt.Vertex) []adt.Feature { sets := extractFeatures(v.Structs) m := sortArcs(sets) // TODO: use for convenience. @@ -44,7 +44,11 @@ func VertexFeatures(v *adt.Vertex) []adt.Feature { sets = append(sets, a) } - return sortedArcs(sets) + a = sortedArcs(sets) + if adt.DebugSort > 0 { + adt.DebugSortFields(c, a) + } + return a } // func structFeatures(a []*adt.StructLit) []adt.Feature { @@ -53,6 +57,10 @@ func VertexFeatures(v *adt.Vertex) []adt.Feature { // } func (e *exporter) sortedArcs(v *adt.Vertex) (sorted []*adt.Vertex) { + if adt.DebugSort > 0 { + return v.Arcs + } + a := extractFeatures(v.Structs) if len(a) == 0 { return v.Arcs diff --git a/internal/core/export/value.go b/internal/core/export/value.go index 358da1575ee..53065e9e826 100644 --- a/internal/core/export/value.go +++ b/internal/core/export/value.go @@ -415,7 +415,7 @@ func (e *exporter) structComposite(v *adt.Vertex, attrs []*ast.Attribute) ast.Ex } p := e.cfg - for _, label := range VertexFeatures(v) { + for _, label := range VertexFeatures(e.ctx, v) { show := false switch label.Typ() { case adt.StringLabel: diff --git a/internal/core/subsume/vertex.go b/internal/core/subsume/vertex.go index 362ec292b80..55f83dcff92 100644 --- a/internal/core/subsume/vertex.go +++ b/internal/core/subsume/vertex.go @@ -99,7 +99,7 @@ func (s *subsumer) vertices(x, y *adt.Vertex) bool { } // All arcs in x must exist in y and its values must subsume. - xFeatures := export.VertexFeatures(x) + xFeatures := export.VertexFeatures(s.ctx, x) for _, f := range xFeatures { if s.Final && !f.IsRegular() { continue @@ -165,7 +165,7 @@ func (s *subsumer) vertices(x, y *adt.Vertex) bool { return false } - yFeatures := export.VertexFeatures(y) + yFeatures := export.VertexFeatures(s.ctx, y) outer: for _, f := range yFeatures { if s.Final && !f.IsRegular() {