internal/core/adt: precompute optional field type info

HasOptional now only refers to `...T`
(which is not implemented yet)

Change-Id: I5f885894218694dba57e821df6aaa1d42f0c6982
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8202
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
index 1a37a41..74031fa 100644
--- a/internal/core/adt/expr.go
+++ b/internal/core/adt/expr.go
@@ -50,6 +50,8 @@
 	IsOpen      bool // has a ...
 	initialized bool
 
+	types OptionalType
+
 	// administrative fields like hasreferences.
 	// hasReferences bool
 }
@@ -59,6 +61,10 @@
 	Optional []Node
 }
 
+func (x *StructLit) HasOptional() bool {
+	return x.types&(HasField|HasPattern|HasAdditional) != 0
+}
+
 func (x *StructLit) Source() ast.Node { return x.Src }
 
 func (x *StructLit) evaluate(c *OpContext) Value {
@@ -96,9 +102,11 @@
 				o.Fields = append(o.Fields, FieldInfo{Label: x.Label})
 			}
 			o.Fields[p].Optional = append(o.Fields[p].Optional, x)
+			o.types |= HasField
 
 		case *DynamicField:
 			o.Dynamic = append(o.Dynamic, x)
+			o.types |= HasDynamic
 
 		case Expr:
 			o.HasEmbed = true
@@ -107,12 +115,16 @@
 
 		case *BulkOptionalField:
 			o.Bulk = append(o.Bulk, x)
+			o.types |= HasPattern
 
 		case *Ellipsis:
 			expr := x.Value
 			if x.Value == nil {
 				o.IsOpen = true
+				o.types |= IsOpen
 				expr = &Top{}
+			} else {
+				o.types |= HasAdditional
 			}
 			o.Additional = append(o.Additional, expr)
 
@@ -131,26 +143,8 @@
 	return -1
 }
 
-func (o *StructLit) OptionalTypes() (mask OptionalType) {
-	for _, f := range o.Fields {
-		if len(f.Optional) > 0 {
-			mask = HasField
-			break
-		}
-	}
-	if len(o.Dynamic) > 0 {
-		mask |= HasDynamic
-	}
-	if len(o.Bulk) > 0 {
-		mask |= HasPattern
-	}
-	if o.Additional != nil {
-		mask |= HasAdditional
-	}
-	if o.IsOpen {
-		mask |= IsOpen
-	}
-	return mask
+func (o *StructLit) OptionalTypes() OptionalType {
+	return o.types
 }
 
 func (o *StructLit) IsOptional(label Feature) bool {
diff --git a/internal/core/adt/optional_test.go b/internal/core/adt/optional_test.go
index 63f26e2..8204b22 100644
--- a/internal/core/adt/optional_test.go
+++ b/internal/core/adt/optional_test.go
@@ -32,7 +32,7 @@
 		in: `
 		...
 		`,
-		out: adt.HasAdditional | adt.IsOpen,
+		out: adt.IsOpen,
 	}, {
 		in: `
 		[string]: int