A T is wrapped in Some wherever a T? is wanted, one level at a time and never unwrapped back.
This commit is contained in:
parent
1763157ef2
commit
cb60df1dc0
4
TODO.org
4
TODO.org
@ -39,6 +39,10 @@ Decided (133): =x?= is a bool; =if x?=, =elif x?=, =while x?= and the rest of an
|
||||
make a local Option its payload in the block, in place (not a copy). Assigning an Option
|
||||
to it there is refused rather than ending the narrowing; =e? as g= names what a test
|
||||
found. Rules out =if let g = x= over a plain name, which is refused toward these.
|
||||
** DONE A T is wrapped where a T? is wanted
|
||||
CLOSED: [2026-09-26]
|
||||
Decided (138), like Swift: one level per boundary, the literal built at T first; not
|
||||
inside a container. Rules out implicit unwrapping: a T? where a T is wanted stays refused.
|
||||
** TODO The stepper does not step inside an optional chain
|
||||
=Ast.step_expr= treats a =Chain= as a leaf (its catch-all), so nothing in a chain's
|
||||
body gets a step point of its own.
|
||||
|
||||
151
lib/check.ml
151
lib/check.ml
@ -2891,6 +2891,13 @@ let rec bind_ty ?(widen = false) ?(ro = true) subst (pat : Types.t)
|
||||
bind_ty ~ro:(m = Types.Const) subst p a
|
||||
| Types.Vec p, Types.Vec a
|
||||
| Types.Option p, Types.Option a -> inner p a
|
||||
(* A plain value at a [$t?] parameter binds [$t] to its own type, and
|
||||
[expect] wraps it (decision 138). At the top of an argument only, as the
|
||||
widening below: an element of a Vec is never wrapped, so [(Vec $t?)]
|
||||
meets a [(Vec i32)] as a mismatch. *)
|
||||
| Types.Option p, a
|
||||
when widen && (match a with Types.Dyn | Types.Never -> false | _ -> true) ->
|
||||
bind_ty ~widen subst p a
|
||||
| Types.Array (n, p), Types.Array (m, a) -> Int64.equal n m && inner p a
|
||||
| Types.Map (k, v), Types.Map (k', v') -> inner k k' && inner v v'
|
||||
(* Each function type against its own. *)
|
||||
@ -3227,6 +3234,10 @@ let rec literal_arith (e : Ast.expr) : int64 option =
|
||||
over literals alone. *)
|
||||
let lone_literal (e : Ast.expr) = is_literal e || literal_arith e <> None
|
||||
|
||||
(* [None] as written, which has no type until an Option is asked of it. *)
|
||||
let is_none_lit (e : Ast.expr) =
|
||||
match e.Ast.e with Ast.Var "None" -> true | _ -> false
|
||||
|
||||
(* A value whose type comes only from defaults — a literal, [nil], [(Some 3)],
|
||||
arithmetic over literals, a [do] ending in one — so it takes the type of whatever
|
||||
meets it. An arm of this kind is checked after the others, at their type,
|
||||
@ -4960,7 +4971,7 @@ let const_note ?(fln = false) env ~(want : Types.t) ~(got : Types.t) =
|
||||
(Types.spell ~indented:fln want) (Types.spell ~indented:fln got)
|
||||
| _ -> ""
|
||||
|
||||
let expect ctx loc ~want (got : Tast.expr) =
|
||||
let rec expect ctx loc ~want (got : Tast.expr) =
|
||||
match want with
|
||||
| None -> got
|
||||
| Some w ->
|
||||
@ -5033,6 +5044,21 @@ let expect ctx loc ~want (got : Tast.expr) =
|
||||
| (Types.Slice (Types.Const, _) | Types.Ptr (Types.Const, _)), _
|
||||
when Types.const_widens ~from:got.Tast.ty ~into:w ->
|
||||
{ got with Tast.ty = w }
|
||||
(* Decision 138, Swift's rule: a T where a (Option T) is wanted is
|
||||
[Some] of it. One level each time — a T? into a T?? is [Some] of the
|
||||
Option, never the Option itself — and the payload goes through this
|
||||
same boundary first, so a T into a T?? is [Some (Some t)] and an i32
|
||||
into an (Option i64) is widened, then wrapped. Never the other way:
|
||||
a T? where a T is wanted is still refused, and a dyn is left to the
|
||||
nil <-> None arms above. When the payload is refused too, the
|
||||
refusal below names the Option, as it did before. *)
|
||||
| Types.Option t, g
|
||||
when (match g with Types.Dyn | Types.Never -> false | _ -> true)
|
||||
&& not (Types.fits ~expected:w ~actual:g) ->
|
||||
(match expect ctx loc ~want:(Some t) got with
|
||||
| v when Types.fits ~expected:t ~actual:v.Tast.ty -> mk loc w (Tast.Some_ v)
|
||||
| _ -> got
|
||||
| exception Loc.Error _ -> got)
|
||||
| _ -> got
|
||||
in
|
||||
if Types.fits ~expected:w ~actual:got.Tast.ty then got
|
||||
@ -5133,8 +5159,16 @@ let arm_join (a : Types.t) (b : Types.t) =
|
||||
match Types.const_join a b with
|
||||
| Some j -> Some j
|
||||
| None ->
|
||||
(* A T beside a T? meets at the T?, the T wrapped in [Some]
|
||||
(decision 138). Only the plain side moves, and by one level. *)
|
||||
let wraps p u =
|
||||
(match u with Types.Option _ | Types.Unit -> false | _ -> true)
|
||||
&& (Types.equal p u || Types.widens_to ~from:u ~into:p)
|
||||
in
|
||||
(match a, b with
|
||||
| Types.Dyn, _ | _, Types.Dyn -> Some Types.Dyn
|
||||
| Types.Option p, u when wraps p u -> Some a
|
||||
| u, Types.Option p when wraps p u -> Some b
|
||||
| _ -> None)
|
||||
(* The type two untyped literals meet at: the wider of their own types, and
|
||||
an integer beside a float at the float — [(if c 1 2.5)] is an f32, though
|
||||
@ -6095,6 +6129,18 @@ and check_value ctx ?want (e : Ast.expr) : Tast.expr =
|
||||
let used = ctx.used || List.memq e ctx.kept in
|
||||
ctx.used <- false;
|
||||
match e.Ast.e with
|
||||
(* A literal where an (Option T) is wanted is built at T and then wrapped
|
||||
(decision 138): [s = -1] over an [i64?] is [Some] of an i64 -1. It has
|
||||
no type until one is asked of it, so it is asked the payload's, rather
|
||||
than being built at a default and wrapped at the wrong width. *)
|
||||
| Ast.Int _ | Ast.UInt _ | Ast.Float _ | Ast.Byte _ | Ast.Call _ | Ast.Arr (_ :: _)
|
||||
when (match want with Some (Types.Option _) -> true | _ -> false)
|
||||
&& (lone_literal e || (match e.Ast.e with Ast.Arr _ -> true | _ -> false)) ->
|
||||
let w = Option.get want in
|
||||
let t = match w with Types.Option t -> t | _ -> assert false in
|
||||
let v = check ctx ~want:t e in
|
||||
if Types.fits ~expected:t ~actual:v.Tast.ty then mk loc w (Tast.Some_ v)
|
||||
else expect ctx loc ~want v
|
||||
(* A negative literal in a generic body, at an instantiation that made it
|
||||
unsigned. The cast the ordinary refusal names would be wrong at every
|
||||
other type the function is called at, so the fix is one that needs no
|
||||
@ -8571,6 +8617,30 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
expect ctx loc ~want (mk loc Types.Dyn (Tast.If (c, t, e)))
|
||||
| ty ->
|
||||
let oty = Types.Option ty in
|
||||
(* A later arm that is a T? where this one is a T: the arms meet at
|
||||
T? (decision 138), so the chain is a T??, as it is when the T?
|
||||
arm comes first. Tried only where the arm is refused at T. *)
|
||||
let nested =
|
||||
match want, ty with
|
||||
| None, (Types.Option _ | Types.Unit) -> None
|
||||
| None, _ ->
|
||||
(match trial ctx (fun () -> rest ~used:true ~want:oty ()) with
|
||||
| Ok e -> Some (Error e)
|
||||
| Error _ ->
|
||||
let ooty = Types.Option oty in
|
||||
(match trial ctx (fun () -> rest ~used:true ~want:ooty ()) with
|
||||
| Ok e -> Some (Ok e)
|
||||
| Error _ -> None))
|
||||
| _ -> None
|
||||
in
|
||||
match nested with
|
||||
| Some (Ok e) ->
|
||||
let ooty = Types.Option oty in
|
||||
let t = mk loc ooty (Tast.Some_ (mk loc oty (Tast.Some_ t))) in
|
||||
mk loc ooty (Tast.If (c, t, e))
|
||||
| Some (Error e) ->
|
||||
mk loc oty (Tast.If (c, mk loc oty (Tast.Some_ t), e))
|
||||
| None ->
|
||||
let e = rest ~used:true ~want:oty () in
|
||||
expect ctx loc ~want
|
||||
(mk loc oty (Tast.If (c, mk loc oty (Tast.Some_ t), e))))
|
||||
@ -8585,14 +8655,30 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
let t = branch ctx (fun () -> in_tail (fun () -> check ctx ?want t)) in
|
||||
let e = branch ctx (fun () -> in_tail (fun () -> check ctx ?want e)) in
|
||||
mk loc t.Tast.ty (Tast.If (c, t, e))
|
||||
| Some e when want = None && adapts t && not (adapts e)
|
||||
| Some e when want = None && (adapts t || is_none_lit t)
|
||||
&& not (adapts e || is_none_lit e)
|
||||
&& not (and_sentinel e) ->
|
||||
(* A literal has no type of its own until something asks, so with no
|
||||
expectation the other arm decides: [(if c 4000000 n)] over an i64 [n]
|
||||
is an i64, as [(+ 4000000 n)] is. *)
|
||||
let e = branch ctx (fun () -> in_tail (fun () -> check ctx e)) in
|
||||
let twant = if e.Tast.ty = Types.Never then None else Some e.Tast.ty in
|
||||
let t = branch ctx (fun () -> in_tail (fun () -> check ctx ?want:twant t)) in
|
||||
let then_at w = branch ctx (fun () -> in_tail (fun () -> check ctx ?want:w t)) in
|
||||
(* [None] or [Some(1)] beside a plain T: the two meet at T?, the other
|
||||
arm wrapped (decision 138). Tried only once the arm is refused at T,
|
||||
so an arm that fits T is never an Option. *)
|
||||
let t, e =
|
||||
match e.Tast.ty with
|
||||
| Types.Option _ | Types.Dyn | Types.Unit | Types.Never -> then_at twant, e
|
||||
| ety ->
|
||||
(match trial ctx (fun () -> then_at twant) with
|
||||
| Ok t -> t, e
|
||||
| Error _ ->
|
||||
let oty = Types.Option ety in
|
||||
(match trial ctx (fun () -> then_at (Some oty)) with
|
||||
| Ok t -> t, expect ctx e.Tast.loc ~want:(Some oty) e
|
||||
| Error _ -> then_at twant, e))
|
||||
in
|
||||
let ty = if e.Tast.ty = Types.Never then t.Tast.ty else e.Tast.ty in
|
||||
mk loc ty (Tast.If (c, t, e))
|
||||
| Some e ->
|
||||
@ -8656,7 +8742,24 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
| Some j -> Some (j, expect ctx v.Tast.loc ~want:(Some j) v)
|
||||
| None -> None
|
||||
in
|
||||
match at_then () with
|
||||
(* The else arm refused at T and fine at T? — [None], [Some(1)] —
|
||||
and the two meet at T?, the then arm wrapped (decision 138). *)
|
||||
let at_option () =
|
||||
match t.Tast.ty with
|
||||
| Types.Option _ | Types.Dyn | Types.Unit -> None
|
||||
| ty ->
|
||||
let oty = Types.Option ty in
|
||||
(match
|
||||
trial ctx (fun () ->
|
||||
branch ctx (fun () -> in_tail (fun () -> check ctx ~want:oty e)))
|
||||
with
|
||||
| Ok v when Types.equal v.Tast.ty oty -> Some (oty, v)
|
||||
| _ -> None)
|
||||
in
|
||||
(* Only where the arms met nowhere else, so nothing that met before
|
||||
meets differently: a dyn else arm still meets at dyn. *)
|
||||
match
|
||||
(match at_then () with
|
||||
| Ok v ->
|
||||
(match opened_dyn ~box:(to_dyn ctx) v with
|
||||
| Some box -> Some (Types.Dyn, box)
|
||||
@ -8683,7 +8786,10 @@ and check_if_once ctx ~tail ~used ?want loc c t e =
|
||||
| Some r -> Some r
|
||||
| None ->
|
||||
Some (t.Tast.ty, expect ctx v.Tast.loc ~want:(Some t.Tast.ty) v))
|
||||
| Error _ -> None)
|
||||
| Error _ -> None))
|
||||
with
|
||||
| None -> at_option ()
|
||||
| j -> j
|
||||
in
|
||||
match joined with
|
||||
| Some (j, v) ->
|
||||
@ -9731,6 +9837,14 @@ and check_the ctx ~want loc (t : Ast.texpr) (v : Ast.expr) =
|
||||
(* A keyword naming one of an enum's members is that member at the
|
||||
enum's type, not a dyn: [let d: Dir = :north]. *)
|
||||
&& not (match ty, v.Ast.e with Types.Enum _, Ast.Kw _ -> true | _ -> false)
|
||||
(* An array literal that is a dyn vector only because its elements do
|
||||
not agree among themselves — [[1, None]] — is built at the annotation
|
||||
when every element fits it, as [x: [2 i32?] = [1, None]] (decision
|
||||
138). Nothing is converted: the literal is built at T. *)
|
||||
&& not (match v.Ast.e, ty with
|
||||
| Ast.Arr _, (Types.Array (_, Types.Option _) | Types.Slice (_, Types.Option _)) ->
|
||||
probe ctx loc (fun () -> ignore (check ctx ~want:ty v)) <> None
|
||||
| _ -> false)
|
||||
then begin
|
||||
let tn = tyname loc ty in
|
||||
let numeric = match ty with Types.Int _ | Types.Float _ -> true | _ -> false in
|
||||
@ -10319,6 +10433,19 @@ and check_match ctx ?(tail = false) ?(used = false) ?(stmt = false) ?(opt = fals
|
||||
(head, (ctx.scope, ctx.ret), w, d);
|
||||
Error d)
|
||||
in
|
||||
(* Where it would be refused: an arm fine at T? — [None],
|
||||
[Some(1)] — meets a T join at T? (decision 138), and
|
||||
[arm_join] wraps the arms before it. *)
|
||||
let refused () =
|
||||
let fallback () = at !want () in
|
||||
match w with
|
||||
| Types.Option _ | Types.Dyn | Types.Unit -> fallback ()
|
||||
| _ ->
|
||||
let oty = Types.Option w in
|
||||
(match trial ctx (at (Some oty)) with
|
||||
| Ok b when Types.equal b.Tast.ty oty -> b
|
||||
| _ -> fallback ())
|
||||
in
|
||||
match at_join () with
|
||||
| Ok b ->
|
||||
(match opened_dyn ~box:(to_dyn ctx) b with
|
||||
@ -10334,10 +10461,10 @@ and check_match ctx ?(tail = false) ?(used = false) ?(stmt = false) ?(opt = fals
|
||||
with
|
||||
| Ok b -> b
|
||||
| Error own when String.equal own.Loc.kind not_kept ->
|
||||
at !want ()
|
||||
refused ()
|
||||
| Error own when is_mismatch d && not (is_mismatch own) ->
|
||||
at None ()
|
||||
| Error _ -> at !want ())
|
||||
| Error _ -> refused ())
|
||||
else block ctx ?want:!want a.Ast.aloc a.Ast.body
|
||||
in
|
||||
let body =
|
||||
@ -16610,6 +16737,12 @@ and generic_call ctx ~want loc name vars pats pret args =
|
||||
call with no type variables in it. *)
|
||||
| _ ->
|
||||
(match subst_ty !subst pat, a.Tast.ty with
|
||||
(* A plain value at a [$t?] parameter, which [bind_ty] bound
|
||||
through the Option: wrapped now that $t is known (decision
|
||||
138). *)
|
||||
| Types.Option _ as o, at
|
||||
when (match at with Types.Option _ | Types.Dyn | Types.Never -> false | _ -> true) ->
|
||||
expect ctx a.Tast.loc ~want:(Some o) a
|
||||
| Types.Fn (ps, r), Types.CFn (ps', r') ->
|
||||
if Types.equal (Types.Fn (ps, r)) (Types.Fn (ps', r')) then
|
||||
mk a.Tast.loc (Types.Fn (ps, r))
|
||||
@ -17565,7 +17698,9 @@ let builtins : (string * string * string) list =
|
||||
(* Option *)
|
||||
("Some", "Some [T] (Option T)",
|
||||
"Wraps a value as a present Option. None is the other half, and is \
|
||||
written as a name rather than as a call.");
|
||||
written as a name rather than as a call. Where an (Option T) is \
|
||||
expected a T is wrapped with no Some written, one level at a time; an \
|
||||
Option is never unwrapped that way.");
|
||||
|
||||
(* the host primitives *)
|
||||
("bytes", "bytes [str Allocator?] [u8]",
|
||||
|
||||
@ -217,6 +217,15 @@ Each item: the proposal, then the reason in one line.
|
||||
holds — `a?.f(x)`, `a?[i]`, `a?.b.c`. A result that is already an Option
|
||||
is not wrapped again, so `a?.b?.c` is one Option. A rest with no value
|
||||
makes the whole a statement. `~o1` is a fresh name no reader produces.
|
||||
- A `T` where a `T?` is wanted is `Some` of it (decision 138): an
|
||||
assignment, a `let` with a type, an argument, a return, a struct field, an
|
||||
array or `Vec` element, and an `if` or `match` arm beside an Option arm.
|
||||
A literal is built at `T` first, so `s = -1` over an `i64?` is `Some(-1)`
|
||||
at i64. One level at a time: a `T` into a `T??` is `Some(Some(t))`, a `T?`
|
||||
into a `T??` is `Some` of it. A `$t` meeting `$u?` binds `$u` to `T`.
|
||||
Never inside a container (`Vec(i32)` is not a `Vec(i32?)`), and never the
|
||||
other way: a `T?` where a `T` is wanted still needs `!`, `??`, `x?` or
|
||||
`as`. A kept chain whose arms are a `T` and a `T?` is a `T??`. **Built.**
|
||||
- **Casts and type-taking builtins are calls:** `i32(x)`, `vec-new(u8)`,
|
||||
`max-value(u8)`, `the([3 f32], [1 2 3.5])`. A pointer cast is the type
|
||||
called: `Ptr(Color)(p)` reads `((Ptr Color) p)`. **Built.**
|
||||
|
||||
85
test/programs/autowrap.fln
Normal file
85
test/programs/autowrap.fln
Normal file
@ -0,0 +1,85 @@
|
||||
;; A T where a T? is wanted is Some of it (decision 138): each position,
|
||||
;; a literal built at the payload's type, nested Options, generics, and the
|
||||
;; arms of an if, a match and a kept chain.
|
||||
|
||||
struct P
|
||||
a: i64?
|
||||
b: i32?
|
||||
|
||||
fn show(o: i32?) -> i32 = o ?? -9
|
||||
|
||||
fn back(b: bool, x: i32) -> i32?
|
||||
if b
|
||||
return x
|
||||
None
|
||||
|
||||
fn pick(b: bool, x: i32) -> i32?
|
||||
if b then x else None
|
||||
|
||||
fn pick2(b: bool, x: i32) -> i32?
|
||||
if b then None else x
|
||||
|
||||
fn two(o: Option(i32?)) -> str
|
||||
match o
|
||||
Some(i) -> if i? then "some some" else "some none"
|
||||
None -> "none"
|
||||
|
||||
fn first(o: $u?) -> $u = o!
|
||||
|
||||
fn wrap(x: $t) -> $t? = x
|
||||
|
||||
fn chain(a: bool, b: bool, opt: i32?) -> Option(i32?)
|
||||
if a
|
||||
1
|
||||
elif b
|
||||
opt
|
||||
|
||||
fn main()
|
||||
;; assignment, and a literal at the payload's width
|
||||
let s: i64? = None
|
||||
s = -1
|
||||
println(s ?? 0)
|
||||
;; a let with an annotation, from a literal, a name and arithmetic
|
||||
let x: i32 = 4
|
||||
let a: i32? = x + 1
|
||||
let w: i64? = x
|
||||
let f: f64? = 2
|
||||
println(a ?? 0, w ?? 0, f ?? 0.0)
|
||||
;; an argument and a return value
|
||||
println(show(7), back(true, 8) ?? -1, back(false, 8) ?? -1)
|
||||
;; a struct field
|
||||
let p = P{.a 3 .b x}
|
||||
println(p.a ?? 0, p.b ?? 0)
|
||||
;; an array and a Vec element
|
||||
let xs: [3 i32?] = [1, None, x]
|
||||
println(xs[0] ?? 0, xs[1] ?? 0, xs[2] ?? 0)
|
||||
let v: Vec(i32?) = vec-new(i32?)
|
||||
push(v, 6)
|
||||
push(v, None)
|
||||
println(length(v), v[0] ?? 0, v[1] ?? 0)
|
||||
;; the arms of an if and a match
|
||||
println(pick(true, 2) ?? -1, pick(false, 2) ?? -1, pick2(true, 2) ?? -1, pick2(false, 2) ?? -1)
|
||||
let m = match x
|
||||
4 -> x
|
||||
_ -> None
|
||||
println(m ?? 0)
|
||||
;; nested: a T into a T?? is Some(Some(t)), a T? is Some of it
|
||||
let nn: Option(i32?) = 5
|
||||
let none: i32? = None
|
||||
let nn2: Option(i32?) = none
|
||||
println(two(nn), two(nn2))
|
||||
;; a kept chain: a T arm beside a T? arm makes the chain a T??
|
||||
println(two(chain(true, false, none)), two(chain(false, true, none)), two(chain(false, false, none)))
|
||||
let kk =
|
||||
if x > 9
|
||||
none
|
||||
elif x > 1
|
||||
1
|
||||
println(two(kk))
|
||||
;; generics
|
||||
println(first(9), first(Some(8)), wrap(3) ?? 0)
|
||||
;; a narrowed name still takes a payload value
|
||||
let o: i32? = Some(1)
|
||||
if o?
|
||||
o = 10
|
||||
println(o)
|
||||
@ -2274,6 +2274,10 @@ let () =
|
||||
outputs ~opt:"-O0" (path ^ ", -O0") ("programs/" ^ path) want;
|
||||
outputs ~x86:true (path ^ ", --x86") ("programs/" ^ path) want)
|
||||
[ ("optionals.fln", optionals_out); ("optionals-dyn.fln", optionals_dyn_out);
|
||||
(* A T where a T? is wanted is Some of it (decision 138). *)
|
||||
("autowrap.fln",
|
||||
"-1\n5 4 2\n7 8 -1\n3 4\n1 0 4\n2 6 0\n2 -1 -1 2\n4\nsome some some none\n\
|
||||
some some some none none\nsome some\n9 8 3\n10\n");
|
||||
(* x? tests and narrows, e? as g names what it found (decision 133). *)
|
||||
("presence.fln", "true false true\n6\n-1\n3\n101 209 0\n11\n42\n2\nabsent\n6\nfalse true\n3\n6\n15\n"); ("presence-dyn.fln", "true false\n103 209 0\nno pet\nann\n3 2\n") ];
|
||||
(* x! over nothing traps at its site and names the expression. *)
|
||||
|
||||
@ -8402,6 +8402,23 @@ let () =
|
||||
parse_rejects "_ in a defgeneric's return slot"
|
||||
~needle:"defgeneric's methods each have their own"
|
||||
"(defgeneric area [s] _)";
|
||||
(* Decision 138: a T is wrapped where a T? is wanted, and never the other
|
||||
way. The program half is programs/autowrap.fln. *)
|
||||
accepts "a T is Some of it at a T?" "(defn f [] (Option i64) -1)\n(defn main [] ())";
|
||||
rejects_check "a T? is not unwrapped at a T" ~needle:"expected i32, found (Option i32)"
|
||||
"(defn f [o (Option i32)] i32 o)\n(defn main [] ())";
|
||||
rejects_check "a T? argument is not unwrapped" ~needle:"expected i32, found (Option i32)"
|
||||
"(defn g [x i32] i32 x)\n(defn f [o (Option i32)] i32 (g o))\n(defn main [] ())";
|
||||
rejects_check "a payload that does not fit is refused at the Option"
|
||||
~needle:"expected (Option i32), found str"
|
||||
"(defn f [] (Option i32) \"no\")\n(defn main [] ())";
|
||||
rejects_check "a narrowing is not wrapped" ~needle:"expected (Option i32), found i64"
|
||||
"(defn f [x i64] (Option i32) x)\n(defn main [] ())";
|
||||
rejects_check "no wrap inside a container" ~needle:"expected (Vec (Option i32)), found (Vec i32)"
|
||||
"(defn f [v (Vec i32)] (Vec (Option i32)) v)\n(defn main [] ())";
|
||||
rejects_check "a narrowed name still refuses an Option"
|
||||
~needle:"it cannot be given an Option here"
|
||||
"(defn main [] () (let [o (the (Option i32) (Some 1))] (when (? o) (set o (Some 2)))))";
|
||||
(* A plain name binds what an Option or a dyn holds; over anything else
|
||||
it cannot fail, and is refused toward let. The program half is
|
||||
programs/if-let.flan and programs/optionals.fln. *)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user