encoding/openapi: more accurate handling of disjunctions

Instead of assumingt that the input is from Protobuf,
now actually compute the logic for the "not(anyOf(...))"
elements.

Basically, each element in a disjunction subsumed by
the other elements. All instances of an element need
to be excluded by a "not(anyOf(...))" pattern. For
Protobuf, this means there is exactly one for each
oneOf.

This chance is also no longer preexpands values based
on the node count heuristic. This generally gives
better and more consistent output for Protobuf.

An additional optimization based on unification
eliminates impossible disjuncts to compensate for the
fact that this simplifcation is no longer done by
CUE itself.

Change-Id: I665f529cee24ad084eb0a6d83a373b2392f67e3e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5344
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index da14cf0..2a90b8e 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -27,6 +27,8 @@
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+	"golang.org/x/xerrors"
 )
 
 type buildContext struct {
@@ -339,17 +341,14 @@
 	}
 
 	if count > 0 { // TODO: implement IsAny.
-		// NOTE: Eval is not necessary here. Removing it will yield
-		// different results that also are correct. The difference is that
-		// Eval detects and eliminates impossible combinations at the
-		// expense of having potentially much larger configurations due to
-		// a combinatorial explosion. This rudimentary check picks the least
-		// fo the two extreme forms.
-		if eval := values.Eval(); countNodes(eval) < countNodes(values) {
-			values = eval
+		// TODO: perhaps find optimal representation. For now we assume the
+		// representation as is is already optimized for human consumption.
+		if values.IncompleteKind()&cue.StructKind != cue.StructKind && !isRef {
+			values = values.Eval()
 		}
 
-		for _, v := range appendSplit(nil, cue.AndOp, values) {
+		conjuncts := appendSplit(nil, cue.AndOp, values)
+		for i, v := range conjuncts {
 			switch {
 			case isConcrete(v):
 				b.dispatch(f, v)
@@ -357,15 +356,46 @@
 					b.set("enum", ast.NewList(b.decode(v)))
 				}
 			default:
-				if a := appendSplit(nil, cue.OrOp, v); len(a) > 1 {
-					b.disjunction(a, f)
-				} else {
+				a := appendSplit(nil, cue.OrOp, v)
+				for i, v := range a {
+					if _, r := v.Reference(); len(r) == 0 {
+						a[i] = v.Eval()
+					}
+				}
+
+				if len(a) > 1 {
+					// Filter disjuncts that cannot unify with other conjuncts,
+					// and thus can never be satisfied.
+					// TODO: there should be generalized simplification logic
+					// in CUE (outside of the usual implicit simplifications).
+					k := 0
+				outer:
+					for _, d := range a {
+						for j, w := range conjuncts {
+							if i == j {
+								continue
+							}
+							if d.Unify(w).Err() != nil {
+								continue outer
+							}
+						}
+						a[k] = d
+						k++
+					}
+					a = a[:k]
+				}
+				switch len(a) {
+				case 0:
+					// Conjunct entirely eliminated.
+				case 1:
 					v = a[0]
 					if err := v.Err(); err != nil {
 						b.failf(v, "openapi: %v", err)
 						return
 					}
 					b.dispatch(f, v)
+				default:
+					b.disjunction(a, f)
 				}
 			}
 		}
@@ -487,36 +517,58 @@
 		anyOf = append(anyOf, b.kv("enum", ast.NewList(enums...)))
 	}
 
-	hasAny := false
-	for _, v := range disjuncts {
-		c := newOASBuilder(b)
-		c.value(v, f)
-		t := c.finish()
-		if len(t.Elts) == 0 {
-			if c.typ == "" {
-				hasAny = true
-			}
-			continue
-		}
-		anyOf = append(anyOf, (*ast.StructLit)(t))
-	}
-
-	// If any of the types was "any", a oneOf may be discarded.
-	if !hasAny {
-		// TODO: analyze CUE structs to figure out if it should be oneOf or
-		// anyOf. More precisely, if non of the elements subsume each other,
-		// it is oneOf, if some do, it is anyOf. For closed structs, the
-		// structure needs to be oneOf(X, not(anyOf(X))).
-		// As the source is protobuf for now, it is always the latter form.
-		b.set("oneOf", ast.NewList(
-			append(anyOf,
-				ast.NewStruct("not",
-					ast.NewStruct("anyOf", ast.NewList(anyOf...))))...))
-	}
-
 	if nullable {
 		b.setSingle("nullable", ast.NewBool(true), true)
 	}
+
+	schemas := make([]*ast.StructLit, len(disjuncts))
+	for i, v := range disjuncts {
+		c := newOASBuilder(b)
+		c.value(v, f)
+		t := c.finish()
+		schemas[i] = (*ast.StructLit)(t)
+		if len(t.Elts) == 0 {
+			if c.typ == "" {
+				return
+			}
+		}
+	}
+
+	for i, v := range disjuncts {
+		// In OpenAPI schema are open by default. To ensure forward compability,
+		// we do not represent closed structs with additionalProperties: false
+		// (this is discouraged and often disallowed by implementions), but
+		// rather enforce this by ensuring uniqueness of the disjuncts.
+		//
+		// TODO: subsumption may currently give false negatives. We are extra
+		// conservative in these instances.
+		subsumed := []ast.Expr{}
+		for j, w := range disjuncts {
+			if i == j {
+				continue
+			}
+			err := v.Subsume(w, cue.Schema())
+			if err == nil || xerrors.Is(err, internal.ErrInexact) {
+				subsumed = append(subsumed, schemas[j])
+			}
+		}
+
+		t := schemas[i]
+		if len(subsumed) > 0 {
+			// TODO: elide anyOf if there is only one element. This should be
+			// rare if originating from oneOf.
+			exclude := ast.NewStruct("not",
+				ast.NewStruct("anyOf", ast.NewList(subsumed...)))
+			if len(t.Elts) == 0 {
+				t = exclude
+			} else {
+				t = ast.NewStruct("allOf", ast.NewList(t, exclude))
+			}
+		}
+		anyOf = append(anyOf, t)
+	}
+
+	b.set("oneOf", ast.NewList(anyOf...))
 }
 
 func (b *builder) setValueType(v cue.Value) {
diff --git a/encoding/openapi/testdata/array.json b/encoding/openapi/testdata/array.json
index f4d2a32..2cf335a 100644
--- a/encoding/openapi/testdata/array.json
+++ b/encoding/openapi/testdata/array.json
@@ -10,36 +10,13 @@
                "bar": {
                   "type": "array",
                   "items": {
-                     "type": "string",
-                     "enum": [
-                        "1",
-                        "2",
-                        "3"
-                     ],
-                     "default": "1"
+                     "$ref": "#/components/schemas/MyEnum"
                   }
                },
                "foo": {
                   "type": "array",
                   "items": {
-                     "type": "object",
-                     "properties": {
-                        "a": {
-                           "type": "integer"
-                        },
-                        "e": {
-                           "type": "array",
-                           "items": {
-                              "type": "string",
-                              "enum": [
-                                 "1",
-                                 "2",
-                                 "3"
-                              ],
-                              "default": "1"
-                           }
-                        }
-                     }
+                     "$ref": "#/components/schemas/MyStruct"
                   }
                },
                "baz": {
diff --git a/encoding/openapi/testdata/oneof-funcs.json b/encoding/openapi/testdata/oneof-funcs.json
index 4e4da66..816f5b3 100644
--- a/encoding/openapi/testdata/oneof-funcs.json
+++ b/encoding/openapi/testdata/oneof-funcs.json
@@ -7,63 +7,137 @@
    "paths": {},
    "components": {
       "schemas": {
-         "MYSTRING": {
+         "T": {
             "description": "Randomly picked description from a set of size one.",
             "type": "object",
-            "oneOf": [
+            "properties": {
+               "shared": {
+                  "description": "Randomly picked description from a set of size one.",
+                  "type": "integer"
+               },
+               "shared2": {
+                  "description": "Randomly picked description from a set of size one.",
+                  "type": "integer"
+               }
+            },
+            "allOf": [
                {
                   "required": [
-                     "exact"
+                     "shared"
                   ],
-                  "properties": {
-                     "exact": {
-                        "description": "Randomly picked description from a set of size one.",
-                        "type": "string",
-                        "format": "string"
-                     }
-                  }
-               },
-               {
-                  "required": [
-                     "regex"
-                  ],
-                  "properties": {
-                     "regex": {
-                        "description": "Randomly picked description from a set of size one.",
-                        "type": "string",
-                        "format": "string"
-                     }
-                  }
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "exact"
-                           ],
-                           "properties": {
-                              "exact": {
-                                 "description": "Randomly picked description from a set of size one.",
-                                 "type": "string",
-                                 "format": "string"
+                  "oneOf": [
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "exact"
+                                 ],
+                                 "properties": {
+                                    "exact": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "string",
+                                       "format": "string"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "regex"
+                                 ],
+                                 "properties": {
+                                    "regex": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "string",
+                                       "format": "string"
+                                    }
+                                 }
                               }
-                           }
-                        },
-                        {
-                           "required": [
-                              "regex"
-                           ],
-                           "properties": {
-                              "regex": {
-                                 "description": "Randomly picked description from a set of size one.",
-                                 "type": "string",
-                                 "format": "string"
-                              }
+                           ]
+                        }
+                     },
+                     {
+                        "required": [
+                           "exact"
+                        ],
+                        "properties": {
+                           "exact": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "string",
+                              "format": "string"
                            }
                         }
-                     ]
-                  }
+                     },
+                     {
+                        "required": [
+                           "regex"
+                        ],
+                        "properties": {
+                           "regex": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "string",
+                              "format": "string"
+                           }
+                        }
+                     }
+                  ]
+               },
+               {
+                  "oneOf": [
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "count"
+                                 ],
+                                 "properties": {
+                                    "count": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "amount"
+                                 ],
+                                 "properties": {
+                                    "amount": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     },
+                     {
+                        "required": [
+                           "count"
+                        ],
+                        "properties": {
+                           "count": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "integer"
+                           }
+                        }
+                     },
+                     {
+                        "required": [
+                           "amount"
+                        ],
+                        "properties": {
+                           "amount": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "integer"
+                           }
+                        }
+                     }
+                  ],
+                  "required": [
+                     "shared2"
+                  ]
                }
             ]
          },
@@ -75,25 +149,291 @@
             "description": "Randomly picked description from a set of size one.",
             "type": "object",
             "required": [
+               "count",
                "include",
-               "exclude",
-               "count"
+               "exclude"
             ],
             "properties": {
+               "count": {
+                  "$ref": "#/components/schemas/MYINT"
+               },
                "include": {
-                  "$ref": "#/components/schemas/MYSTRING"
+                  "$ref": "#/components/schemas/T"
                },
                "exclude": {
                   "description": "Randomly picked description from a set of size one.",
                   "type": "array",
                   "items": {
-                     "$ref": "#/components/schemas/MYSTRING"
+                     "$ref": "#/components/schemas/T"
                   }
-               },
-               "count": {
-                  "$ref": "#/components/schemas/MYINT"
                }
             }
+         },
+         "INCOMPATIBLE": {
+            "description": "Randomly picked description from a set of size one.",
+            "type": "object",
+            "oneOf": [
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ],
+                        "properties": {
+                           "shared": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "integer"
+                           }
+                        }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra1"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    },
+                                    "extra1": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra2"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    },
+                                    "extra2": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra1"
+                  ],
+                  "properties": {
+                     "shared": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     },
+                     "extra1": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     }
+                  }
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra2"
+                  ],
+                  "properties": {
+                     "shared": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     },
+                     "extra2": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     }
+                  }
+               }
+            ]
+         },
+         "WITHMAP": {
+            "description": "Randomly picked description from a set of size one.",
+            "type": "object",
+            "oneOf": [
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ],
+                        "properties": {
+                           "shared": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "object",
+                              "additionalProperties": {
+                                 "description": "Randomly picked description from a set of size one.",
+                                 "type": "integer"
+                              }
+                           }
+                        }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "object",
+                                       "additionalProperties": {
+                                          "description": "Randomly picked description from a set of size one.",
+                                          "type": "integer"
+                                       }
+                                    },
+                                    "extra": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "integer"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared",
+                           "extra"
+                        ],
+                        "properties": {
+                           "shared": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "object",
+                              "additionalProperties": {
+                                 "description": "Randomly picked description from a set of size one.",
+                                 "type": "integer"
+                              }
+                           },
+                           "extra": {
+                              "description": "Randomly picked description from a set of size one.",
+                              "type": "integer"
+                           }
+                        }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "description": "Randomly picked description from a set of size one.",
+                                       "type": "object",
+                                       "additionalProperties": {
+                                          "description": "Randomly picked description from a set of size one.",
+                                          "type": "integer"
+                                       }
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra"
+                  ],
+                  "properties": {
+                     "shared": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "string",
+                        "format": "string"
+                     },
+                     "extra": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     }
+                  }
+               }
+            ]
+         },
+         "EMBED": {
+            "description": "Randomly picked description from a set of size one.",
+            "type": "object",
+            "properties": {
+               "a": {
+                  "description": "Randomly picked description from a set of size one.",
+                  "type": "integer"
+               }
+            },
+            "oneOf": [
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ],
+                           "properties": {
+                              "b": {
+                                 "description": "Randomly picked description from a set of size one.",
+                                 "type": "integer"
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "c"
+                           ],
+                           "properties": {
+                              "c": {
+                                 "description": "Randomly picked description from a set of size one.",
+                                 "type": "integer"
+                              }
+                           }
+                        }
+                     ]
+                  }
+               },
+               {
+                  "required": [
+                     "b"
+                  ],
+                  "properties": {
+                     "b": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     }
+                  }
+               },
+               {
+                  "required": [
+                     "c"
+                  ],
+                  "properties": {
+                     "c": {
+                        "description": "Randomly picked description from a set of size one.",
+                        "type": "integer"
+                     }
+                  }
+               }
+            ]
          }
       }
    }
diff --git a/encoding/openapi/testdata/oneof-resolve.json b/encoding/openapi/testdata/oneof-resolve.json
index 62158c5..c37b83a 100644
--- a/encoding/openapi/testdata/oneof-resolve.json
+++ b/encoding/openapi/testdata/oneof-resolve.json
@@ -7,9 +7,21 @@
    "paths": {},
    "components": {
       "schemas": {
-         "MyString": {
+         "T": {
             "type": "object",
             "properties": {
+               "shared": {
+                  "type": "integer"
+               },
+               "shared2": {
+                  "type": "integer"
+               },
+               "count": {
+                  "type": "integer"
+               },
+               "amount": {
+                  "type": "integer"
+               },
                "exact": {
                   "type": "string",
                   "format": "string"
@@ -19,70 +31,13 @@
                   "format": "string"
                }
             },
-            "oneOf": [
+            "allOf": [
                {
                   "required": [
-                     "exact"
-                  ]
-               },
-               {
-                  "required": [
-                     "regex"
-                  ]
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "exact"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "regex"
-                           ]
-                        }
-                     ]
-                  }
-               }
-            ]
-         },
-         "MyInt": {
-            "type": "integer"
-         },
-         "Foo": {
-            "type": "object",
-            "required": [
-               "include",
-               "exclude",
-               "count"
-            ],
-            "properties": {
-               "include": {
-                  "type": "object",
-                  "properties": {
-                     "exact": {
-                        "type": "string",
-                        "format": "string"
-                     },
-                     "regex": {
-                        "type": "string",
-                        "format": "string"
-                     }
-                  },
+                     "shared"
+                  ],
                   "oneOf": [
                      {
-                        "required": [
-                           "exact"
-                        ]
-                     },
-                     {
-                        "required": [
-                           "regex"
-                        ]
-                     },
-                     {
                         "not": {
                            "anyOf": [
                               {
@@ -97,6 +52,158 @@
                               }
                            ]
                         }
+                     },
+                     {
+                        "required": [
+                           "exact"
+                        ]
+                     },
+                     {
+                        "required": [
+                           "regex"
+                        ]
+                     }
+                  ]
+               },
+               {
+                  "oneOf": [
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "count"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "amount"
+                                 ]
+                              }
+                           ]
+                        }
+                     },
+                     {
+                        "required": [
+                           "count"
+                        ]
+                     },
+                     {
+                        "required": [
+                           "amount"
+                        ]
+                     }
+                  ],
+                  "required": [
+                     "shared2"
+                  ]
+               }
+            ]
+         },
+         "MyInt": {
+            "type": "integer"
+         },
+         "Foo": {
+            "type": "object",
+            "required": [
+               "count",
+               "include",
+               "exclude"
+            ],
+            "properties": {
+               "count": {
+                  "type": "integer"
+               },
+               "include": {
+                  "type": "object",
+                  "properties": {
+                     "shared": {
+                        "type": "integer"
+                     },
+                     "shared2": {
+                        "type": "integer"
+                     },
+                     "count": {
+                        "type": "integer"
+                     },
+                     "amount": {
+                        "type": "integer"
+                     },
+                     "exact": {
+                        "type": "string",
+                        "format": "string"
+                     },
+                     "regex": {
+                        "type": "string",
+                        "format": "string"
+                     }
+                  },
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ],
+                        "oneOf": [
+                           {
+                              "not": {
+                                 "anyOf": [
+                                    {
+                                       "required": [
+                                          "exact"
+                                       ]
+                                    },
+                                    {
+                                       "required": [
+                                          "regex"
+                                       ]
+                                    }
+                                 ]
+                              }
+                           },
+                           {
+                              "required": [
+                                 "exact"
+                              ]
+                           },
+                           {
+                              "required": [
+                                 "regex"
+                              ]
+                           }
+                        ]
+                     },
+                     {
+                        "oneOf": [
+                           {
+                              "not": {
+                                 "anyOf": [
+                                    {
+                                       "required": [
+                                          "count"
+                                       ]
+                                    },
+                                    {
+                                       "required": [
+                                          "amount"
+                                       ]
+                                    }
+                                 ]
+                              }
+                           },
+                           {
+                              "required": [
+                                 "count"
+                              ]
+                           },
+                           {
+                              "required": [
+                                 "amount"
+                              ]
+                           }
+                        ],
+                        "required": [
+                           "shared2"
+                        ]
                      }
                   ]
                },
@@ -105,6 +212,18 @@
                   "items": {
                      "type": "object",
                      "properties": {
+                        "shared": {
+                           "type": "integer"
+                        },
+                        "shared2": {
+                           "type": "integer"
+                        },
+                        "count": {
+                           "type": "integer"
+                        },
+                        "amount": {
+                           "type": "integer"
+                        },
                         "exact": {
                            "type": "string",
                            "format": "string"
@@ -114,40 +233,239 @@
                            "format": "string"
                         }
                      },
-                     "oneOf": [
+                     "allOf": [
                         {
                            "required": [
-                              "exact"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "regex"
-                           ]
-                        },
-                        {
-                           "not": {
-                              "anyOf": [
-                                 {
-                                    "required": [
-                                       "exact"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "regex"
+                              "shared"
+                           ],
+                           "oneOf": [
+                              {
+                                 "not": {
+                                    "anyOf": [
+                                       {
+                                          "required": [
+                                             "exact"
+                                          ]
+                                       },
+                                       {
+                                          "required": [
+                                             "regex"
+                                          ]
+                                       }
                                     ]
                                  }
-                              ]
-                           }
+                              },
+                              {
+                                 "required": [
+                                    "exact"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "regex"
+                                 ]
+                              }
+                           ]
+                        },
+                        {
+                           "oneOf": [
+                              {
+                                 "not": {
+                                    "anyOf": [
+                                       {
+                                          "required": [
+                                             "count"
+                                          ]
+                                       },
+                                       {
+                                          "required": [
+                                             "amount"
+                                          ]
+                                       }
+                                    ]
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "count"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "amount"
+                                 ]
+                              }
+                           ],
+                           "required": [
+                              "shared2"
+                           ]
+                        }
+                     ]
+                  }
+               }
+            }
+         },
+         "Incompatible": {
+            "type": "object",
+            "properties": {
+               "shared": {
+                  "type": "integer"
+               },
+               "extra1": {
+                  "type": "integer"
+               },
+               "extra2": {
+                  "type": "integer"
+               }
+            },
+            "oneOf": [
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra1"
+                                 ]
+                              },
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra2"
+                                 ]
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra1"
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra2"
+                  ]
+               }
+            ]
+         },
+         "WithMap": {
+            "type": "object",
+            "properties": {
+               "shared": {
+                  "type": "object",
+                  "format": "string",
+                  "additionalProperties": {
+                     "type": "integer"
+                  }
+               },
+               "extra": {
+                  "type": "integer"
+               }
+            },
+            "oneOf": [
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra"
+                                 ]
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared",
+                           "extra"
+                        ]
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared"
+                                 ]
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra"
+                  ]
+               }
+            ]
+         },
+         "Embed": {
+            "type": "object",
+            "properties": {
+               "a": {
+                  "type": "integer"
+               },
+               "b": {
+                  "type": "integer"
+               },
+               "c": {
+                  "type": "integer"
+               }
+            },
+            "oneOf": [
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ]
+                        },
+                        {
+                           "required": [
+                              "c"
+                           ]
                         }
                      ]
                   }
                },
-               "count": {
-                  "type": "integer"
+               {
+                  "required": [
+                     "b"
+                  ]
+               },
+               {
+                  "required": [
+                     "c"
+                  ]
                }
-            }
+            ]
          }
       }
    }
diff --git a/encoding/openapi/testdata/oneof.cue b/encoding/openapi/testdata/oneof.cue
index a7aad07..6fa37d4 100644
--- a/encoding/openapi/testdata/oneof.cue
+++ b/encoding/openapi/testdata/oneof.cue
@@ -1,13 +1,57 @@
-MyString :: {} | {
+T :: {
+	shared: int
+}
+T :: {} | {
 	exact: string
 } | {
 	regex: string
 }
+T :: {} | {
+	count: int
+} | {
+	amount: int
+}
+T :: {
+	shared2: int
+}
 
 MyInt :: int
 
 Foo: {
-	include: MyString
-	exclude: [...MyString]
+	include: T
+	exclude: [...T]
 	count: MyInt
 }
+
+Incompatible :: {
+	shared: int
+} | {
+	shared: int
+	extra1: int
+} | {
+	shared: int
+	extra2: int
+}
+
+WithMap :: {
+	shared: [string]: int
+} | {
+	shared: [string]: int
+	extra: int
+} | {
+	shared: string // incompatible
+	extra:  int
+}
+
+Embed :: {
+	a?: int
+
+	close({}) |
+	close({b: int}) |
+	close({c: int})
+
+	// TODO: maybe support builtin to write this as
+	// oneof({},
+	// {b: int},
+	// {c: int})
+}
diff --git a/encoding/openapi/testdata/oneof.json b/encoding/openapi/testdata/oneof.json
index 40b31b9..f7a13f0 100644
--- a/encoding/openapi/testdata/oneof.json
+++ b/encoding/openapi/testdata/oneof.json
@@ -4,58 +4,126 @@
    "paths": {},
    "components": {
       "schemas": {
-         "MyString": {
+         "T": {
             "type": "object",
-            "oneOf": [
+            "properties": {
+               "shared": {
+                  "type": "integer"
+               },
+               "shared2": {
+                  "type": "integer"
+               }
+            },
+            "allOf": [
                {
                   "required": [
-                     "exact"
+                     "shared"
                   ],
-                  "properties": {
-                     "exact": {
-                        "type": "string",
-                        "format": "string"
-                     }
-                  }
-               },
-               {
-                  "required": [
-                     "regex"
-                  ],
-                  "properties": {
-                     "regex": {
-                        "type": "string",
-                        "format": "string"
-                     }
-                  }
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "exact"
-                           ],
-                           "properties": {
-                              "exact": {
-                                 "type": "string",
-                                 "format": "string"
+                  "oneOf": [
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "exact"
+                                 ],
+                                 "properties": {
+                                    "exact": {
+                                       "type": "string",
+                                       "format": "string"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "regex"
+                                 ],
+                                 "properties": {
+                                    "regex": {
+                                       "type": "string",
+                                       "format": "string"
+                                    }
+                                 }
                               }
-                           }
-                        },
-                        {
-                           "required": [
-                              "regex"
-                           ],
-                           "properties": {
-                              "regex": {
-                                 "type": "string",
-                                 "format": "string"
-                              }
+                           ]
+                        }
+                     },
+                     {
+                        "required": [
+                           "exact"
+                        ],
+                        "properties": {
+                           "exact": {
+                              "type": "string",
+                              "format": "string"
                            }
                         }
-                     ]
-                  }
+                     },
+                     {
+                        "required": [
+                           "regex"
+                        ],
+                        "properties": {
+                           "regex": {
+                              "type": "string",
+                              "format": "string"
+                           }
+                        }
+                     }
+                  ]
+               },
+               {
+                  "oneOf": [
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "count"
+                                 ],
+                                 "properties": {
+                                    "count": {
+                                       "type": "integer"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "amount"
+                                 ],
+                                 "properties": {
+                                    "amount": {
+                                       "type": "integer"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     },
+                     {
+                        "required": [
+                           "count"
+                        ],
+                        "properties": {
+                           "count": {
+                              "type": "integer"
+                           }
+                        }
+                     },
+                     {
+                        "required": [
+                           "amount"
+                        ],
+                        "properties": {
+                           "amount": {
+                              "type": "integer"
+                           }
+                        }
+                     }
+                  ],
+                  "required": [
+                     "shared2"
+                  ]
                }
             ]
          },
@@ -65,24 +133,261 @@
          "Foo": {
             "type": "object",
             "required": [
+               "count",
                "include",
-               "exclude",
-               "count"
+               "exclude"
             ],
             "properties": {
+               "count": {
+                  "$ref": "#/components/schemas/MyInt"
+               },
                "include": {
-                  "$ref": "#/components/schemas/MyString"
+                  "$ref": "#/components/schemas/T"
                },
                "exclude": {
                   "type": "array",
                   "items": {
-                     "$ref": "#/components/schemas/MyString"
+                     "$ref": "#/components/schemas/T"
                   }
-               },
-               "count": {
-                  "$ref": "#/components/schemas/MyInt"
                }
             }
+         },
+         "Incompatible": {
+            "type": "object",
+            "oneOf": [
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ],
+                        "properties": {
+                           "shared": {
+                              "type": "integer"
+                           }
+                        }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra1"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "type": "integer"
+                                    },
+                                    "extra1": {
+                                       "type": "integer"
+                                    }
+                                 }
+                              },
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra2"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "type": "integer"
+                                    },
+                                    "extra2": {
+                                       "type": "integer"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra1"
+                  ],
+                  "properties": {
+                     "shared": {
+                        "type": "integer"
+                     },
+                     "extra1": {
+                        "type": "integer"
+                     }
+                  }
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra2"
+                  ],
+                  "properties": {
+                     "shared": {
+                        "type": "integer"
+                     },
+                     "extra2": {
+                        "type": "integer"
+                     }
+                  }
+               }
+            ]
+         },
+         "WithMap": {
+            "type": "object",
+            "oneOf": [
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared"
+                        ],
+                        "properties": {
+                           "shared": {
+                              "type": "object",
+                              "additionalProperties": {
+                                 "type": "integer"
+                              }
+                           }
+                        }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared",
+                                    "extra"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "type": "object",
+                                       "additionalProperties": {
+                                          "type": "integer"
+                                       }
+                                    },
+                                    "extra": {
+                                       "type": "integer"
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "allOf": [
+                     {
+                        "required": [
+                           "shared",
+                           "extra"
+                        ],
+                        "properties": {
+                           "shared": {
+                              "type": "object",
+                              "additionalProperties": {
+                                 "type": "integer"
+                              }
+                           },
+                           "extra": {
+                              "type": "integer"
+                           }
+                        }
+                     },
+                     {
+                        "not": {
+                           "anyOf": [
+                              {
+                                 "required": [
+                                    "shared"
+                                 ],
+                                 "properties": {
+                                    "shared": {
+                                       "type": "object",
+                                       "additionalProperties": {
+                                          "type": "integer"
+                                       }
+                                    }
+                                 }
+                              }
+                           ]
+                        }
+                     }
+                  ]
+               },
+               {
+                  "required": [
+                     "shared",
+                     "extra"
+                  ],
+                  "properties": {
+                     "shared": {
+                        "type": "string",
+                        "format": "string"
+                     },
+                     "extra": {
+                        "type": "integer"
+                     }
+                  }
+               }
+            ]
+         },
+         "Embed": {
+            "type": "object",
+            "properties": {
+               "a": {
+                  "type": "integer"
+               }
+            },
+            "oneOf": [
+               {
+                  "not": {
+                     "anyOf": [
+                        {
+                           "required": [
+                              "b"
+                           ],
+                           "properties": {
+                              "b": {
+                                 "type": "integer"
+                              }
+                           }
+                        },
+                        {
+                           "required": [
+                              "c"
+                           ],
+                           "properties": {
+                              "c": {
+                                 "type": "integer"
+                              }
+                           }
+                        }
+                     ]
+                  }
+               },
+               {
+                  "required": [
+                     "b"
+                  ],
+                  "properties": {
+                     "b": {
+                        "type": "integer"
+                     }
+                  }
+               },
+               {
+                  "required": [
+                     "c"
+                  ],
+                  "properties": {
+                     "c": {
+                        "type": "integer"
+                     }
+                  }
+               }
+            ]
          }
       }
    }
diff --git a/encoding/openapi/testdata/openapi-norefs.json b/encoding/openapi/testdata/openapi-norefs.json
index e8c5fd0..dc246b1 100644
--- a/encoding/openapi/testdata/openapi-norefs.json
+++ b/encoding/openapi/testdata/openapi-norefs.json
@@ -67,22 +67,6 @@
                   "required": [
                      "b"
                   ]
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "a"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "b"
-                           ]
-                        }
-                     ]
-                  }
                }
             ]
          },
@@ -129,22 +113,6 @@
                   "required": [
                      "b"
                   ]
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "b"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "b"
-                           ]
-                        }
-                     ]
-                  }
                }
             ]
          },
@@ -182,22 +150,6 @@
                         "required": [
                            "b"
                         ]
-                     },
-                     {
-                        "not": {
-                           "anyOf": [
-                              {
-                                 "required": [
-                                    "a"
-                                 ]
-                              },
-                              {
-                                 "required": [
-                                    "b"
-                                 ]
-                              }
-                           ]
-                        }
                      }
                   ]
                },
@@ -212,22 +164,6 @@
                         "required": [
                            "d"
                         ]
-                     },
-                     {
-                        "not": {
-                           "anyOf": [
-                              {
-                                 "required": [
-                                    "c"
-                                 ]
-                              },
-                              {
-                                 "required": [
-                                    "d"
-                                 ]
-                              }
-                           ]
-                        }
                      }
                   ]
                },
@@ -242,22 +178,6 @@
                         "required": [
                            "f"
                         ]
-                     },
-                     {
-                        "not": {
-                           "anyOf": [
-                              {
-                                 "required": [
-                                    "e"
-                                 ]
-                              },
-                              {
-                                 "required": [
-                                    "f"
-                                 ]
-                              }
-                           ]
-                        }
                      }
                   ]
                }
@@ -284,22 +204,6 @@
                   "required": [
                      "a"
                   ]
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "b"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "a"
-                           ]
-                        }
-                     ]
-                  }
                }
             ]
          },
@@ -347,23 +251,6 @@
                   "required": [
                      "port"
                   ]
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "port",
-                              "obj"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "port"
-                           ]
-                        }
-                     ]
-                  }
                }
             ]
          }
diff --git a/encoding/openapi/testdata/openapi.json b/encoding/openapi/testdata/openapi.json
index 0438a36..923fd0d 100644
--- a/encoding/openapi/testdata/openapi.json
+++ b/encoding/openapi/testdata/openapi.json
@@ -61,37 +61,6 @@
                         "format": "string"
                      }
                   }
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "a"
-                           ],
-                           "properties": {
-                              "a": {
-                                 "description": "Field a.",
-                                 "type": "integer",
-                                 "enum": [
-                                    1
-                                 ]
-                              }
-                           }
-                        },
-                        {
-                           "required": [
-                              "b"
-                           ],
-                           "properties": {
-                              "b": {
-                                 "type": "string",
-                                 "format": "string"
-                              }
-                           }
-                        }
-                     ]
-                  }
                }
             ]
          },
@@ -119,16 +88,18 @@
          },
          "YourMessage": {
             "type": "object",
+            "properties": {
+               "a": {
+                  "type": "string",
+                  "format": "string"
+               }
+            },
             "oneOf": [
                {
                   "required": [
                      "b"
                   ],
                   "properties": {
-                     "a": {
-                        "type": "string",
-                        "format": "string"
-                     },
                      "b": {
                         "type": "string",
                         "format": "string"
@@ -140,49 +111,10 @@
                      "b"
                   ],
                   "properties": {
-                     "a": {
-                        "type": "string",
-                        "format": "string"
-                     },
                      "b": {
                         "type": "number"
                      }
                   }
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "b"
-                           ],
-                           "properties": {
-                              "a": {
-                                 "type": "string",
-                                 "format": "string"
-                              },
-                              "b": {
-                                 "type": "string",
-                                 "format": "string"
-                              }
-                           }
-                        },
-                        {
-                           "required": [
-                              "b"
-                           ],
-                           "properties": {
-                              "a": {
-                                 "type": "string",
-                                 "format": "string"
-                              },
-                              "b": {
-                                 "type": "number"
-                              }
-                           }
-                        }
-                     ]
-                  }
                }
             ]
          },
@@ -210,32 +142,6 @@
                               "type": "number"
                            }
                         }
-                     },
-                     {
-                        "not": {
-                           "anyOf": [
-                              {
-                                 "required": [
-                                    "a"
-                                 ],
-                                 "properties": {
-                                    "a": {
-                                       "type": "number"
-                                    }
-                                 }
-                              },
-                              {
-                                 "required": [
-                                    "b"
-                                 ],
-                                 "properties": {
-                                    "b": {
-                                       "type": "number"
-                                    }
-                                 }
-                              }
-                           ]
-                        }
                      }
                   ]
                },
@@ -260,32 +166,6 @@
                               "type": "number"
                            }
                         }
-                     },
-                     {
-                        "not": {
-                           "anyOf": [
-                              {
-                                 "required": [
-                                    "c"
-                                 ],
-                                 "properties": {
-                                    "c": {
-                                       "type": "number"
-                                    }
-                                 }
-                              },
-                              {
-                                 "required": [
-                                    "d"
-                                 ],
-                                 "properties": {
-                                    "d": {
-                                       "type": "number"
-                                    }
-                                 }
-                              }
-                           ]
-                        }
                      }
                   ]
                },
@@ -310,32 +190,6 @@
                               "type": "number"
                            }
                         }
-                     },
-                     {
-                        "not": {
-                           "anyOf": [
-                              {
-                                 "required": [
-                                    "e"
-                                 ],
-                                 "properties": {
-                                    "e": {
-                                       "type": "number"
-                                    }
-                                 }
-                              },
-                              {
-                                 "required": [
-                                    "f"
-                                 ],
-                                 "properties": {
-                                    "f": {
-                                       "type": "number"
-                                    }
-                                 }
-                              }
-                           ]
-                        }
                      }
                   ]
                }
@@ -364,33 +218,6 @@
                         "format": "string"
                      }
                   }
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "b"
-                           ],
-                           "properties": {
-                              "b": {
-                                 "type": "number"
-                              }
-                           }
-                        },
-                        {
-                           "required": [
-                              "a"
-                           ],
-                           "properties": {
-                              "a": {
-                                 "type": "string",
-                                 "format": "string"
-                              }
-                           }
-                        }
-                     ]
-                  }
                }
             ]
          },
@@ -434,28 +261,6 @@
                         ]
                      }
                   }
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "$ref": "#/components/schemas/Port"
-                        },
-                        {
-                           "required": [
-                              "port"
-                           ],
-                           "properties": {
-                              "port": {
-                                 "type": "integer",
-                                 "enum": [
-                                    1
-                                 ]
-                              }
-                           }
-                        }
-                     ]
-                  }
                }
             ]
          }
diff --git a/encoding/openapi/testdata/structural.json b/encoding/openapi/testdata/structural.json
index ad0b83a..dab5eb7 100644
--- a/encoding/openapi/testdata/structural.json
+++ b/encoding/openapi/testdata/structural.json
@@ -110,52 +110,6 @@
                            "required": [
                               "stringMapValue"
                            ]
-                        },
-                        {
-                           "not": {
-                              "anyOf": [
-                                 {
-                                    "required": [
-                                       "stringValue"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "int64Value"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "doubleValue"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "boolValue"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "bytesValue"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "timestampValue"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "durationValue"
-                                    ]
-                                 },
-                                 {
-                                    "required": [
-                                       "stringMapValue"
-                                    ]
-                                 }
-                              ]
-                           }
                         }
                      ]
                   }
@@ -257,52 +211,6 @@
                   "required": [
                      "stringMapValue"
                   ]
-               },
-               {
-                  "not": {
-                     "anyOf": [
-                        {
-                           "required": [
-                              "stringValue"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "int64Value"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "doubleValue"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "boolValue"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "bytesValue"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "timestampValue"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "durationValue"
-                           ]
-                        },
-                        {
-                           "required": [
-                              "stringMapValue"
-                           ]
-                        }
-                     ]
-                  }
                }
             ]
          },