A union is not a missing struct either
layout searched only Tast.structs, so a declared union came back as "no struct is named X" — which reads as "that type does not exist" about a type the checker knows. Refused by kind beside the enum, and both refusals now have a test: a new enum and a new union, evaluated into the session.
This commit is contained in:
parent
404b8559c5
commit
41025fc0ac
4
BUILT.md
4
BUILT.md
@ -1042,7 +1042,9 @@ suffix would reintroduce the ambiguity the rule exists to remove, and a rule wit
|
||||
rely on. The refusal carries `:candidates`, so a person is one copy-paste from the answer and a client has its
|
||||
completion list — the same shape as `package_of` refusing a directory imported under two aliases rather than picking
|
||||
one. An enum is refused by *kind* (`X is an enum, not a struct`): its members are erased to `i32` before `Tast.program`
|
||||
exists, which is the same fact that makes a `defenum` unreloadable.
|
||||
exists, which is the same fact that makes a `defenum` unreloadable. A union is refused the same way and for its own
|
||||
reason — it is declared, and union *values* are milestone 6. Both are `Types.Named` at a use site, so falling through
|
||||
to "no struct is named X" would say a type does not exist about one that plainly does.
|
||||
|
||||
**`render.ml` is not reused, and that is not a second walk.** It walks a *value* and emits the code that prints it;
|
||||
this describes a *type* and emits text. What is shared is the spelling: field types go through `Types.to_string`, which
|
||||
|
||||
12
lib/dev.ml
12
lib/dev.ml
@ -529,12 +529,18 @@ let layout t ~ty =
|
||||
Wire.quote (Types.to_string f.Tast.fty) ])
|
||||
s.Tast.fields) ]
|
||||
| None ->
|
||||
(* An enum is a type the checker knows and this op cannot describe: its
|
||||
(* Two types the checker knows and this op cannot describe. An enum's
|
||||
members are erased to i32 before [Tast.program] exists, which is the
|
||||
same fact that makes a defenum unreloadable. Saying which kind it is
|
||||
beats "no such type" for a name that plainly exists. *)
|
||||
same fact that makes a defenum unreloadable; a union is declared and
|
||||
has no values yet. Either way, saying which kind it is beats "no such
|
||||
type" for a name that plainly exists. *)
|
||||
if Hashtbl.mem t.session.Session.env.Check.enums ty then
|
||||
error (ty ^ " is an enum, not a struct; its members are erased to i32")
|
||||
else if
|
||||
List.exists (fun (u : Tast.union) -> String.equal u.Tast.uname ty)
|
||||
t.session.Session.program.Tast.unions
|
||||
then
|
||||
error (ty ^ " is a union, not a struct; union values are milestone 6")
|
||||
else
|
||||
let suffix = "/" ^ ty in
|
||||
let candidates =
|
||||
|
||||
@ -174,6 +174,44 @@ let () =
|
||||
let r = request c "(:op \"layout\" :type \"Nonesuch\")" in
|
||||
if status r <> "error" then fail "a type that does not exist got a layout";
|
||||
|
||||
(* A name that plainly exists and is not a struct is refused by *kind*.
|
||||
Both of these are types the checker knows and this op cannot
|
||||
describe, and "no struct is named X" would read as "X does not
|
||||
exist". *)
|
||||
let refusal r =
|
||||
Option.value ~default:(status r) (Wire.string_field r "message")
|
||||
in
|
||||
let contains hay needle =
|
||||
let n = String.length needle in
|
||||
let rec go i =
|
||||
i + n <= String.length hay
|
||||
&& (String.equal (String.sub hay i n) needle || go (i + 1))
|
||||
in
|
||||
go 0
|
||||
in
|
||||
let r =
|
||||
request c
|
||||
"(:op \"eval\" :code \"(defenum Colour [red 0 green 1])\" :file \"/tmp/buf.flan\")"
|
||||
in
|
||||
if status r <> "ok" then fail "a new enum: %s" (refusal r)
|
||||
else begin
|
||||
let r = request c "(:op \"layout\" :type \"Colour\")" in
|
||||
if status r <> "error" then fail "an enum answered a struct layout"
|
||||
else if not (contains (refusal r) "is an enum") then
|
||||
fail "an enum is refused as: %s" (refusal r)
|
||||
end;
|
||||
let r =
|
||||
request c
|
||||
"(:op \"eval\" :code \"(defunion Shape [(Circle [r f32])])\" :file \"/tmp/buf.flan\")"
|
||||
in
|
||||
if status r <> "ok" then fail "a new union: %s" (refusal r)
|
||||
else begin
|
||||
let r = request c "(:op \"layout\" :type \"Shape\")" in
|
||||
if status r <> "error" then fail "a union answered a struct layout"
|
||||
else if not (contains (refusal r) "is a union") then
|
||||
fail "a union is refused as: %s" (refusal r)
|
||||
end;
|
||||
|
||||
(* The identity rule, and the case NEXT.md named: a second [Blob] typed
|
||||
into a package is [agent/Blob], the qualified name resolves, and the
|
||||
bare one is refused with the names it could have meant rather than
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user