# Tests the explicitopen experiment: allows using the spread operator (...) to
# explicitly open closed structs. NewClosed1: {#A} is closed; NewOpen1: {#A...}
# is open. The test covers interactions between old and new style closedness.
-- cue.mod/module.cue --
module: "mod.test"

language: version: "v0.15.0"

-- old.cue --
package foo

#A: a: 1
Old1: {
	{
		#A
		b: 1
	}
}

#Old2: {
	{
		#A
		b: 1
	}
}

useNewClosed1: {
	t1: err: NewClosed1 & {c: 3}
	t2: ok: {NewClosed1, c: 3}
}

useNewClosed2: {
	t1: err: #NewClosed2 & {c: 3}
	t2: ok: {#NewClosed2, c: 3}
}

useNewOpen1: {
	t1: ok: NewOpen1 & {c: 3}
	t2: ok: {NewOpen1, c: 3}
}

useNewOpen2: {
	t1: ok: NewOpen2 & {c: 3}
	t2: ok: {NewOpen2, c: 3}
}

combineold: {
	// all {a: 1}
	a0: ok: #A & NewClosed1
	a1: ok: {#A, NewClosed1}

	a2: ok: NewClosed1 & NewOpen2
	a3: ok: {NewClosed1, NewOpen2}

	// all {a: 1, b: 2}
	b0: ok: Old1 & #NewClosed2
	b1: ok: #Old2 & #NewClosed2

	c1: ok: {#NewClosed2, Old1}
	c2: ok: {#NewClosed2, #Old2}

	// {a: 1} and {a: 1, b: 2}
	e0: err: #A & Old1
	e1: err: #A & #Old2
	e2: err: #A & #NewClosed2
	e3: err: #A & NewOpen1

	e4: ok: {#A, Old1}
	e5: ok: {#A, #Old2}
	e6: ok: {#A, #NewClosed2}
	e7: ok: {#A, NewOpen1}

	f0: err: NewClosed1 & Old1
	f1: err: NewClosed1 & #Old2
	f2: err: NewClosed1 & #NewClosed2
	f3: err: NewClosed1 & NewOpen1

	f4: ok: {NewClosed1, Old1}
	f5: ok: {NewClosed1, #Old2}
	f6: ok: {NewClosed1, NewClosed1}
	f7: ok: {NewClosed1, #NewClosed2}
	f8: ok: {NewClosed1, NewOpen1}
}
-- new.cue --
@experiment(explicitopen)

package foo

NewClosed1: {
	#A
}

#NewClosed2: {
	#A...
	b: 1
}

NewOpen1: {
	#A...
	b: 1
}

NewOpen2: #A...

useOld1: {
	t1: err: Old1 & {c: 3}
	t2: err: {Old1, c: 3} // TODO
	t3: ok: {Old1..., c: 3}
}

useOld2: {
	t1: err: #Old2 & {c: 3}
	t2: err: {#Old2, c: 3}
	t3: ok: {#Old2..., c: 3}
}

combinenew: {
	// all {a: 1}
	a0: ok: #A & NewClosed1
	a1: ok: {#A, NewClosed1}

	a2: ok: NewClosed1 & NewOpen2
	a3: ok: {NewClosed1, NewOpen2}
	a4: ok: {NewClosed1..., NewOpen2}

	a5: ok: NewOpen2 & NewClosed1
	a6: ok: {NewOpen2, NewClosed1}
	a7: ok: {NewOpen2..., NewClosed1}

	// all {a: 1, b: 2}
	b0: ok: Old1 & #NewClosed2
	b1: ok: #Old2 & #NewClosed2
	b2: ok: #NewClosed2 & Old1
	b3: ok: #NewClosed2 & #Old2

	c1: ok: {#NewClosed2, Old1}
	c2: ok: {#NewClosed2, #Old2}
	c3: ok: {Old1, #NewClosed2}
	c4: ok: {#Old2, #NewClosed2}

	d0: ok: {#NewClosed2, Old1...}
	d1: ok: {#NewClosed2, #Old2...}
	d2: ok: {#NewClosed2..., Old1}
	d3: ok: {#NewClosed2..., #Old2}
	d4: ok: {#NewClosed2..., Old1...}
	d5: ok: {#NewClosed2..., #Old2...}

	d6: ok: {Old1..., #NewClosed2}
	d7: ok: {#Old2..., #NewClosed2}
	d8: ok: {Old1, #NewClosed2...}
	d9: ok: {#Old2, #NewClosed2...}
	d10: ok: {Old1..., #NewClosed2...}
	d11: ok: {#Old2..., #NewClosed2...}

	// {a: 1} and {a: 1, b: 2}
	e0: err: #A & Old1
	e1: err: #A & #Old2
	e2: err: #A & #NewClosed2
	e3: err: #A & NewOpen1

	e4: err: {#A, Old1}
	e5: err: {#A, #Old2}
	e6: err: {#A, #NewClosed2}
	e7: err: {#A, NewOpen1}

	e8: ok: {#A..., Old1}
	e9: ok: {#A..., #Old2}
	e10: ok: {#A..., #NewClosed2}
	e11: ok: {#A..., NewOpen1}

	f0: err: NewClosed1 & Old1
	f1: err: NewClosed1 & #Old2
	f2: err: NewClosed1 & #NewClosed2
	f3: err: NewClosed1 & NewOpen1
	f4: ok:  NewClosed1 & NewClosed1

	f5: err: {NewClosed1, Old1}
	f6: err: {NewClosed1, #Old2}
	f7: err: {NewClosed1, #NewClosed2}
	f8: err: {NewClosed1, NewOpen1}
	f9: ok: {NewClosed1, NewClosed1}

	f9: ok: {NewClosed1..., Old1}
	f10: ok: {NewClosed1..., #Old2}
	f11: ok: {NewClosed1..., NewClosed1}
	f12: ok: {NewClosed1..., #NewClosed2}
	f13: ok: {NewClosed1..., NewOpen1}

	f14: err: Old1 & NewClosed1
	f15: err: #Old2 & NewClosed1
	f16: err: #NewClosed2 & NewClosed1
	f17: err: NewOpen1 & NewClosed1
	f18: ok:  NewClosed1 & NewClosed1
}
-- issues.cue --
@experiment(explicitopen)

// explicitopen experiment: postfix `...` spread cannot directly combine with
// `&`; tests open-struct spread behavior and non-struct operand tolerance.
// https://cuelang.org/issue/4150
issue4150: {
	#Def: a: int

	x: #Def... & {
		a: 42
		b: 42
	}

	foo: {a: int}
	bar: {b: int}
	y: foo... & bar...

	// NOTE: we allow this, even though it doesn't mean anything. The benefit
	// of this is that code doesn't become brittle using the spread operator
	// for references. Consider this example:
	//
	//  #Def: a: int
	//	foo: 1 | #Def
	//	bar: foo...
	//
	// Not allowing ... on any expression would mean #foo... may or may not be
	// erroneous, depending on the value foo is set to.
	z: 1...
}
-- out/compile --
--- issues.cue
{
  issue4150: {
    #Def: {
      a: int
    }
    x: (〈0;#Def〉... & {
      a: 42
      b: 42
    })
    foo: {
      a: int
    }
    bar: {
      b: int
    }
    y: (〈0;foo〉... & 〈0;bar〉...)
    z: 1...
  }
}
--- new.cue
{
  NewClosed1: {
    〈1;#A〉
  }
  #NewClosed2: {
    〈1;#A〉...
    b: 1
  }
  NewOpen1: {
    〈1;#A〉...
    b: 1
  }
  NewOpen2: 〈0;#A〉...
  useOld1: {
    t1: {
      err: (〈2;Old1〉 & {
        c: 3
      })
    }
    t2: {
      err: {
        〈3;Old1〉
        c: 3
      }
    }
    t3: {
      ok: {
        〈3;Old1〉...
        c: 3
      }
    }
  }
  useOld2: {
    t1: {
      err: (〈2;#Old2〉 & {
        c: 3
      })
    }
    t2: {
      err: {
        〈3;#Old2〉
        c: 3
      }
    }
    t3: {
      ok: {
        〈3;#Old2〉...
        c: 3
      }
    }
  }
  combinenew: {
    a0: {
      ok: (〈2;#A〉 & 〈2;NewClosed1〉)
    }
    a1: {
      ok: {
        〈3;#A〉
        〈3;NewClosed1〉
      }
    }
    a2: {
      ok: (〈2;NewClosed1〉 & 〈2;NewOpen2〉)
    }
    a3: {
      ok: {
        〈3;NewClosed1〉
        〈3;NewOpen2〉
      }
    }
    a4: {
      ok: {
        〈3;NewClosed1〉...
        〈3;NewOpen2〉
      }
    }
    a5: {
      ok: (〈2;NewOpen2〉 & 〈2;NewClosed1〉)
    }
    a6: {
      ok: {
        〈3;NewOpen2〉
        〈3;NewClosed1〉
      }
    }
    a7: {
      ok: {
        〈3;NewOpen2〉...
        〈3;NewClosed1〉
      }
    }
    b0: {
      ok: (〈2;Old1〉 & 〈2;#NewClosed2〉)
    }
    b1: {
      ok: (〈2;#Old2〉 & 〈2;#NewClosed2〉)
    }
    b2: {
      ok: (〈2;#NewClosed2〉 & 〈2;Old1〉)
    }
    b3: {
      ok: (〈2;#NewClosed2〉 & 〈2;#Old2〉)
    }
    c1: {
      ok: {
        〈3;#NewClosed2〉
        〈3;Old1〉
      }
    }
    c2: {
      ok: {
        〈3;#NewClosed2〉
        〈3;#Old2〉
      }
    }
    c3: {
      ok: {
        〈3;Old1〉
        〈3;#NewClosed2〉
      }
    }
    c4: {
      ok: {
        〈3;#Old2〉
        〈3;#NewClosed2〉
      }
    }
    d0: {
      ok: {
        〈3;#NewClosed2〉
        〈3;Old1〉...
      }
    }
    d1: {
      ok: {
        〈3;#NewClosed2〉
        〈3;#Old2〉...
      }
    }
    d2: {
      ok: {
        〈3;#NewClosed2〉...
        〈3;Old1〉
      }
    }
    d3: {
      ok: {
        〈3;#NewClosed2〉...
        〈3;#Old2〉
      }
    }
    d4: {
      ok: {
        〈3;#NewClosed2〉...
        〈3;Old1〉...
      }
    }
    d5: {
      ok: {
        〈3;#NewClosed2〉...
        〈3;#Old2〉...
      }
    }
    d6: {
      ok: {
        〈3;Old1〉...
        〈3;#NewClosed2〉
      }
    }
    d7: {
      ok: {
        〈3;#Old2〉...
        〈3;#NewClosed2〉
      }
    }
    d8: {
      ok: {
        〈3;Old1〉
        〈3;#NewClosed2〉...
      }
    }
    d9: {
      ok: {
        〈3;#Old2〉
        〈3;#NewClosed2〉...
      }
    }
    d10: {
      ok: {
        〈3;Old1〉...
        〈3;#NewClosed2〉...
      }
    }
    d11: {
      ok: {
        〈3;#Old2〉...
        〈3;#NewClosed2〉...
      }
    }
    e0: {
      err: (〈2;#A〉 & 〈2;Old1〉)
    }
    e1: {
      err: (〈2;#A〉 & 〈2;#Old2〉)
    }
    e2: {
      err: (〈2;#A〉 & 〈2;#NewClosed2〉)
    }
    e3: {
      err: (〈2;#A〉 & 〈2;NewOpen1〉)
    }
    e4: {
      err: {
        〈3;#A〉
        〈3;Old1〉
      }
    }
    e5: {
      err: {
        〈3;#A〉
        〈3;#Old2〉
      }
    }
    e6: {
      err: {
        〈3;#A〉
        〈3;#NewClosed2〉
      }
    }
    e7: {
      err: {
        〈3;#A〉
        〈3;NewOpen1〉
      }
    }
    e8: {
      ok: {
        〈3;#A〉...
        〈3;Old1〉
      }
    }
    e9: {
      ok: {
        〈3;#A〉...
        〈3;#Old2〉
      }
    }
    e10: {
      ok: {
        〈3;#A〉...
        〈3;#NewClosed2〉
      }
    }
    e11: {
      ok: {
        〈3;#A〉...
        〈3;NewOpen1〉
      }
    }
    f0: {
      err: (〈2;NewClosed1〉 & 〈2;Old1〉)
    }
    f1: {
      err: (〈2;NewClosed1〉 & 〈2;#Old2〉)
    }
    f2: {
      err: (〈2;NewClosed1〉 & 〈2;#NewClosed2〉)
    }
    f3: {
      err: (〈2;NewClosed1〉 & 〈2;NewOpen1〉)
    }
    f4: {
      ok: (〈2;NewClosed1〉 & 〈2;NewClosed1〉)
    }
    f5: {
      err: {
        〈3;NewClosed1〉
        〈3;Old1〉
      }
    }
    f6: {
      err: {
        〈3;NewClosed1〉
        〈3;#Old2〉
      }
    }
    f7: {
      err: {
        〈3;NewClosed1〉
        〈3;#NewClosed2〉
      }
    }
    f8: {
      err: {
        〈3;NewClosed1〉
        〈3;NewOpen1〉
      }
    }
    f9: {
      ok: {
        〈3;NewClosed1〉
        〈3;NewClosed1〉
      }
    }
    f9: {
      ok: {
        〈3;NewClosed1〉...
        〈3;Old1〉
      }
    }
    f10: {
      ok: {
        〈3;NewClosed1〉...
        〈3;#Old2〉
      }
    }
    f11: {
      ok: {
        〈3;NewClosed1〉...
        〈3;NewClosed1〉
      }
    }
    f12: {
      ok: {
        〈3;NewClosed1〉...
        〈3;#NewClosed2〉
      }
    }
    f13: {
      ok: {
        〈3;NewClosed1〉...
        〈3;NewOpen1〉
      }
    }
    f14: {
      err: (〈2;Old1〉 & 〈2;NewClosed1〉)
    }
    f15: {
      err: (〈2;#Old2〉 & 〈2;NewClosed1〉)
    }
    f16: {
      err: (〈2;#NewClosed2〉 & 〈2;NewClosed1〉)
    }
    f17: {
      err: (〈2;NewOpen1〉 & 〈2;NewClosed1〉)
    }
    f18: {
      ok: (〈2;NewClosed1〉 & 〈2;NewClosed1〉)
    }
  }
}
--- old.cue
{
  #A: {
    a: 1
  }
  Old1: {
    {
      〈2;#A〉
      b: 1
    }
  }
  #Old2: {
    {
      〈2;#A〉
      b: 1
    }
  }
  useNewClosed1: {
    t1: {
      err: (〈2;NewClosed1〉 & {
        c: 3
      })
    }
    t2: {
      ok: {
        〈3;NewClosed1〉
        c: 3
      }
    }
  }
  useNewClosed2: {
    t1: {
      err: (〈2;#NewClosed2〉 & {
        c: 3
      })
    }
    t2: {
      ok: {
        〈3;#NewClosed2〉
        c: 3
      }
    }
  }
  useNewOpen1: {
    t1: {
      ok: (〈2;NewOpen1〉 & {
        c: 3
      })
    }
    t2: {
      ok: {
        〈3;NewOpen1〉
        c: 3
      }
    }
  }
  useNewOpen2: {
    t1: {
      ok: (〈2;NewOpen2〉 & {
        c: 3
      })
    }
    t2: {
      ok: {
        〈3;NewOpen2〉
        c: 3
      }
    }
  }
  combineold: {
    a0: {
      ok: (〈2;#A〉 & 〈2;NewClosed1〉)
    }
    a1: {
      ok: {
        〈3;#A〉
        〈3;NewClosed1〉
      }
    }
    a2: {
      ok: (〈2;NewClosed1〉 & 〈2;NewOpen2〉)
    }
    a3: {
      ok: {
        〈3;NewClosed1〉
        〈3;NewOpen2〉
      }
    }
    b0: {
      ok: (〈2;Old1〉 & 〈2;#NewClosed2〉)
    }
    b1: {
      ok: (〈2;#Old2〉 & 〈2;#NewClosed2〉)
    }
    c1: {
      ok: {
        〈3;#NewClosed2〉
        〈3;Old1〉
      }
    }
    c2: {
      ok: {
        〈3;#NewClosed2〉
        〈3;#Old2〉
      }
    }
    e0: {
      err: (〈2;#A〉 & 〈2;Old1〉)
    }
    e1: {
      err: (〈2;#A〉 & 〈2;#Old2〉)
    }
    e2: {
      err: (〈2;#A〉 & 〈2;#NewClosed2〉)
    }
    e3: {
      err: (〈2;#A〉 & 〈2;NewOpen1〉)
    }
    e4: {
      ok: {
        〈3;#A〉
        〈3;Old1〉
      }
    }
    e5: {
      ok: {
        〈3;#A〉
        〈3;#Old2〉
      }
    }
    e6: {
      ok: {
        〈3;#A〉
        〈3;#NewClosed2〉
      }
    }
    e7: {
      ok: {
        〈3;#A〉
        〈3;NewOpen1〉
      }
    }
    f0: {
      err: (〈2;NewClosed1〉 & 〈2;Old1〉)
    }
    f1: {
      err: (〈2;NewClosed1〉 & 〈2;#Old2〉)
    }
    f2: {
      err: (〈2;NewClosed1〉 & 〈2;#NewClosed2〉)
    }
    f3: {
      err: (〈2;NewClosed1〉 & 〈2;NewOpen1〉)
    }
    f4: {
      ok: {
        〈3;NewClosed1〉
        〈3;Old1〉
      }
    }
    f5: {
      ok: {
        〈3;NewClosed1〉
        〈3;#Old2〉
      }
    }
    f6: {
      ok: {
        〈3;NewClosed1〉
        〈3;NewClosed1〉
      }
    }
    f7: {
      ok: {
        〈3;NewClosed1〉
        〈3;#NewClosed2〉
      }
    }
    f8: {
      ok: {
        〈3;NewClosed1〉
        〈3;NewOpen1〉
      }
    }
  }
}
