TODO: a bound value resolving to a disjunction should probably be an error. In this case #Size.amx should resolve. -- in.cue -- // Range disjunction without cycle (checks only one-way). #Size: { res: uint | *0 min: >res | *(1 + res) max: >min | *min } s0: #Size & {res: 1} // This discards the default for max. This is correct, but unfortunate. // TODO: is there a tweak to the default mechanism possible that would fix that? // Tread very carefully, though! Perhaps we could have a builtin that // discards any default, so that we can at least manually override this // behavior. s1: #Size & {min: 5} s2: #Size & {max: 5} s3: #Size & { min: 5 max: 10 } es3: #Size & { min: 10 max: 5 } // Disjunctions with cycles // TODO: improve error message here. Logic is correct, though. #nonEmptyRange: { min: *1 | int min: min } r1: #nonEmptyRange & { min: 3 } r2: #nonEmptyRange & { max: 5 } r3: #nonEmptyRange & { min: 3 max: 6 } er3: #nonEmptyRange & { min: 5 max: 5 } -- out/eval/stats -- Leaks: 0 Freed: 98 Reused: 91 Allocs: 7 Retain: 12 Unifications: 40 Conjuncts: 154 Disjuncts: 101 -- out/eval -- Errors: er3.min: 2 errors in empty disjunction: er3.min: conflicting values 1 and 5: ./in.cue:28:8 ./in.cue:43:6 ./in.cue:44:7 es3.max: 3 errors in empty disjunction: es3.max: conflicting values 1 and 5: ./in.cue:4:16 ./in.cue:5:15 ./in.cue:20:6 ./in.cue:22:7 es3.max: conflicting values 10 and 5: ./in.cue:5:15 ./in.cue:20:6 ./in.cue:21:7 ./in.cue:22:7 es3.max: invalid value 5 (out of bound >10): ./in.cue:5:7 ./in.cue:22:7 er3.min: invalid value 5 (out of bound <5): ./in.cue:29:7 ./in.cue:44:7 Result: (_|_){ // [eval] #Size: (#struct){ res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) } min: (number){ |(*(int){ 1 }, (number){ >0 }) } max: (number){ |(*(int){ 1 }, (number){ >0 }, (number){ >1 }) } } s0: (#struct){ res: (int){ 1 } min: (number){ |(*(int){ 2 }, (number){ >1 }) } max: (number){ |(*(int){ 2 }, (number){ >1 }, (number){ >2 }) } } s1: (#struct){ res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) } min: (int){ 5 } max: (number){ |(*(int){ 5 }, (number){ >5 }) } } s2: (#struct){ res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) } min: (number){ |(*(int){ 1 }, (number){ >0 }) } max: (int){ 5 } } s3: (#struct){ res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) } min: (int){ 5 } max: (int){ 10 } } es3: (_|_){ // [eval] res: (int){ |(*(int){ 0 }, (int){ &(>=0, int) }) } min: (int){ 10 } max: (_|_){ // [eval] es3.max: 3 errors in empty disjunction: // es3.max: conflicting values 1 and 5: // ./in.cue:4:16 // ./in.cue:5:15 // ./in.cue:20:6 // ./in.cue:22:7 // es3.max: conflicting values 10 and 5: // ./in.cue:5:15 // ./in.cue:20:6 // ./in.cue:21:7 // ./in.cue:22:7 // es3.max: invalid value 5 (out of bound >10): // ./in.cue:5:7 // ./in.cue:22:7 } } #nonEmptyRange: (#struct){ min: (_|_){ // [cycle] cycle error } max: (number){ >1 } } r1: (#struct){ min: (int){ 3 } max: (number){ >3 } } r2: (#struct){ min: (int){ |(*(int){ 1 }, (int){ &(<5, int) }) } max: (int){ 5 } } r3: (#struct){ min: (int){ 3 } max: (int){ 6 } } er3: (_|_){ // [eval] min: (_|_){ // [eval] er3.min: 2 errors in empty disjunction: // er3.min: conflicting values 1 and 5: // ./in.cue:28:8 // ./in.cue:43:6 // ./in.cue:44:7 // er3.min: invalid value 5 (out of bound <5): // ./in.cue:29:7 // ./in.cue:44:7 } max: (_|_){ // [eval] er3.min: 2 errors in empty disjunction: // er3.min: conflicting values 1 and 5: // ./in.cue:28:8 // ./in.cue:43:6 // ./in.cue:44:7 // er3.min: invalid value 5 (out of bound <5): // ./in.cue:29:7 // ./in.cue:44:7 } } } -- out/compile -- --- in.cue { #Size: { res: (&(int, >=0)|*0) min: (>〈0;res〉|*(1 + 〈0;res〉)) max: (>〈0;min〉|*〈0;min〉) } s0: (〈0;#Size〉 & { res: 1 }) s1: (〈0;#Size〉 & { min: 5 }) s2: (〈0;#Size〉 & { max: 5 }) s3: (〈0;#Size〉 & { min: 5 max: 10 }) es3: (〈0;#Size〉 & { min: 10 max: 5 }) #nonEmptyRange: { min: (*1|int) min: <〈0;max〉 max: >〈0;min〉 } r1: (〈0;#nonEmptyRange〉 & { min: 3 }) r2: (〈0;#nonEmptyRange〉 & { max: 5 }) r3: (〈0;#nonEmptyRange〉 & { min: 3 max: 6 }) er3: (〈0;#nonEmptyRange〉 & { min: 5 max: 5 }) }