Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler/ml/translcore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,23 @@ and transl_exp0 (e : Typedtree.expression) : Lambda.lambda =
[lambda],
loc )
| None -> lambda)
| Texp_apply
{
funct =
{
exp_desc =
Texp_ident (_, _, {val_kind = Val_prim {prim_name = "%assert"}});
};
args = [(_, Some cond)];
} -> (
(* assert(cond) — same semantics as the old assert keyword *)
match cond.exp_desc with
| Texp_construct (_, {cstr_name = "false"}, _) ->
if !Clflags.no_assert_false then Lambda.lambda_assert_false
else assert_failed e
| _ ->
if !Clflags.noassert then lambda_unit
else Lifthenelse (transl_exp cond, lambda_unit, assert_failed e))
| Texp_apply
{
funct =
Expand Down
23 changes: 23 additions & 0 deletions compiler/ml/typecore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2503,6 +2503,29 @@ and type_expect_ ?deprecated_context ~context ?in_function ?(recarg = Rejected)
type_function ?in_function ~arity ~async loc sexp.pexp_attributes env
ty_expected l
[Ast_helper.Exp.case spat sbody]
| Pexp_apply {funct = {pexp_desc = Pexp_ident lid}; args = [(Nolabel, scond)]}
when match Env.lookup_value lid.txt env with
| _, {val_kind = Val_prim {Primitive.prim_name = "%assert"}} -> true
| _ -> false
| exception Not_found -> false ->
Comment on lines +2505 to +2509
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle %assert when used via aliases or higher-order values

This special-case only rewrites direct assert(cond) calls, so %assert can still escape as a first-class value (e.g. let f = assert; f(true)). In that path the backend no longer sees Texp_assert, and %assert falls through primitive lowering paths that are not implemented for it, which can surface as an internal primitive-conversion failure instead of normal assert semantics. Please either reject non-direct %assert usage in typing or add full primitive lowering support for %assert as a value.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in adbb76c

(* assert(cond) via the %assert primitive — same semantics as the keyword form *)
let cond =
type_expect ~context:(Some AssertCondition) env scond Predef.type_bool
in
let exp_type =
match cond.exp_desc with
| Texp_construct (_, {cstr_name = "false"}, _) -> instance env ty_expected
| _ -> instance_def Predef.type_unit
in
rue
{
exp_desc = Texp_assert cond;
exp_loc = loc;
exp_extra = [];
exp_type;
exp_attributes = sexp.pexp_attributes;
exp_env = env;
}
| Pexp_apply {funct = sfunct; args = sargs; partial; transformed_jsx} ->
assert (sargs <> []);
begin_def ();
Expand Down
6 changes: 0 additions & 6 deletions compiler/syntax/src/res_core.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2374,15 +2374,9 @@ and parse_unary_expr p =
* If you have `a + b`, `a` and `b` both represent
* the operands of the binary expression with opeartor `+` *)
and parse_operand_expr ~context p =
let start_pos = p.Parser.start_pos in
let attrs = ref (parse_attributes p) in
let expr =
match p.Parser.token with
| Assert ->
Parser.next p;
let expr = parse_expr p in
let loc = mk_loc start_pos p.prev_end_pos in
Ast_helper.Exp.assert_ ~loc expr
| Lident "async"
(* we need to be careful when we're in a ternary true branch:
`condition ? ternary-true-branch : false-branch`
Expand Down
10 changes: 5 additions & 5 deletions compiler/syntax/src/res_grammar.ml
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,10 @@ let is_atomic_typ_expr_start = function
| _ -> false

let is_expr_start = function
| Token.Assert | At | Await | Backtick | Bang | Codepoint _ | False | Float _
| For | Hash | If | Int _ | Lbrace | Lbracket | LessThan | Lident _ | List
| Lparen | Minus | MinusDot | Module | Percent | Plus | PlusDot | Bnot | Bor
| Bxor | Band | String _ | Switch | True | Try | Uident _
| Token.At | Await | Backtick | Bang | Codepoint _ | False | Float _ | For
| Hash | If | Int _ | Lbrace | Lbracket | LessThan | Lident _ | List | Lparen
| Minus | MinusDot | Module | Percent | Plus | PlusDot | Bnot | Bor | Bxor
| Band | String _ | Switch | True | Try | Uident _
| Underscore (* _ => doThings() *)
| While | Forwardslash | ForwardslashDot | Dict ->
true
Expand Down Expand Up @@ -264,7 +264,7 @@ let is_attribute_start = function
let is_jsx_child_start = is_atomic_expr_start

let is_block_expr_start = function
| Token.Assert | At | Await | Backtick | Bang | Break | Codepoint _ | Continue
| Token.At | Await | Backtick | Bang | Break | Codepoint _ | Continue
| Exception | False | Float _ | For | Forwardslash | ForwardslashDot | Hash
| If | Int _ | Lbrace | Lbracket | LessThan | Let _ | Lident _ | List | Lparen
| Minus | MinusDot | Module | Open | Percent | Plus | PlusDot | String _
Expand Down
5 changes: 1 addition & 4 deletions compiler/syntax/src/res_token.ml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ type t =
| LessThan
| Hash
| HashEqual
| Assert
| Tilde
| Question
| If
Expand Down Expand Up @@ -182,7 +181,6 @@ let to_string = function
| Asterisk -> "*"
| AsteriskDot -> "*."
| Exponentiation -> "**"
| Assert -> "assert"
| Tilde -> "~"
| Question -> "?"
| If -> "if"
Expand Down Expand Up @@ -234,7 +232,6 @@ let to_string = function
let keyword_table = function
| "and" -> And
| "as" -> As
| "assert" -> Assert
| "await" -> Await
| "break" -> Break
| "constraint" -> Constraint
Expand Down Expand Up @@ -267,7 +264,7 @@ let keyword_table = function
[@@raises Not_found]

let is_keyword = function
| Await | Break | Continue | And | As | Assert | Constraint | Else | Exception
| Await | Break | Continue | And | As | Constraint | Else | Exception
| External | False | For | If | In | Include | Land | Let _ | List | Lor
| Module | Mutable | Of | Open | Private | Rec | Switch | True | Try | Typ
| When | While | Dict ->
Expand Down
1 change: 0 additions & 1 deletion compiler/syntax/src/res_token_debugger.ml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ let dump_tokens filename =
| Res_token.LessThan -> "LessThan"
| Res_token.Hash -> "Hash"
| Res_token.HashEqual -> "HashEqual"
| Res_token.Assert -> "Assert"
| Res_token.Tilde -> "Tilde"
| Res_token.Question -> "Question"
| Res_token.If -> "If"
Expand Down
2 changes: 2 additions & 0 deletions packages/@rescript/runtime/Pervasives.res
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ result == "Caught exception: Out of milk"
*/
external throw: exn => 'a = "%raise"

external assert: bool => 'a = "%assert"

@deprecated({
reason: "`raise` has been renamed to `throw` to align with JavaScript vocabulary. Please use `throw` instead",
migrate: throw(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ let progress = {
let counter = ref((100))
() => {
if counter.contents < 0 {
assert false
assert(false)
}
counter := counter.contents - 1
}
Expand Down Expand Up @@ -247,7 +247,7 @@ module UITermination = {
let nothing: onClick = () => ()

type div = (~text: string, ~onClick: onClick) => dom
let div: div = (~text, ~onClick) => assert false
let div: div = (~text, ~onClick) => assert(false)

let initState = n => n == 0 ? Some(42) : None
let increment = n => Some(n + 1)
Expand Down
2 changes: 1 addition & 1 deletion tests/analysis_tests/tests/src/Objects.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ type nestedObjT = {"y": objT}
module Rec = {
type recordt = {xx: int, ss: string}

let recordVal: recordt = assert false
let recordVal: recordt = assert(false)
}

let object: objT = {"name": "abc", "age": 4}
2 changes: 1 addition & 1 deletion tests/analysis_tests/tests/src/Patterns.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module A = {

type rec arr = A(array<arr>)

let A([v1, _, _]) | _ as v1 = assert false
let A([v1, _, _]) | _ as v1 = assert(false)

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ node :=
node
}

let x = z->switch z {| _ => false}
let x = z->@attr switch z {| _ => false}
let x = z->assert(z)
let x = z->@attr assert(z)
let x = z->switch z {| _ => false}
let x = z->@attr switch z {| _ => false}
let x = z->try sideEffect() catch { | _ => f() }
let x = z->@attr try sideEffect() catch { | _ => f() }
let x = z->for i in 0 to 10 { () }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
[@attr ])
let x = z -> (match z with | _ -> false)
let x = z -> ((match z with | _ -> false)[@attr ])
let x = z -> (assert z)
let x = z -> ((assert z)[@attr ])
let x = z -> (try sideEffect () with | _ -> f ())
let x = z -> ((try sideEffect () with | _ -> f ())[@attr ])
let x = z -> for i = 0 to 10 do () done
Expand Down
5 changes: 1 addition & 4 deletions tests/syntax_tests/data/printer/comments/blockExpr.res
Original file line number Diff line number Diff line change
Expand Up @@ -357,16 +357,13 @@ if {
true
} // trailing

// here
assert({
// here
open /* inside */ Matrix
// c

// c2
compare(m1, m2)
// after

// test
})

user.name = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,16 +354,13 @@ if {
true
} // trailing

// here
assert({
// here
open /* inside */ Matrix
// c

// c2
compare(m1, m2)
// after

// test
})

user.name = {
Expand Down
70 changes: 35 additions & 35 deletions tests/syntax_tests/data/printer/expr/assert.res
Original file line number Diff line number Diff line change
@@ -1,56 +1,56 @@
assert false

assert truth

let x = assert true
let x = assert 12
let x = assert (12: int)
let x = assert 12
let x = assert list{1, 2, ...x}
let x = assert module(Foo: Bar)
let x = assert module(Foo)
let x = assert Rgb(1, 2, 3)
let x = assert [a, b, c]
let x = assert {x: 1, y: 3}
let x = assert (1, 2, 3)
let x = assert %extension
let x = assert user.name
let x = assert streets[0]
let x = assert apply(arg1, arg2)
let x = assert apply(arg1, arg2)
let x = assert -1
let x = assert !true
let x = assert (x => print(x))
let x = assert (switch x {
assert(false)

assert(truth)

let x = assert(true)
let x = assert(12)
let x = assert(12: int)
let x = assert(12)
let x = assert(list{1, 2, ...x})
let x = assert(module(Foo: Bar))
let x = assert(module(Foo))
let x = assert(Rgb(1, 2, 3))
let x = assert([a, b, c])
let x = assert({x: 1, y: 3})
let x = assert((1, 2, 3))
let x = assert(%extension)
let x = assert(user.name)
let x = assert(streets[0])
let x = assert(apply(arg1, arg2))
let x = assert(apply(arg1, arg2))
let x = assert(-1)
let x = assert(!true)
let x = assert(x => print(x))
let x = assert(switch x {
| Blue => ()
| Yello => ()
})

let x = assert (for i in 0 to 10 {
let x = assert(for i in 0 to 10 {
print_int(i)
})

let x = assert (if i < 10 {
let x = assert(if i < 10 {
print_int(i)
} else {
print_int(1000)
})

let x = assert (while i < 10 {
let x = assert(while i < 10 {
print_int(i)
})

let x = assert (try sideEffect() catch {| Exit => ()})
let x = assert(try sideEffect() catch {| Exit => ()})

let x = assert (@attr expr)
let x = assert(@attr expr)

let x = assert (a + b)
let x = assert(a + b)

let x = @attr assert false
let x = @attr assert(false)

assert invariant["fatal"]
assert invariants[0]
assert(invariant["fatal"])
assert(invariants[0])

assert address["street"] = "Brusselsestraat"
assert(address["street"] = "Brusselsestraat")

assert (true ? 0 : 1)
assert(true ? 0 : 1)
56 changes: 33 additions & 23 deletions tests/syntax_tests/data/printer/expr/expected/assert.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ assert(truth)

let x = assert(true)
let x = assert(12)
let x = assert(12: int)
let x = assert((12: int))
let x = assert(12)
let x = assert(list{1, 2, ...x})
let x = assert(module(Foo: Bar))
Expand All @@ -21,28 +21,38 @@ let x = assert(apply(arg1, arg2))
let x = assert(-1)
let x = assert(!true)
let x = assert(x => print(x))
let x = assert(switch x {
| Blue => ()
| Yello => ()
})

let x = assert(for i in 0 to 10 {
print_int(i)
})

let x = assert(if i < 10 {
print_int(i)
} else {
print_int(1000)
})

let x = assert(while i < 10 {
print_int(i)
})

let x = assert(try sideEffect() catch {
| Exit => ()
})
let x = assert(
switch x {
| Blue => ()
| Yello => ()
},
)

let x = assert(
for i in 0 to 10 {
print_int(i)
},
)

let x = assert(
if i < 10 {
print_int(i)
} else {
print_int(1000)
},
)

let x = assert(
while i < 10 {
print_int(i)
},
)

let x = assert(
try sideEffect() catch {
| Exit => ()
},
)

let x = assert(@attr expr)

Expand Down
Loading
Loading