cuelang.org/go@v0.13.0/internal/core/adt/equality.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 adt
    16  
    17  type Flag uint16
    18  
    19  const (
    20  	// IgnoreOptional allows optional information to be ignored. This only
    21  	// applies when CheckStructural is given.
    22  	IgnoreOptional Flag = 1 << iota
    23  
    24  	// CheckStructural indicates that closedness information should be
    25  	// considered for equality. Equal may return false even when values are
    26  	// equal.
    27  	CheckStructural
    28  
    29  	// RegularOnly indicates that only regular fields should be considered,
    30  	// thus excluding hidden and definition fields.
    31  	RegularOnly
    32  )
    33  
    34  func Equal(ctx *OpContext, v, w Value, flags Flag) bool {
    35  	if x, ok := v.(*Vertex); ok {
    36  		return equalVertex(ctx, x, w, flags)
    37  	}
    38  	if y, ok := w.(*Vertex); ok {
    39  		return equalVertex(ctx, y, v, flags)
    40  	}
    41  	return equalTerminal(ctx, v, w, flags)
    42  }
    43  
    44  func equalVertex(ctx *OpContext, x *Vertex, v Value, flags Flag) bool {
    45  	y, ok := v.(*Vertex)
    46  	if !ok {
    47  		return false
    48  	}
    49  
    50  	// Note that the arc type of an originating node may be different than
    51  	// the one we are sharing. So do this check before dereferencing.
    52  	// For instance:
    53  	//
    54  	//    a?: #B  // ArcOptional
    55  	//    #B: {}  // ArcMember
    56  	if x.ArcType != y.ArcType {
    57  		return false
    58  	}
    59  
    60  	x = x.DerefValue()
    61  	y = y.DerefValue()
    62  
    63  	if x == y {
    64  		return true
    65  	}
    66  
    67  	xk := x.Kind()
    68  	yk := y.Kind()
    69  
    70  	if xk != yk {
    71  		return false
    72  	}
    73  
    74  	maxArcType := ArcMember
    75  	if flags&CheckStructural != 0 {
    76  		// Do not ignore optional fields
    77  		// TODO(required): consider making this unconditional
    78  		maxArcType = ArcOptional
    79  	}
    80  
    81  	// TODO: this really should be subsumption.
    82  	if flags != 0 {
    83  		if x.IsClosedStruct() != y.IsClosedStruct() {
    84  			return false
    85  		}
    86  		if x.IsClosedList() != y.IsClosedList() {
    87  			return false
    88  		}
    89  		if !equalClosed(ctx, x, y, flags) {
    90  			return false
    91  		}
    92  	}
    93  
    94  	skipRegular := flags&RegularOnly != 0
    95  
    96  loop1:
    97  	for _, a := range x.Arcs {
    98  		if (skipRegular && !a.Label.IsRegular()) || a.ArcType > maxArcType {
    99  			continue
   100  		}
   101  		for _, b := range y.Arcs {
   102  			if a.Label == b.Label {
   103  				if a.ArcType != b.ArcType || !Equal(ctx, a, b, flags) {
   104  					return false
   105  				}
   106  				continue loop1
   107  			}
   108  		}
   109  		return false
   110  	}
   111  
   112  loop2:
   113  	for _, b := range y.Arcs {
   114  		if (skipRegular && !b.Label.IsRegular()) || b.ArcType > maxArcType {
   115  			continue
   116  		}
   117  		for _, a := range x.Arcs {
   118  			if a.Label == b.Label {
   119  				if a.ArcType > maxArcType {
   120  					// No need to continue: arc with label not found.
   121  					break
   122  				}
   123  				// Label found. Equality was already tested in loop 1.
   124  				continue loop2
   125  			}
   126  		}
   127  		// Arc with same label not found.
   128  		return false
   129  	}
   130  
   131  	v, ok1 := x.BaseValue.(Value)
   132  	w, ok2 := y.BaseValue.(Value)
   133  	if !ok1 && !ok2 {
   134  		return true // both are struct or list.
   135  	}
   136  
   137  	return equalTerminal(ctx, v, w, flags)
   138  }
   139  
   140  // equalClosed tests if x and y have the same set of close information.
   141  // TODO: the following refinements are possible:
   142  //   - unify optional fields and equate the optional fields
   143  //   - do the same for pattern constraints, where the pattern constraints
   144  //     are collated by pattern equality.
   145  //   - a further refinement would collate patterns by ranges.
   146  //
   147  // For all these refinements it would be necessary to have well-working
   148  // structure sharing so as to not repeatedly recompute optional arcs.
   149  func equalClosed(ctx *OpContext, x, y *Vertex, flags Flag) bool {
   150  	return verifyStructs(x, y, flags) && verifyStructs(y, x, flags)
   151  }
   152  
   153  func verifyStructs(x, y *Vertex, flags Flag) bool {
   154  outer:
   155  	for _, s := range x.Structs {
   156  		if (flags&IgnoreOptional != 0) && !s.StructLit.HasOptional() {
   157  			continue
   158  		}
   159  		if s.span()&DefinitionSpan == 0 {
   160  			if !s.StructLit.HasOptional() {
   161  				continue
   162  			}
   163  		}
   164  		for _, t := range y.Structs {
   165  			if s.StructLit == t.StructLit {
   166  				continue outer
   167  			}
   168  		}
   169  		return false
   170  	}
   171  	return true
   172  }
   173  
   174  func equalTerminal(ctx *OpContext, v, w Value, flags Flag) bool {
   175  	if v == w {
   176  		return true
   177  	}
   178  
   179  	switch x := v.(type) {
   180  	case *Bottom:
   181  		// All errors are logically the same.
   182  		_, ok := w.(*Bottom)
   183  		return ok
   184  
   185  	case *Top:
   186  		_, ok := w.(*Top)
   187  		return ok
   188  
   189  	case *Num, *String, *Bool, *Bytes, *Null:
   190  		if b, ok := BinOp(ctx, EqualOp, v, w).(*Bool); ok {
   191  			return b.B
   192  		}
   193  		return false
   194  
   195  	// TODO: for the remainder we are dealing with non-concrete values, so we
   196  	// could also just not bother.
   197  
   198  	case *BoundValue:
   199  		if y, ok := w.(*BoundValue); ok {
   200  			return x.Op == y.Op && Equal(ctx, x.Value, y.Value, flags)
   201  		}
   202  
   203  	case *BasicType:
   204  		if y, ok := w.(*BasicType); ok {
   205  			return x.K == y.K
   206  		}
   207  
   208  	case *Conjunction:
   209  		y, ok := w.(*Conjunction)
   210  		if !ok || len(x.Values) != len(y.Values) {
   211  			return false
   212  		}
   213  		// always ordered the same
   214  		for i, xe := range x.Values {
   215  			if !Equal(ctx, xe, y.Values[i], flags) {
   216  				return false
   217  			}
   218  		}
   219  		return true
   220  
   221  	case *Disjunction:
   222  		// The best way to compute this is with subsumption, but even that won't
   223  		// be too accurate. Assume structural equivalence for now.
   224  		y, ok := w.(*Disjunction)
   225  		if !ok || len(x.Values) != len(y.Values) {
   226  			return false
   227  		}
   228  		for i, xe := range x.Values {
   229  			if !Equal(ctx, xe, y.Values[i], flags) {
   230  				return false
   231  			}
   232  		}
   233  		return true
   234  
   235  	case *BuiltinValidator:
   236  	}
   237  
   238  	return false
   239  }