Braces are no longer a type: (Map K V) is the only spelling
The author's decision, and it removes the one syntax question generics
had. A return type can no longer be written in braces, so a {...} after
the signature is unambiguously the constraint map and there is no
structural rule to explain.
The reasons for the record: the brace's value meaning and its type
meaning do not correspond the way the bracket's do - [1 2 3] is a value
whose type is [3 i32], but {.x 1} is a value whose type is a name, and a
map value is built by map-new with no braces anywhere - and dropping it
reserves {} in type position for anonymous struct types.
Braces in a type are refused with the surviving spelling named rather
than falling through to "expected a type". Types.to_string and
Cimport's source printer both print (Map K V) now, and Shim refuses the
application spelling where it used to refuse only Ast.Tmap.
This commit is contained in:
parent
de93ffc89e
commit
70af1966a2
@ -392,7 +392,8 @@ let rec ty_source (t : Ast.texpr) =
|
|||||||
| Ast.Tslice e -> Printf.sprintf "[%s]" (ty_source e)
|
| Ast.Tslice e -> Printf.sprintf "[%s]" (ty_source e)
|
||||||
| Ast.Tarray (Ast.Lint n, e) -> Printf.sprintf "[%Ld %s]" n (ty_source e)
|
| Ast.Tarray (Ast.Lint n, e) -> Printf.sprintf "[%Ld %s]" n (ty_source e)
|
||||||
| Ast.Tarray (Ast.Lname n, e) -> Printf.sprintf "[%s %s]" n (ty_source e)
|
| Ast.Tarray (Ast.Lname n, e) -> Printf.sprintf "[%s %s]" n (ty_source e)
|
||||||
| Ast.Tmap (k, v) -> Printf.sprintf "{%s %s}" (ty_source k) (ty_source v)
|
| Ast.Tmap (k, v) ->
|
||||||
|
Printf.sprintf "(Map %s %s)" (ty_source k) (ty_source v)
|
||||||
| Ast.Tfn (ps, r) ->
|
| Ast.Tfn (ps, r) ->
|
||||||
Printf.sprintf "(Fn [%s] %s)"
|
Printf.sprintf "(Fn [%s] %s)"
|
||||||
(String.concat " " (List.map ty_source ps)) (ty_source r)
|
(String.concat " " (List.map ty_source ps)) (ty_source r)
|
||||||
|
|||||||
47
lib/parse.ml
47
lib/parse.ml
@ -65,8 +65,26 @@ let rec texpr (f : Form.t) : Ast.texpr =
|
|||||||
| Vec [ n; elem ] -> mk (Ast.Tarray (len n, texpr elem))
|
| Vec [ n; elem ] -> mk (Ast.Tarray (len n, texpr elem))
|
||||||
| Vec _ ->
|
| Vec _ ->
|
||||||
fail f "a type in brackets is [T] for a slice or [n T] for a fixed array"
|
fail f "a type in brackets is [T] for a slice or [n T] for a fixed array"
|
||||||
| Map [ k; v ] -> mk (Ast.Tmap (texpr k, texpr v))
|
(* Braces are not a type. [{K V}] used to spell [(Map K V)] and the two
|
||||||
| Map _ -> fail f "a map type is {K V}"
|
resolved to the same thing; the brace spelling is withdrawn, and the
|
||||||
|
refusal names the surviving one rather than letting the form fall through
|
||||||
|
to "expected a type".
|
||||||
|
|
||||||
|
Two reasons, and the second is the one that decided it. The brace's value
|
||||||
|
meaning and its type meaning do not correspond the way the bracket's do:
|
||||||
|
[[1 2 3]] is a value whose type is [[3 i32]], but [{.x 1 .y 0}] is a
|
||||||
|
value whose type is a *name*, and a map value is built by [map-new] with
|
||||||
|
no braces anywhere. And dropping it reserves [{}] in type position for
|
||||||
|
anonymous struct types, [{.x f32 .y f32}], which is a likelier thing to
|
||||||
|
want than a second spelling of a type that already has one.
|
||||||
|
|
||||||
|
It also settles the one syntax question generics had: a defn's constraint
|
||||||
|
map, [{:where (ordered? $t)}], sits immediately after the return type,
|
||||||
|
and with braces gone from type position there is nothing for it to be
|
||||||
|
confused with. *)
|
||||||
|
| Map _ ->
|
||||||
|
fail f "a map type is written (Map K V), not in braces — braces in type \
|
||||||
|
position are not a type"
|
||||||
| List ({ v = Sym "Fn"; _ } :: rest) ->
|
| List ({ v = Sym "Fn"; _ } :: rest) ->
|
||||||
(match rest with
|
(match rest with
|
||||||
| [ { v = Vec params; _ }; ret ] ->
|
| [ { v = Vec params; _ }; ret ] ->
|
||||||
@ -99,21 +117,18 @@ let rec fields (f : Form.t) (items : Form.t list) : Ast.field list =
|
|||||||
rather than a bare keyword: it leaves room for further keys without new
|
rather than a bare keyword: it leaves room for further keys without new
|
||||||
syntax.
|
syntax.
|
||||||
|
|
||||||
**The disambiguation, since it is the one syntax question the feature had
|
**The one syntax question it had, and how it stopped being one.** [{K V}]
|
||||||
to settle.** [{K V}] is a legal *return type* spelling for [(Map K V)], so
|
used to be a legal *return type* spelling for [(Map K V)], which put two
|
||||||
[(defn f [xs [$t]] {string i32} {:where ...} body)] puts two braces in a
|
braces in a row meaning different things — [(defn f [xs [$t]] {string i32}
|
||||||
row meaning different things. They are told apart structurally, by the
|
{:where ...} body)]. The brace spelling has since been withdrawn from type
|
||||||
first form inside: a constraint map leads with a *keyword*, and a map type
|
position entirely ([texpr] above), so the slot after the return type can be
|
||||||
leads with a type — [{string i32}], [{K V}] — and a keyword is not a type
|
nothing but this. A bare [{}] in *expression* position is already refused
|
||||||
anywhere in the language. So [Map ({v = Kw _} :: _)] in the slot after the
|
([expr] below), so there is nothing for it to be confused with on the other
|
||||||
return type is a constraint map and nothing else can be. The return-type
|
side either.
|
||||||
slot itself is never ambiguous: [Parse] takes it unconditionally, before
|
|
||||||
this is consulted. [{K V}] stays exactly as it was — whether it survives is
|
|
||||||
a separate open question, and this feature does not force it.
|
|
||||||
|
|
||||||
A bare [{}] in *expression* position is already refused ([expr] below), so
|
The leading keyword is still required and still checked, because it is what
|
||||||
there is also nothing for a constraint map to be confused with once past
|
tells a constraint map from a struct literal's field list, [{.x 1}], which
|
||||||
the return type. *)
|
is what braces mean in the position a body starts in. *)
|
||||||
let constraints (body : Form.t list) : Ast.pred list * Form.t list =
|
let constraints (body : Form.t list) : Ast.pred list * Form.t list =
|
||||||
match body with
|
match body with
|
||||||
| ({ Form.v = Form.Map (({ Form.v = Form.Kw _; _ } :: _ as kvs)); _ } as m)
|
| ({ Form.v = Form.Map (({ Form.v = Form.Kw _; _ } :: _ as kvs)); _ } as m)
|
||||||
|
|||||||
@ -222,7 +222,11 @@ let rec cty env ~needed ~loc ~what (t : Ast.texpr) : string =
|
|||||||
"%s is a fixed array, which C passes as a pointer and Flan as a value — \
|
"%s is a fixed array, which C passes as a pointer and Flan as a value — \
|
||||||
declare (Ptr T) and say which"
|
declare (Ptr T) and say which"
|
||||||
what
|
what
|
||||||
| Ast.Tmap _ -> fail loc "%s is a map, which has no C representation" what
|
(* Two spellings reach the same type: [Ast.Tmap], which only [Cimport]
|
||||||
|
builds now, and [(Map K V)], which is what source writes since the brace
|
||||||
|
spelling was withdrawn from type position. Both are refused here. *)
|
||||||
|
| Ast.Tmap _ | Ast.Tapp ("Map", _) ->
|
||||||
|
fail loc "%s is a map, which has no C representation" what
|
||||||
(* A Vec owns its storage, so handing its header to C hands out an owner and
|
(* A Vec owns its storage, so handing its header to C hands out an owner and
|
||||||
there is no rule for what C would then be allowed to do with it. The
|
there is no rule for what C would then be allowed to do with it. The
|
||||||
elements cross the way any other run of elements does. *)
|
elements cross the way any other run of elements does. *)
|
||||||
|
|||||||
@ -133,7 +133,7 @@ let rec to_string = function
|
|||||||
| Named n | Enum n -> n
|
| Named n | Enum n -> n
|
||||||
| Slice t -> "[" ^ to_string t ^ "]"
|
| Slice t -> "[" ^ to_string t ^ "]"
|
||||||
| Array (n, t) -> Printf.sprintf "[%Ld %s]" n (to_string t)
|
| Array (n, t) -> Printf.sprintf "[%Ld %s]" n (to_string t)
|
||||||
| Map (k, v) -> Printf.sprintf "{%s %s}" (to_string k) (to_string v)
|
| Map (k, v) -> Printf.sprintf "(Map %s %s)" (to_string k) (to_string v)
|
||||||
| Ptr t -> "(Ptr " ^ to_string t ^ ")"
|
| Ptr t -> "(Ptr " ^ to_string t ^ ")"
|
||||||
| Alloc -> "Allocator"
|
| Alloc -> "Allocator"
|
||||||
| Vec t -> "(Vec " ^ to_string t ^ ")"
|
| Vec t -> "(Vec " ^ to_string t ^ ")"
|
||||||
|
|||||||
@ -18,13 +18,17 @@
|
|||||||
;; [4 f32] fixed array — a value, copies on assignment
|
;; [4 f32] fixed array — a value, copies on assignment
|
||||||
;; [f32] slice, ptr+len — a NON-OWNING view, copies the view only
|
;; [f32] slice, ptr+len — a NON-OWNING view, copies the view only
|
||||||
;; (Vec f32) owning growable, ptr+len+cap — MOVE-ONLY, carries allocator
|
;; (Vec f32) owning growable, ptr+len+cap — MOVE-ONLY, carries allocator
|
||||||
;; {string i32} owning hashmap — move-only, shorthand for (Map string i32)
|
;; (Map string i32) owning hashmap — move-only
|
||||||
;;
|
;;
|
||||||
;; Braces are read by position: in a TYPE position {K V} is a map type; in a
|
;; Braces are NOT a type. {K V} used to be a second spelling of (Map K V) and
|
||||||
;; VALUE position {.field v ...} is a struct or condition literal — a field
|
;; was withdrawn: the brace's value and type meanings do not correspond the way
|
||||||
;; label is a dot, and the colon is left for keys. There is no map literal yet;
|
;; the bracket's do, and {} in type position is wanted for anonymous struct
|
||||||
;; a map is built with make-map and an allocator, and when a literal arrives it
|
;; types, {.x f32 .y f32}. In a VALUE position {.field v ...} is a struct or
|
||||||
;; takes {:key value}, which is why the dot is what struct construction uses.
|
;; condition literal — a field label is a dot, and the colon is left for keys.
|
||||||
|
;; There is no map literal yet; a map is built with map-new and an allocator,
|
||||||
|
;; and when a literal arrives it takes {:key value}, which is why the dot is
|
||||||
|
;; what struct construction uses. A defn's constraint map, {:where (ordered?
|
||||||
|
;; $t)}, is the other brace form, and it sits after the return type.
|
||||||
;; (Ptr World) pointer
|
;; (Ptr World) pointer
|
||||||
;; (Fn [f32] bool) function pointer, no captured environment
|
;; (Fn [f32] bool) function pointer, no captured environment
|
||||||
;; (Option a) union from the stdlib
|
;; (Option a) union from the stdlib
|
||||||
|
|||||||
@ -1844,7 +1844,7 @@ ERR@7 unexpected token: not the kind the caller was reading
|
|||||||
"(declare-c takes [xs [4 f32]] \"Takes\")"
|
"(declare-c takes [xs [4 f32]] \"Takes\")"
|
||||||
"which C passes as a pointer and Flan as a value";
|
"which C passes as a pointer and Flan as a value";
|
||||||
shim_refuses "declare-c: a map"
|
shim_refuses "declare-c: a map"
|
||||||
"(declare-c takes [m {string i32}] \"Takes\")"
|
"(declare-c takes [m (Map string i32)] \"Takes\")"
|
||||||
"which has no C representation";
|
"which has no C representation";
|
||||||
shim_refuses "declare-c: a returned string"
|
shim_refuses "declare-c: a returned string"
|
||||||
"(declare-c name [] string \"Name\")"
|
"(declare-c name [] string \"Name\")"
|
||||||
|
|||||||
@ -412,8 +412,10 @@ let () =
|
|||||||
| _ -> check "nested array with named lengths" false);
|
| _ -> check "nested array with named lengths" false);
|
||||||
(match ty "(Ptr Cursor)" with
|
(match ty "(Ptr Cursor)" with
|
||||||
| Tapp ("Ptr", [ _ ]) -> () | _ -> check "(Ptr T)" false);
|
| Tapp ("Ptr", [ _ ]) -> () | _ -> check "(Ptr T)" false);
|
||||||
(match ty "{string i32}" with
|
(* A map type is an application like (Ptr T) and (Vec T) now that the brace
|
||||||
| Tmap (_, _) -> () | _ -> check "{K V} is a map type" false);
|
spelling is gone: [Ast.Tmap] survives only as what [Cimport] builds. *)
|
||||||
|
(match ty "(Map string i32)" with
|
||||||
|
| Tapp ("Map", [ _; _ ]) -> () | _ -> check "(Map K V) is a map type" false);
|
||||||
(match ty "(Fn [a a] bool)" with
|
(match ty "(Fn [a a] bool)" with
|
||||||
| Tfn ([ _; _ ], _) -> () | _ -> check "(Fn [T] R)" false);
|
| Tfn ([ _; _ ], _) -> () | _ -> check "(Fn [T] R)" false);
|
||||||
|
|
||||||
@ -858,11 +860,11 @@ let () =
|
|||||||
back as generics. *)
|
back as generics. *)
|
||||||
rejects_check "Vec takes one type" "(defn f [x (Vec i32 i32)] ())"
|
rejects_check "Vec takes one type" "(defn f [x (Vec i32 i32)] ())"
|
||||||
~needle:"exactly one type";
|
~needle:"exactly one type";
|
||||||
(* {K V} resolves now — it is the Map type spelling, and the only one, since
|
(* (Map K V) is the map type spelling, and now the only one: the brace form
|
||||||
a bare map form in expression position is a struct literal's field list.
|
is withdrawn from type position, so braces there are refused with the
|
||||||
What is still refused is the arity, for the same reason Vec's is: a
|
surviving spelling named. What is refused here is the arity, for the same
|
||||||
near-miss would otherwise resolve to a type variable and come back as
|
reason Vec's is: a near-miss would otherwise resolve to a type variable
|
||||||
generics. *)
|
and come back as generics. *)
|
||||||
rejects_check "Map takes two types" "(defn f [x (Map i32)] ())"
|
rejects_check "Map takes two types" "(defn f [x (Map i32)] ())"
|
||||||
~needle:"exactly two types";
|
~needle:"exactly two types";
|
||||||
rejects_check "Result is milestone 6" "(defn f [] (Result i32 i32) None)"
|
rejects_check "Result is milestone 6" "(defn f [] (Result i32 i32) None)"
|
||||||
@ -2118,21 +2120,22 @@ let () =
|
|||||||
| [] -> check "a report has a first line" false)
|
| [] -> check "a report has a first line" false)
|
||||||
| None -> check "a report needs a diagnostic" false);
|
| None -> check "a report needs a diagnostic" false);
|
||||||
|
|
||||||
(* ── Generics: the syntax, the predicates, and the two defaults ──
|
(* ── Generics: the syntax, the predicates, and the two defaults ── *)
|
||||||
The syntax question the feature had to settle first: [{K V}] is a legal
|
|
||||||
*return type*, so a defn with a map return type and a constraint map puts
|
(* The one syntax question the feature had, and how it stopped being one.
|
||||||
two braces in a row meaning different things. They are told apart
|
[{K V}] used to be a legal *return type* spelling for (Map K V), so a
|
||||||
structurally, by the first form inside — a constraint map leads with a
|
defn with a map return type and a constraint map put two braces in a row
|
||||||
keyword and a map type leads with a type — so [{K V}] did not have to go
|
meaning different things. The brace spelling is now withdrawn from type
|
||||||
and is still exactly what it was. *)
|
position entirely, so the slot after the return type can be nothing but
|
||||||
accepts "a map return type is still a map return type"
|
the constraint map, and braces in a type say where the spelling went. *)
|
||||||
"(defn f [] {string i32} (map-new string i32))";
|
accepts "a map return type, written the one way there is"
|
||||||
|
"(defn f [] (Map string i32) (map-new string i32))";
|
||||||
accepts "a map return type followed by a constraint map"
|
accepts "a map return type followed by a constraint map"
|
||||||
"(defn f [x $t] {string i32} {:where (copyable? $t)} \
|
"(defn f [x $t] (Map string i32) {:where (copyable? $t)} \
|
||||||
(do x (map-new string i32)))";
|
(do x (map-new string i32)))";
|
||||||
rejects_check "a map return type is not read as a constraint map"
|
rejects_check "braces in type position say where the spelling went"
|
||||||
~needle:"is not a type variable of f"
|
~needle:"written (Map K V)"
|
||||||
"(defn f [] {string i32} {:where (ordered? $t)} (map-new string i32))";
|
"(defn f [] {string i32} (map-new string i32))";
|
||||||
|
|
||||||
(* The predicates, and each one gating the operator it is for. *)
|
(* The predicates, and each one gating the operator it is for. *)
|
||||||
accepts "ordered? admits <"
|
accepts "ordered? admits <"
|
||||||
@ -2194,9 +2197,9 @@ let () =
|
|||||||
they are chosen from the concrete type, which does not exist yet. *)
|
they are chosen from the concrete type, which does not exist yet. *)
|
||||||
rejects_check "a map keyed by a type variable that is not hashable?"
|
rejects_check "a map keyed by a type variable that is not hashable?"
|
||||||
~needle:"is not a map key"
|
~needle:"is not a map key"
|
||||||
"(defn f [m {$t i32}] i32 {:where (copyable? $t)} (len m))";
|
"(defn f [m (Map $t i32)] i32 {:where (copyable? $t)} (len m))";
|
||||||
accepts "and hashable? is what says it is"
|
accepts "and hashable? is what says it is"
|
||||||
"(defn f [m {$t i32}] i32 {:where (hashable? $t)} (len m))";
|
"(defn f [m (Map $t i32)] i32 {:where (hashable? $t)} (len m))";
|
||||||
|
|
||||||
(* ── The acceptance program checks end to end ──────────────────── *)
|
(* ── The acceptance program checks end to end ──────────────────── *)
|
||||||
accepts "calc-me.flan type checks"
|
accepts "calc-me.flan type checks"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user