internal/core/eval: separate out dynamic fields

Change-Id: Ide3e5b04807c07ad969ea42617de748b7be31510
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7983
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/eval/closed.go b/internal/core/eval/closed.go
index c5a8aa9..8dbcc2b 100644
--- a/internal/core/eval/closed.go
+++ b/internal/core/eval/closed.go
@@ -732,6 +732,13 @@
 			continue
 		}
 
+		for _, b := range o.Dynamic {
+			m := dynamicMatcher{b.Key}
+			if m.Match(ctx, o.env, f) {
+				return true, false
+			}
+		}
+
 		for _, b := range o.Bulk {
 			if b.check.Match(ctx, o.env, f) {
 				return true, false
diff --git a/internal/core/eval/optionals.go b/internal/core/eval/optionals.go
index ad27e33..5e3d2cf 100644
--- a/internal/core/eval/optionals.go
+++ b/internal/core/eval/optionals.go
@@ -35,6 +35,8 @@
 	// Required Fields are marked as empty
 	Fields []FieldInfo
 
+	Dynamic []*adt.DynamicField
+
 	// excluded are all literal fields that already exist.
 	Bulk []bulkField
 
@@ -49,12 +51,11 @@
 			break
 		}
 	}
-	for _, b := range o.Bulk {
-		if b.expr == nil {
-			mask |= adt.HasDynamic
-		} else {
-			mask |= adt.HasPattern
-		}
+	if len(o.Dynamic) > 0 {
+		mask |= adt.HasDynamic
+	}
+	if len(o.Bulk) > 0 {
+		mask |= adt.HasPattern
 	}
 	if o.Additional != nil {
 		mask |= adt.HasAdditional
@@ -92,6 +93,12 @@
 	if o.fieldIndex(f) >= 0 {
 		return true
 	}
+	for _, d := range o.Dynamic {
+		m := dynamicMatcher{d.Key}
+		if m.Match(c, o.env, f) {
+			return true
+		}
+	}
 	for _, b := range o.Bulk {
 		if b.check.Match(c, o.env, f) {
 			return true
@@ -179,8 +186,7 @@
 }
 
 func (o *fieldSet) AddDynamic(c *adt.OpContext, x *adt.DynamicField) {
-	// not in bulk: count as regular field?
-	o.Bulk = append(o.Bulk, bulkField{dynamicMatcher{x.Key}, nil, false})
+	o.Dynamic = append(o.Dynamic, x)
 }
 
 func (o *fieldSet) AddBulk(c *adt.OpContext, x *adt.BulkOptionalField) {