# This file mostly contains edge cases of disjunction semantics.
# We should ensure that all these cases can be derived from thee spec.
#
# Many of these cases were discovered as part of the development of the new
# evaluator that started at v0.7 and do not pass for that version.
-- in.cue --
cyclicalInsertInDisjunction: {
	// We do not allow lookups across unresolved disjunctions. As such, the
	// references across disjunctions are not allowed to disambiguate a
	// disjunction.
	//
	// Algorithmically, the distinction between allowing this or not is
	// achieved by allowing dereferencing of the node in which a disjunction
	// occurs to the disjunct under evaluation. Effectively this is what the
	// v0.6 evaluator does.

	ok1: {
		// In this case, unifying the comprehension into the processing of the
		// disjunctions would _not_ case the reference to cross disjunction
		// boundaries.
		c: {d: e, e: _}
		c: {d: 1, e: 2} | {d: 2, e: 2}
	}
	maybe1: {
		// In this case, the reference c.e crosses a disjunction boundary.
		// However, since the reference starts from within a disjunction, this
		// could be allowed, if we define it to be.
		c: d: c.e
		c: {d: 1, e: 2} | {d: 2, e: 2}
	}
	err1: {
		x: c.e
		c: d: x
		c: {d: 1, e: 2} | {d: 2, e: 2}
	}
	err2: {
		x: div(c.e, 2)
		c: d: x
		c: {d: 1, e: 2} | {d: 2, e: 3}
	}
	err3: {
		x: c.e
		c: d: 1 | 2 | x
		c: {d: 1, e: 2} | {d: 2, e: 3}
	}
}

preseveBottomSemantics: ok: {
	// `a: a`, which just says that `a` is `a`, is logically equivalent to
	// `a: _`. Consequently, the disjunction `a: a | X` is equivalent to
	// `a: _ | X`. As we do not simplify disjunctions, the result is `a: _ | X`.
	// However, we could consider simplifying in this case to `a: _` if we can
	// come up with a good general mechanism for subsumption.
	#x: {#x | {}}
}

eliminateOpenVariant: ok: {
	// We have two structs in the disjunction that are identical in every way
	// except openness. TODO: should we disambiguate those?
	c: {a: 1} | #b
	#b: {a: 1}
}

cyclicSelfElimination: ok: {
	// This should resolve to {y: 1} (works for new evaluator)
	y: 1 | {y: 1}
	y
}

// Value aliases must resolve to the specific disjunct arm's value,
// not the original unresolved disjunction.
issue4251_notC: {
	#NotC: x={
		string
		_must: true & x != "c"
	}

	a: #NotC
	a: "a" | "b"
	a: "a"
}
issue4251_notB: {
	#NotB: x={
		string
		_must: true & x != "b"
	}
	a: #NotB
	a: "a" | "b"
	a: "a"
}
issue4251_notB_reversed: {
	#NotB: x={
		string
		_must: true & x != "b"
	}
	a: #NotB
	a: "b" | "a"
	a: "a"
}
-- panic.cue --
// This used to cause a panic with evalv3.
panic1: {
	#TypeFoo: {
		name: "foo"
		// Make foo_field here optional and master panics too!
		foo_field: string
	}
	#TypeBar: {
		#TypeBar2
		bar_field?: string
	}

	#TypeBar2: name: "bar"

	objs: obj1: {
		type: #TypeFoo | #TypeBar
		type: *objs["missing"] | #TypeFoo | #TypeBar
	}

	out: #TypeFoo | #TypeBar
	out: objs["obj1"].type
}
-- out/compile --
--- in.cue
{
  cyclicalInsertInDisjunction: {
    ok1: {
      c: {
        d: 〈0;e〉
        e: _
      }
      c: ({
        d: 1
        e: 2
      }|{
        d: 2
        e: 2
      })
    }
    maybe1: {
      c: {
        d: 〈1;c〉.e
      }
      c: ({
        d: 1
        e: 2
      }|{
        d: 2
        e: 2
      })
    }
    err1: {
      x: 〈0;c〉.e
      c: {
        d: 〈1;x〉
      }
      c: ({
        d: 1
        e: 2
      }|{
        d: 2
        e: 2
      })
    }
    err2: {
      x: div(〈0;c〉.e, 2)
      c: {
        d: 〈1;x〉
      }
      c: ({
        d: 1
        e: 2
      }|{
        d: 2
        e: 3
      })
    }
    err3: {
      x: 〈0;c〉.e
      c: {
        d: (1|2|〈1;x〉)
      }
      c: ({
        d: 1
        e: 2
      }|{
        d: 2
        e: 3
      })
    }
  }
  preseveBottomSemantics: {
    ok: {
      #x: {
        (〈1;#x〉|{})
      }
    }
  }
  eliminateOpenVariant: {
    ok: {
      c: ({
        a: 1
      }|〈0;#b〉)
      #b: {
        a: 1
      }
    }
  }
  cyclicSelfElimination: {
    ok: {
      y: (1|{
        y: 1
      })
      〈0;y〉
    }
  }
  issue4251_notC: {
    #NotC: {
      string
      _must: (true & (〈1〉 != "c"))
    }
    a: 〈0;#NotC〉
    a: ("a"|"b")
    a: "a"
  }
  issue4251_notB: {
    #NotB: {
      string
      _must: (true & (〈1〉 != "b"))
    }
    a: 〈0;#NotB〉
    a: ("a"|"b")
    a: "a"
  }
  issue4251_notB_reversed: {
    #NotB: {
      string
      _must: (true & (〈1〉 != "b"))
    }
    a: 〈0;#NotB〉
    a: ("b"|"a")
    a: "a"
  }
}
--- panic.cue
{
  panic1: {
    #TypeFoo: {
      name: "foo"
      foo_field: string
    }
    #TypeBar: {
      〈1;#TypeBar2〉
      bar_field?: string
    }
    #TypeBar2: {
      name: "bar"
    }
    objs: {
      obj1: {
        type: (〈2;#TypeFoo〉|〈2;#TypeBar〉)
        type: (*〈2;objs〉["missing"]|〈2;#TypeFoo〉|〈2;#TypeBar〉)
      }
    }
    out: (〈0;#TypeFoo〉|〈0;#TypeBar〉)
    out: 〈0;objs〉["obj1"].type
  }
}
