blob: 2c15badb85ed3e97367a852d78819c22bbae57b6 [file] [log] [blame]
// Copyright 2020 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 subsume defines various subsumption relations.
package subsume
import (
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal"
"cuelang.org/go/internal/core/adt"
)
// Profile configures the type of subsumption. One should typically use one
// of the preconfigured profiles.
type Profile struct {
// Final indicates subsumption should only consider fields that are relevant
// to data mode, and ignore definitions, hidden fields, pattern constraints
// and additional constraints.
Final bool
// Defaults indicate that default values should be used for the subsumed
// value.
Defaults bool
// Ignore optional fields.
IgnoreOptional bool
// IgnoreClosedness ignores closedness of structs and is used for comparing
// APIs.
IgnoreClosedness bool
}
var CUE = Profile{}
// Final checks subsumption interpreting the subsumed value as data.
var Final = Profile{
Final: true,
Defaults: true,
}
// FinalOpen exists as an artifact of the old API. One should probably not use
// this.
var FinalOpen = Profile{
Final: true,
Defaults: true,
IgnoreClosedness: true,
}
// API is subsumption used for APIs.
var API = Profile{
IgnoreClosedness: true,
}
// Value subsumes two values based on their logical (evaluated) values.
func Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
return CUE.Value(ctx, a, b)
}
func (p *Profile) Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
s := subsumer{ctx: ctx, Profile: *p}
if !s.values(a, b) {
return s.getError()
}
return nil // ignore errors here even if there are some.
}
// Check reports whether b is an instance of a.
func (p *Profile) Check(ctx *adt.OpContext, a, b adt.Value) bool {
s := subsumer{ctx: ctx, Profile: *p}
return s.values(a, b)
}
func isBottom(x adt.Node) bool {
b, _ := x.(*adt.Bottom)
return b != nil
}
type subsumer struct {
ctx *adt.OpContext
errs errors.Error
Profile
inexact bool // If true, the result could be a false negative.
missing adt.Feature
gt adt.Value
lt adt.Value
}
func (s *subsumer) errf(msg string, args ...interface{}) {
b := s.ctx.NewErrf(msg, args...)
s.errs = errors.Append(s.errs, b.Err)
}
func unifyValue(c *adt.OpContext, a, b adt.Value) adt.Value {
v := &adt.Vertex{}
v.AddConjunct(adt.MakeRootConjunct(c.Env(0), a))
v.AddConjunct(adt.MakeRootConjunct(c.Env(0), b))
x, _ := c.Evaluate(c.Env(0), v)
return x
}
func (s *subsumer) getError() (err errors.Error) {
c := s.ctx
// src := binSrc(token.NoPos, opUnify, gt, lt)
if s.gt != nil && s.lt != nil {
// src := binSrc(token.NoPos, opUnify, s.gt, s.lt)
if s.missing != 0 {
s.errf("missing field %q", s.missing.SelectorString(c))
} else if b, ok := unifyValue(c, s.gt, s.lt).(*adt.Bottom); !ok {
s.errf("value not an instance")
} else {
s.errs = errors.Append(s.errs, b.Err)
}
}
if s.errs == nil {
s.errf("value not an instance")
}
err = s.errs
if s.inexact {
err = internal.DecorateError(internal.ErrInexact, err)
}
return err
}