cuelang.org/go@v0.13.0/internal/core/subsume/subsume.go (about)

     1  // Copyright 2020 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package subsume defines various subsumption relations.
    16  package subsume
    17  
    18  import (
    19  	"cuelang.org/go/cue/errors"
    20  	"cuelang.org/go/internal"
    21  	"cuelang.org/go/internal/core/adt"
    22  )
    23  
    24  // Profile configures the type of subsumption. One should typically use one
    25  // of the preconfigured profiles.
    26  type Profile struct {
    27  	// Final indicates subsumption should only consider fields that are relevant
    28  	// to data mode, and ignore definitions, hidden fields, pattern constraints
    29  	// and additional constraints.
    30  	Final bool
    31  
    32  	// Defaults indicate that default values should be used for the subsumed
    33  	// value.
    34  	Defaults bool
    35  
    36  	// LeftDefaults indicates that the default value of the subsuming value
    37  	// needs to be taken. This is necessary for simplifications like trim
    38  	// and simplifying disjunctions.
    39  	LeftDefault bool
    40  
    41  	// Ignore optional fields.
    42  	IgnoreOptional bool
    43  
    44  	// IgnoreClosedness ignores closedness of structs and is used for comparing
    45  	// APIs.
    46  	IgnoreClosedness bool
    47  }
    48  
    49  var Simplify = Profile{
    50  	LeftDefault: true,
    51  }
    52  
    53  var CUE = Profile{}
    54  
    55  // Final checks subsumption interpreting the subsumed value as data.
    56  var Final = Profile{
    57  	Final:    true,
    58  	Defaults: true,
    59  }
    60  
    61  // FinalOpen exists as an artifact of the old API. One should probably not use
    62  // this.
    63  var FinalOpen = Profile{
    64  	Final:            true,
    65  	Defaults:         true,
    66  	IgnoreClosedness: true,
    67  }
    68  
    69  // API is subsumption used for APIs.
    70  var API = Profile{
    71  	IgnoreClosedness: true,
    72  }
    73  
    74  // Value subsumes two values based on their logical (evaluated) values.
    75  func Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
    76  	return CUE.Value(ctx, a, b)
    77  }
    78  
    79  func (p *Profile) Value(ctx *adt.OpContext, a, b adt.Value) errors.Error {
    80  	s := subsumer{ctx: ctx, Profile: *p}
    81  	if !s.values(a, b) {
    82  		return s.getError()
    83  	}
    84  	return nil // ignore errors here even if there are some.
    85  }
    86  
    87  func isBottom(x adt.Node) bool {
    88  	b, _ := x.(*adt.Bottom)
    89  	return b != nil
    90  }
    91  
    92  type subsumer struct {
    93  	ctx  *adt.OpContext
    94  	errs errors.Error
    95  
    96  	Profile
    97  
    98  	inexact bool // If true, the result could be a false negative.
    99  	missing adt.Feature
   100  	gt      adt.Value
   101  	lt      adt.Value
   102  }
   103  
   104  func (s *subsumer) errf(msg string, args ...interface{}) {
   105  	b := s.ctx.NewErrf(msg, args...)
   106  	s.errs = errors.Append(s.errs, b.Err)
   107  }
   108  
   109  func unifyValue(c *adt.OpContext, a, b adt.Value) adt.Value {
   110  	v := &adt.Vertex{}
   111  	v.AddConjunct(adt.MakeRootConjunct(c.Env(0), a))
   112  	v.AddConjunct(adt.MakeRootConjunct(c.Env(0), b))
   113  	x, _ := c.Evaluate(c.Env(0), v)
   114  	return x
   115  }
   116  
   117  func (s *subsumer) getError() (err errors.Error) {
   118  	c := s.ctx
   119  	// src := binSrc(token.NoPos, opUnify, gt, lt)
   120  	if s.gt != nil && s.lt != nil {
   121  		// src := binSrc(token.NoPos, opUnify, s.gt, s.lt)
   122  		if s.missing != 0 {
   123  			s.errf("missing field %q", s.missing.SelectorString(c))
   124  		} else if b, ok := unifyValue(c, s.gt, s.lt).(*adt.Bottom); !ok {
   125  			s.errf("value not an instance")
   126  		} else {
   127  			s.errs = errors.Append(s.errs, b.Err)
   128  		}
   129  	}
   130  	if s.errs == nil {
   131  		s.errf("value not an instance")
   132  	}
   133  	err = s.errs
   134  	if s.inexact {
   135  		err = errors.Wrap(err, internal.ErrInexact)
   136  	}
   137  	return err
   138  }