The review's fixes: a suggestion that does not compile, and a confident wrong guess
F1 was the blocker and it was the worst kind of fault this pass can have: the condition message told the reader to write (not= x 0), and not= does not exist — the operator is !=. Applying the compiler's own advice got 'unknown function not= — did you mean not?'. Both branches say != now, and all three — the named form, the float zero, and the unnamed one — were checked by compiling the sentence the compiler prints. F5: a typo of a declared capitalised name got the generics lecture. (Piont 1 2) with Point declared was told that a capitalised name given type arguments is milestone 5 work, which is a confident answer about a feature nobody was reaching for. The did-you-mean runs first and, for a capitalised head only, asks the type tables as well; the generics sentence is left for a head that resembles nothing. F2: flan_dyn_cast_kind had the site live and passed NULL on the trapping path — the one entry point on this side that had a location and threw it away. The acceptance row now pins the prefix it prints. F3: the case-typo row used (data ...), which is not a top-level form, so it refused as an unknown top-level form and the needle 'unknown' matched that rather than the rule. Rewritten with defdata, and as a pair: a capitalised head gets no accessor advice, a lowercase one does. Both halves were checked to fail when perturbed. F4: an end-to-end pin for the headline. programs/dyn-trap-site.flan is compiled, run, and its stderr read for the file:line:col in front of the sentence, on both backends and at -O0. Proven live: three failures when the expected line is wrong. F8: usize and size_t stay off the foreign-spelling list, and the comment now says why — the honest answer is pointer-width, which is u64 here and u32 on wasm32, and a tree that builds both cannot name one of them. F10 pins the fourth dot shape. F6 moves the not-reached reasons out of the commit bodies and into FIX.org, where they can be read without git.
This commit is contained in:
parent
16f90a7ef5
commit
78d9a0f051
63
FIX.org
63
FIX.org
@ -1598,3 +1598,66 @@ because the code is the same.
|
|||||||
Not fixed here, deliberately: the fix is to catch ~Closed~ in that poll and
|
Not fixed here, deliberately: the fix is to catch ~Closed~ in that poll and
|
||||||
read it as the program having ended, which is a claim about what those rows
|
read it as the program having ended, which is a claim about what those rows
|
||||||
mean and belongs to whoever owns them. Flagged rather than patched.
|
mean and belongs to whoever owns them. Flagged rather than patched.
|
||||||
|
|
||||||
|
* The diagnostics pass, 2026-09-20
|
||||||
|
|
||||||
|
Worked from ~docs/DIAGNOSTICS-AUDIT.md~, which is tracked as of this lane's
|
||||||
|
first commit. Graded against the contract the audit sets out: show the code
|
||||||
|
with the caret, say what was understood, say what conflicts, name the fix.
|
||||||
|
|
||||||
|
** Reached
|
||||||
|
Worst-20 ranks 1, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||||
|
and the runtime half of 2. All four defvar follow-ups. Two from the author's
|
||||||
|
dogfooding notes in DISCUSS.org: the foreign-spelling list, which answers
|
||||||
|
~int~ with ~i32~ instead of a lecture about type variables, and the
|
||||||
|
two-element ~defconst~ whose bracketed type read as an array literal.
|
||||||
|
|
||||||
|
** Not reached, each with the reason
|
||||||
|
|
||||||
|
*** Rank 8: ~unhandled Boom~ has no location
|
||||||
|
Not a copy of the dyn-trap work, and the difference is the calling
|
||||||
|
convention. ~flan_error~ takes five integer arguments — the type id, the
|
||||||
|
condition, the channel, and the name as ptr+len — which is rdi through r8.
|
||||||
|
A ~(loc, loclen)~ pair makes seven, past x86-64's six argument registers, so
|
||||||
|
~lib/x86.ml~ would need stack-argument passing at a call site whose own
|
||||||
|
comment two hundred lines up says "the channel lands in r9 and the register
|
||||||
|
file is exactly full". The dyn entry points took the pair without any of
|
||||||
|
that because none of them was near the limit.
|
||||||
|
|
||||||
|
The rest of rank 8 — the condition's field values, and the handlers that
|
||||||
|
were in scope — is separate work again and has no ABI question in it.
|
||||||
|
|
||||||
|
The audit's gap 4 names a dev-side half of this: ~flan_trap_hook~ hands
|
||||||
|
control to a session that is in-process with the compiler and *can* read the
|
||||||
|
source, so a real caret at runtime belongs in ~lib/dev.ml~. That file is
|
||||||
|
another lane's and the audit already wrote it up as a hand-off.
|
||||||
|
|
||||||
|
*** Rank 20: the fn-literal arity message
|
||||||
|
Re-read and judged already satisfying. The audit asks it to name the
|
||||||
|
parameter list it was measured against; it prints the whole ~(Fn [T ...] R)~,
|
||||||
|
which is that list. Left alone rather than churned.
|
||||||
|
|
||||||
|
*** ~trap_oom~ in flan_dyn.c
|
||||||
|
The other three trap printers took the location pair. This one is reached
|
||||||
|
from ~gc_alloc~, which has no site to be given: every allocation path in the
|
||||||
|
file would have had to carry one for a sentence that is about the host
|
||||||
|
refusing memory rather than about the program. ~trap_range~ has the pair and
|
||||||
|
every caller passes NULL, so giving ~at~, ~set-at~ and ~push~ a site later is
|
||||||
|
a call-site change and not another round of signature churn.
|
||||||
|
|
||||||
|
*** The audit's structural gaps 3, 5 and 6
|
||||||
|
Printing the stable ~kind~ at the end of the first line, the
|
||||||
|
"understood / conflicted" clause order as a writing rule, and non-cascading
|
||||||
|
multiple errors through ~Loc.sink~. Each needs a decision from the author
|
||||||
|
rather than work, which is what the audit says about them too.
|
||||||
|
|
||||||
|
** Two behaviour changes, not only wording
|
||||||
|
~(defn idx [v i] dyn v)~ was *refused* and now compiles as two dyn
|
||||||
|
parameters. The rule is the digits: this language sizes its machine types in
|
||||||
|
the name, so a typo keeps them — ~f65~, ~i33~ — and a parameter called ~i~ or
|
||||||
|
~n~ has none. ~pair_params~'s own comment says that is what the feature is.
|
||||||
|
|
||||||
|
A ~defn~ whose name is a builtin's is still not refused. The builtin still
|
||||||
|
wins every call and the definition is still unreachable; what changed is that
|
||||||
|
the arity message says so and notes the definition. Refusing the shadowing is
|
||||||
|
a language decision and was left to the author.
|
||||||
|
|||||||
58
lib/check.ml
58
lib/check.ml
@ -260,7 +260,11 @@ let nearest cands n = List.find_opt (fun c -> c <> n && one_edit n c) cands
|
|||||||
and this language has [u8] and rune functions, so there is nothing to
|
and this language has [u8] and rune functions, so there is nothing to
|
||||||
translate it to in three words. Nor [void]: it is a return type and the
|
translate it to in three words. Nor [void]: it is a return type and the
|
||||||
answer there is the shape [()], which is [parse]'s message to give and not
|
answer there is the shape [()], which is [parse]'s message to give and not
|
||||||
this one's. *)
|
this one's. Nor [usize] and [size_t]: the honest answer is "as wide as a
|
||||||
|
pointer on this target", which is [u64] on x86-64 and [u32] on wasm32, and
|
||||||
|
a message that named one of them would be wrong half the time on a tree
|
||||||
|
that builds both. A name goes on this list when the answer does not depend
|
||||||
|
on anything. *)
|
||||||
let foreign_spelling = function
|
let foreign_spelling = function
|
||||||
| "int" | "integer" -> Some "i32"
|
| "int" | "integer" -> Some "i32"
|
||||||
| "uint" | "unsigned" -> Some "u32"
|
| "uint" | "unsigned" -> Some "u32"
|
||||||
@ -3915,7 +3919,7 @@ and check_truthy ctx c =
|
|||||||
language. It is said only of the typed side, which is where they are.
|
language. It is said only of the typed side, which is where they are.
|
||||||
|
|
||||||
The comparison is spelled with the condition's own name where there is
|
The comparison is spelled with the condition's own name where there is
|
||||||
one, because [(not= x 0)] is a thing to type and [(not= … 0)] is not.
|
one, because [(!= x 0)] is a thing to type and [(!= … 0)] is not.
|
||||||
Anything more complicated than a name gets the operator and no
|
Anything more complicated than a name gets the operator and no
|
||||||
template: a reconstructed expression would be a guess at code the
|
template: a reconstructed expression would be a guess at code the
|
||||||
reader can see for themselves. *)
|
reader can see for themselves. *)
|
||||||
@ -3930,8 +3934,8 @@ and check_truthy ctx c =
|
|||||||
match c0.Tast.ty with Types.Int _ | Types.Float _ -> true | _ -> false
|
match c0.Tast.ty with Types.Int _ | Types.Float _ -> true | _ -> false
|
||||||
in
|
in
|
||||||
match c.Ast.e, comparable with
|
match c.Ast.e, comparable with
|
||||||
| Ast.Var n, true -> Printf.sprintf " — test it, as (not= %s %s)" n zero
|
| Ast.Var n, true -> Printf.sprintf " — test it, as (!= %s %s)" n zero
|
||||||
| _, true -> Printf.sprintf " — test it against %s with not=" zero
|
| _, true -> Printf.sprintf " — test it against %s with !=" zero
|
||||||
| _ -> ""
|
| _ -> ""
|
||||||
in
|
in
|
||||||
Loc.failk "check/condition-not-bool" loc
|
Loc.failk "check/condition-not-bool" loc
|
||||||
@ -6881,26 +6885,42 @@ and named_call ctx ~want loc name args =
|
|||||||
else if String.contains name '/' then
|
else if String.contains name '/' then
|
||||||
unimplemented loc
|
unimplemented loc
|
||||||
(Printf.sprintf "the call %s into an imported package" name) 4
|
(Printf.sprintf "the call %s into an imported package" name) 4
|
||||||
else if args <> []
|
|
||||||
&& name <> "" && name.[0] = Char.uppercase_ascii name.[0]
|
|
||||||
&& name.[0] <> Char.lowercase_ascii name.[0]
|
|
||||||
then
|
|
||||||
(* [(defvar p (Pair i32))]. A capitalised head with arguments is
|
|
||||||
somebody reaching for a parameterised type, which is what the type
|
|
||||||
resolver says about [(Pair i32)] when the same text lands in a type
|
|
||||||
position. Before defvar took either reading, that is the message
|
|
||||||
this text got; it says the same thing here so the answer does not
|
|
||||||
depend on which side of the fork the form fell down. *)
|
|
||||||
Loc.failk "check/unknown-function" loc
|
|
||||||
"unknown function %s. A capitalised name is a type, and a type given \
|
|
||||||
type arguments — (%s ...) — is generic code, which is milestone 5"
|
|
||||||
name name
|
|
||||||
else
|
else
|
||||||
|
(* The did-you-mean comes first, and for a capitalised head it is asked
|
||||||
|
of the *type* tables as well: [(Piont 1 2)] with [Point] declared is
|
||||||
|
a typo, and the generics sentence below would be a confident answer
|
||||||
|
about a feature nobody was reaching for. Only a capitalised head
|
||||||
|
consults the types — a lowercase name written where a value goes
|
||||||
|
was not a mistyped struct, which is [value_candidates]' whole
|
||||||
|
point. *)
|
||||||
|
let capitalised =
|
||||||
|
name <> "" && name.[0] = Char.uppercase_ascii name.[0]
|
||||||
|
&& name.[0] <> Char.lowercase_ascii name.[0]
|
||||||
|
in
|
||||||
|
let guess =
|
||||||
match nearest (!builtin_names @ value_candidates ctx) name with
|
match nearest (!builtin_names @ value_candidates ctx) name with
|
||||||
|
| Some _ as m -> m
|
||||||
|
| None -> if capitalised then near_miss ctx.env name else None
|
||||||
|
in
|
||||||
|
match guess with
|
||||||
| Some m ->
|
| Some m ->
|
||||||
Loc.failk "check/unknown-function" loc
|
Loc.failk "check/unknown-function" loc
|
||||||
"unknown function %s — did you mean %s?" name m
|
"unknown function %s — did you mean %s?" name m
|
||||||
| None -> Loc.failk "check/unknown-function" loc "unknown function %s" name
|
| None ->
|
||||||
|
if args <> [] && capitalised then
|
||||||
|
(* [(defvar p (Pair i32))]. A capitalised head with arguments and
|
||||||
|
no near miss anywhere is somebody reaching for a parameterised
|
||||||
|
type, which is what the type resolver says about [(Pair i32)]
|
||||||
|
when the same text lands in a type position. Before defvar took
|
||||||
|
either reading, that is the message this text got; it says the
|
||||||
|
same thing here so the answer does not depend on which side of
|
||||||
|
the fork the form fell down. *)
|
||||||
|
Loc.failk "check/unknown-function" loc
|
||||||
|
"unknown function %s. A capitalised name is a type, and a type \
|
||||||
|
given type arguments — (%s ...) — is generic code, which is \
|
||||||
|
milestone 5"
|
||||||
|
name name
|
||||||
|
else Loc.failk "check/unknown-function" loc "unknown function %s" name
|
||||||
|
|
||||||
(* ── A call to a generic function ───────────────────────────────────────
|
(* ── A call to a generic function ───────────────────────────────────────
|
||||||
The whole of instantiation, and it is at the call site because the call
|
The whole of instantiation, and it is at the call site because the call
|
||||||
|
|||||||
@ -1330,7 +1330,10 @@ int32_t flan_dyn_cast_kind(flan_dyn v, const uint8_t *loc, int64_t loc_len,
|
|||||||
: sizeof name - 1;
|
: sizeof name - 1;
|
||||||
memcpy(name, target, n);
|
memcpy(name, target, n);
|
||||||
name[n] = '\0';
|
name[n] = '\0';
|
||||||
trap1(NULL, 0, TYPE_TRAP, name, "a number was wanted", v);
|
/* This one has a site: the cast's own, which the emitter already hands
|
||||||
|
* over for the cross-kind warning below. It was the first entry point on
|
||||||
|
* this side to take a location and it was not passing it on. */
|
||||||
|
trap1(loc, loc_len, TYPE_TRAP, name, "a number was wanted", v);
|
||||||
}
|
}
|
||||||
int32_t is_float = tag == FLAN_DYN_TAG_FLOAT ? 1 : 0;
|
int32_t is_float = tag == FLAN_DYN_TAG_FLOAT ? 1 : 0;
|
||||||
if (is_float != (want_float ? 1 : 0) && site_first_time(loc, loc_len)) {
|
if (is_float != (want_float ? 1 : 0) && site_first_time(loc, loc_len)) {
|
||||||
|
|||||||
28
test/programs/dyn-trap-site.flan
Normal file
28
test/programs/dyn-trap-site.flan
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
;;;; A dyn arithmetic trap says where it happened.
|
||||||
|
;;;;
|
||||||
|
;;;; In a dynamic-first language the dyn traps ARE the type errors, and until
|
||||||
|
;;;; the diagnostics pass they printed with no file, no line and no column:
|
||||||
|
;;;;
|
||||||
|
;;;; dyn +: int and text, and it takes two numbers — (+ 3 "hi")
|
||||||
|
;;;;
|
||||||
|
;;;; flan_rt.c's bounds and arithmetic traps have taken an emitter-threaded
|
||||||
|
;;;; (loc, loclen) pair since they were written, so the ABI precedent was
|
||||||
|
;;;; already there; the five arithmetic and four ordering entry points in
|
||||||
|
;;;; flan_dyn.c simply were never given one. They take it now, and the trap
|
||||||
|
;;;; prints it as the GNU "file:line:col: " prefix, which is what makes
|
||||||
|
;;;; next-error walk to a dyn failure the way it walks to a bounds failure.
|
||||||
|
;;;;
|
||||||
|
;;;; This program exists for the prefix and for nothing else. The line prints
|
||||||
|
;;;; first so that the test can tell "the program ran and then trapped" from
|
||||||
|
;;;; "the program did not start", and the operation is inside a defn so that
|
||||||
|
;;;; the site reported is the operator's own and not the call's — which is the
|
||||||
|
;;;; distinction that matters: the + is what failed, and the + is what the
|
||||||
|
;;;; caret should be under.
|
||||||
|
|
||||||
|
(defn add [x y] dyn
|
||||||
|
(+ x y))
|
||||||
|
|
||||||
|
(defn main [] ()
|
||||||
|
(print "before\n")
|
||||||
|
(print (add 3 "hi"))
|
||||||
|
(print "unreachable\n"))
|
||||||
@ -3788,6 +3788,45 @@ level "1"
|
|||||||
some_nil ~opt:"-O0" ();
|
some_nil ~opt:"-O0" ();
|
||||||
some_nil ~x86:true ();
|
some_nil ~x86:true ();
|
||||||
|
|
||||||
|
(* The dyn trap's own location, end to end: compiled, run, and read off
|
||||||
|
stderr. In a dynamic-first language these traps are the type errors,
|
||||||
|
and they printed with no file and no line at all. The site is a string
|
||||||
|
literal the emitter hands over exactly as it hands [flan_dyn_cast_kind]
|
||||||
|
its own, so this is asserted on both backends and at -O0: the argument
|
||||||
|
is an ordinary one and neither backend treats it specially, which is
|
||||||
|
the claim being pinned.
|
||||||
|
|
||||||
|
23:3 is the (+ x y) inside [add], not the (add 3 "hi") that called it,
|
||||||
|
and that is the point of the site being the operator's: the + is what
|
||||||
|
failed. If the file is edited above line 23 this number moves. *)
|
||||||
|
let trap_site_out = "before\n" in
|
||||||
|
let trap_site ?opt ?x86 () =
|
||||||
|
let exe = compile ?opt ?x86 "programs/dyn-trap-site.flan" in
|
||||||
|
let code, text = run exe None in
|
||||||
|
let name =
|
||||||
|
"dyn: an arithmetic trap says where"
|
||||||
|
^ (match opt with Some o -> ", " ^ o | None -> "")
|
||||||
|
^ (match x86 with Some true -> ", --x86" | _ -> "")
|
||||||
|
in
|
||||||
|
if code <> 134
|
||||||
|
|| not (contains text trap_site_out)
|
||||||
|
|| not (contains text
|
||||||
|
"dyn-trap-site.flan:23:3: dyn +: int and text, and it \
|
||||||
|
takes two numbers — (+ 3 \"hi\")")
|
||||||
|
|| contains text "unreachable"
|
||||||
|
then begin
|
||||||
|
incr failures;
|
||||||
|
Printf.printf
|
||||||
|
"FAIL %s\n got: %S (exit %d)\n wanted: %S then the trap \
|
||||||
|
with its site (exit 134)\n"
|
||||||
|
name text code trap_site_out
|
||||||
|
end;
|
||||||
|
(try Sys.remove exe with Sys_error _ -> ())
|
||||||
|
in
|
||||||
|
trap_site ();
|
||||||
|
trap_site ~opt:"-O0" ();
|
||||||
|
trap_site ~x86:true ();
|
||||||
|
|
||||||
(* A numeric cast opening a dyn box — FIX.org 2026-09-20.
|
(* A numeric cast opening a dyn box — FIX.org 2026-09-20.
|
||||||
programs/dyn-cast.flan is one program because the three behaviours are
|
programs/dyn-cast.flan is one program because the three behaviours are
|
||||||
one story told in order: the same-kind casts print, the cross-kind ones
|
one story told in order: the same-kind casts print, the cross-kind ones
|
||||||
@ -3845,8 +3884,14 @@ level "1"
|
|||||||
|| not (contains text "found a dyn holding an int, and converted it to f64")
|
|| not (contains text "found a dyn holding an int, and converted it to f64")
|
||||||
|| not (contains text "found a dyn holding a float, and converted it to i64")
|
|| not (contains text "found a dyn holding a float, and converted it to i64")
|
||||||
|| not (contains text "found a dyn holding an int, and converted it to f32")
|
|| not (contains text "found a dyn holding an int, and converted it to f32")
|
||||||
(* The non-numeric box, in the runtime's own words. *)
|
(* The non-numeric box, in the runtime's own words — and with the
|
||||||
|| not (contains text "bool, and a number was wanted")
|
site in front of them. [flan_dyn_cast_kind] has taken the cast's
|
||||||
|
location since the warning below needed one, and was the one entry
|
||||||
|
point on the dyn side that had a location and threw it away on the
|
||||||
|
trapping path. 71:10 is the (i64 (as-dyn true)) at the end of
|
||||||
|
the program; if that file is edited above it, this number moves. *)
|
||||||
|
|| not (contains text "dyn-cast.flan:71:10: dyn i64: bool, and a \
|
||||||
|
number was wanted")
|
||||||
then begin
|
then begin
|
||||||
incr failures;
|
incr failures;
|
||||||
Printf.printf
|
Printf.printf
|
||||||
|
|||||||
@ -1746,11 +1746,39 @@ let () =
|
|||||||
"(defstruct P [x i32]) (defn f [] i32 (let [p (P {.x 1})] (set (.x p) 2) (.x p)))";
|
"(defstruct P [x i32]) (defn f [] i32 (let [p (P {.x 1})] (set (.x p) 2) (.x p)))";
|
||||||
rejects_check "a dotted head that is not a struct says what it is"
|
rejects_check "a dotted head that is not a struct says what it is"
|
||||||
"(defn f [] i32 (let [n 1] n.x))" ~needle:"n is i32, which has no fields";
|
"(defn f [] i32 (let [n 1] n.x))" ~needle:"n is i32, which has no fields";
|
||||||
|
(* The fourth shape: nothing is bound under the head either, so the message
|
||||||
|
claims nothing about what q is — only that the dot is not the operator
|
||||||
|
the writer took it for. *)
|
||||||
|
rejects_check "and an unbound head claims nothing about it"
|
||||||
|
"(defn f [] i32 q.x)"
|
||||||
|
~needle:"unknown name q.x — nothing named q is in scope either. A field \
|
||||||
|
is reached through an accessor, (.x q), not with a dot";
|
||||||
(* A capitalised head keeps the case spelling it always had: [Shape.Circle]
|
(* A capitalised head keeps the case spelling it always had: [Shape.Circle]
|
||||||
is real here, so a typo in one is not the dot habit. *)
|
is real here, so a typo in one is not the dot habit. *)
|
||||||
rejects_check "a capitalised dotted name is still a case"
|
(* Both sides of the rule, because only the pair says what it is. A
|
||||||
"(data Shape (Circle [r f64])) (defn f [] Shape Shape.Crcle)"
|
capitalised head is a real spelling here — Shape.Circle — so a typo in
|
||||||
~needle:"unknown";
|
one is a mistyped case and gets none of the accessor advice; the same
|
||||||
|
text with a lowercase head does. The earlier spelling of this row used
|
||||||
|
(data ...), which is not a top-level form at all, so it refused as an
|
||||||
|
unknown top-level form and the needle "unknown" matched that instead of
|
||||||
|
anything this rule does. *)
|
||||||
|
(match (try ignore (checked "(defdata Shape [(Circle [r f64])]) \
|
||||||
|
(defn f [] Shape Shape.Crcle)"); None
|
||||||
|
with Loc.Error d -> Some d) with
|
||||||
|
| Some d ->
|
||||||
|
check "a capitalised dotted name gets no accessor advice"
|
||||||
|
(contains d.Loc.dmsg "unknown name Shape.Crcle"
|
||||||
|
&& not (contains d.Loc.dmsg "accessor"))
|
||||||
|
| None -> check "a mistyped case is refused" false);
|
||||||
|
(match (try ignore (checked "(defdata Shape [(Circle [r f64])]) \
|
||||||
|
(defn f [] Shape shape.Crcle)"); None
|
||||||
|
with Loc.Error d -> Some d) with
|
||||||
|
| Some d ->
|
||||||
|
check "and a lowercase one does"
|
||||||
|
(contains d.Loc.dmsg
|
||||||
|
"nothing named shape is in scope either. A field is reached through \
|
||||||
|
an accessor, (.Crcle shape), not with a dot")
|
||||||
|
| None -> check "a lowercase dotted name is refused" false);
|
||||||
(* [(Pair i32)] in a defvar falls down the value fork now that the third
|
(* [(Pair i32)] in a defvar falls down the value fork now that the third
|
||||||
element takes either reading, and the generics answer the type fork gave
|
element takes either reading, and the generics answer the type fork gave
|
||||||
it has to be reachable from here too. *)
|
it has to be reachable from here too. *)
|
||||||
@ -3790,10 +3818,10 @@ let () =
|
|||||||
is right, and with the condition's own name where it has one. *)
|
is right, and with the condition's own name where it has one. *)
|
||||||
rejects_check "a non-bool condition states the rule"
|
rejects_check "a non-bool condition states the rule"
|
||||||
"(defn f [] i32 (let [x 1] (if x 1 0)))"
|
"(defn f [] i32 (let [x 1] (if x 1 0)))"
|
||||||
~needle:"a condition is a bool or a dyn, and this is i32 — test it, as (not= x 0)";
|
~needle:"a condition is a bool or a dyn, and this is i32 — test it, as (!= x 0)";
|
||||||
rejects_check "and offers no template for a form it cannot name"
|
rejects_check "and offers no template for a form it cannot name"
|
||||||
"(defn f [] i32 (if (+ 1 2) 1 0))"
|
"(defn f [] i32 (if (+ 1 2) 1 0))"
|
||||||
~needle:"this is i32 — test it against 0 with not=";
|
~needle:"this is i32 — test it against 0 with !=";
|
||||||
rejects_check "and offers no comparison at all for a type that has none"
|
rejects_check "and offers no comparison at all for a type that has none"
|
||||||
"(defstruct P [x i32]) (defn f [] i32 (let [p (P {.x 1})] (if p 1 0)))"
|
"(defstruct P [x i32]) (defn f [] i32 (let [p (P {.x 1})] (if p 1 0)))"
|
||||||
~needle:"a condition is a bool or a dyn, and this is P";
|
~needle:"a condition is a bool or a dyn, and this is P";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user