blob: 5d82834c91309a332c538a3730e06ff0d83779b5 [file] [log] [blame]
// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cue
import (
"sort"
)
// A mergedValues type merges structs without unifying their templates.
// It evaluates structs in parallel and then creates a new mergedValues
// for each duplicate arc. The mergedValues do not reappear once there is
// only a single value per arc.
//
// This is used to merge different instances which may have incompatible
// specializations, but have disjuncts objects that may otherwise be shared
// in the same namespace.
type mergedValues struct {
baseValue
values []value
}
func (x *mergedValues) evalPartial(ctx *context) evaluated {
var structs []*structLit
for _, v := range x.values {
v = v.evalPartial(ctx)
o, ok := v.(*structLit)
if !ok {
v := x.values[0]
for _, w := range x.values[1:] {
v = mkBin(ctx, w.Pos(), opUnify, v, w)
}
return v.evalPartial(ctx)
}
o, err := o.expandFields(ctx)
if err != nil {
return err
}
structs = append(structs, o)
}
// Pre-expand the arcs so that we can discard the templates.
obj := &structLit{
baseValue: structs[0].baseValue,
}
var arcs arcs
for _, v := range structs {
for i := 0; i < len(v.arcs); i++ {
w := v.iterAt(ctx, i)
arcs = append(arcs, w)
}
}
obj.arcs = arcs
sort.Stable(obj)
values := []value{}
for _, v := range structs {
if v.emit != nil {
values = append(values, v.emit)
}
}
switch len(values) {
case 0:
case 1:
obj.emit = values[0]
default:
obj.emit = &mergedValues{values[0].base(), values}
}
// merge arcs
k := 0
for i := 0; i < len(arcs); k++ {
a := arcs[i]
// TODO: consider storing the evaluated value. This is a performance
// versus having more information tradeoff. It results in the same
// value.
values := []value{a.v}
for i++; i < len(arcs) && a.feature == arcs[i].feature; i++ {
values = append(values, arcs[i].v)
a.optional = a.optional && arcs[i].optional
var err evaluated
a.attrs, err = unifyAttrs(ctx, a.v, a.attrs, arcs[i].attrs)
if err != nil {
return err
}
a.docs = mergeDocs(a.docs, arcs[i].docs)
}
if len(values) == 1 {
arcs[k] = a
continue
}
a.cache = nil
a.v = &mergedValues{a.v.base(), values}
arcs[k] = a
}
obj.arcs = arcs[:k]
return obj
}
func (x *mergedValues) kind() kind {
k := x.values[0].kind()
for _, v := range x.values {
k = unifyType(k, v.kind())
}
return k
}
func (x *mergedValues) rewrite(ctx *context, fn rewriteFunc) value {
vs := make([]value, len(x.values))
for i, v := range x.values {
vs[i] = rewrite(ctx, v, fn)
}
return &mergedValues{x.baseValue, vs}
}
func (x *mergedValues) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
return false
}