mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
commit
0105fa4c4a
19 changed files with 323 additions and 21 deletions
|
@ -279,6 +279,7 @@ fn to_pending_def<'a>(
|
||||||
Type(TypeDef::Ability { .. }) => todo_abilities!(),
|
Type(TypeDef::Ability { .. }) => todo_abilities!(),
|
||||||
|
|
||||||
Value(AstValueDef::Expect { .. }) => todo!(),
|
Value(AstValueDef::Expect { .. }) => todo!(),
|
||||||
|
Value(AstValueDef::ExpectFx { .. }) => todo!(),
|
||||||
|
|
||||||
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {
|
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {
|
||||||
to_pending_def(env, sub_def, scope, pattern_type)
|
to_pending_def(env, sub_def, scope, pattern_type)
|
||||||
|
|
|
@ -87,6 +87,7 @@ pub struct Annotation {
|
||||||
pub(crate) struct CanDefs {
|
pub(crate) struct CanDefs {
|
||||||
defs: Vec<Option<Def>>,
|
defs: Vec<Option<Def>>,
|
||||||
expects: Expects,
|
expects: Expects,
|
||||||
|
expects_fx: Expects,
|
||||||
def_ordering: DefOrdering,
|
def_ordering: DefOrdering,
|
||||||
aliases: VecMap<Symbol, Alias>,
|
aliases: VecMap<Symbol, Alias>,
|
||||||
}
|
}
|
||||||
|
@ -106,6 +107,12 @@ impl Expects {
|
||||||
preceding_comment: Vec::with_capacity(capacity),
|
preceding_comment: Vec::with_capacity(capacity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, loc_can_condition: Loc<Expr>, preceding_comment: Region) {
|
||||||
|
self.conditions.push(loc_can_condition.value);
|
||||||
|
self.regions.push(loc_can_condition.region);
|
||||||
|
self.preceding_comment.push(preceding_comment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Def that has had patterns and type annnotations canonicalized,
|
/// A Def that has had patterns and type annnotations canonicalized,
|
||||||
|
@ -233,6 +240,7 @@ pub enum Declaration {
|
||||||
DeclareRec(Vec<Def>, IllegalCycleMark),
|
DeclareRec(Vec<Def>, IllegalCycleMark),
|
||||||
Builtin(Def),
|
Builtin(Def),
|
||||||
Expects(Expects),
|
Expects(Expects),
|
||||||
|
ExpectsFx(Expects),
|
||||||
/// If we know a cycle is illegal during canonicalization.
|
/// If we know a cycle is illegal during canonicalization.
|
||||||
/// Otherwise we will try to detect this during solving; see [`IllegalCycleMark`].
|
/// Otherwise we will try to detect this during solving; see [`IllegalCycleMark`].
|
||||||
InvalidCycle(Vec<CycleEntry>),
|
InvalidCycle(Vec<CycleEntry>),
|
||||||
|
@ -247,6 +255,7 @@ impl Declaration {
|
||||||
InvalidCycle { .. } => 0,
|
InvalidCycle { .. } => 0,
|
||||||
Builtin(_) => 0,
|
Builtin(_) => 0,
|
||||||
Expects(_) => 0,
|
Expects(_) => 0,
|
||||||
|
ExpectsFx(_) => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +271,7 @@ impl Declaration {
|
||||||
&cycles.first().unwrap().expr_region,
|
&cycles.first().unwrap().expr_region,
|
||||||
&cycles.last().unwrap().expr_region,
|
&cycles.last().unwrap().expr_region,
|
||||||
),
|
),
|
||||||
Declaration::Expects(expects) => Region::span_across(
|
Declaration::Expects(expects) | Declaration::ExpectsFx(expects) => Region::span_across(
|
||||||
expects.regions.first().unwrap(),
|
expects.regions.first().unwrap(),
|
||||||
expects.regions.last().unwrap(),
|
expects.regions.last().unwrap(),
|
||||||
),
|
),
|
||||||
|
@ -907,6 +916,7 @@ fn canonicalize_value_defs<'a>(
|
||||||
// once we've finished assembling the entire scope.
|
// once we've finished assembling the entire scope.
|
||||||
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
|
||||||
let mut pending_expects = Vec::with_capacity(value_defs.len());
|
let mut pending_expects = Vec::with_capacity(value_defs.len());
|
||||||
|
let mut pending_expect_fx = Vec::with_capacity(value_defs.len());
|
||||||
|
|
||||||
for loc_pending_def in value_defs {
|
for loc_pending_def in value_defs {
|
||||||
match loc_pending_def.value {
|
match loc_pending_def.value {
|
||||||
|
@ -921,6 +931,10 @@ fn canonicalize_value_defs<'a>(
|
||||||
PendingValue::Expect(pending_expect) => {
|
PendingValue::Expect(pending_expect) => {
|
||||||
pending_expects.push(pending_expect);
|
pending_expects.push(pending_expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PendingValue::ExpectFx(pending_expect) => {
|
||||||
|
pending_expect_fx.push(pending_expect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -979,6 +993,7 @@ fn canonicalize_value_defs<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut expects = Expects::with_capacity(pending_expects.len());
|
let mut expects = Expects::with_capacity(pending_expects.len());
|
||||||
|
let mut expects_fx = Expects::with_capacity(pending_expects.len());
|
||||||
|
|
||||||
for pending in pending_expects {
|
for pending in pending_expects {
|
||||||
let (loc_can_condition, can_output) = canonicalize_expr(
|
let (loc_can_condition, can_output) = canonicalize_expr(
|
||||||
|
@ -989,9 +1004,21 @@ fn canonicalize_value_defs<'a>(
|
||||||
&pending.condition.value,
|
&pending.condition.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
expects.conditions.push(loc_can_condition.value);
|
expects.push(loc_can_condition, pending.preceding_comment);
|
||||||
expects.regions.push(loc_can_condition.region);
|
|
||||||
expects.preceding_comment.push(pending.preceding_comment);
|
output.union(can_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
for pending in pending_expect_fx {
|
||||||
|
let (loc_can_condition, can_output) = canonicalize_expr(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
scope,
|
||||||
|
pending.condition.region,
|
||||||
|
&pending.condition.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
expects_fx.push(loc_can_condition, pending.preceding_comment);
|
||||||
|
|
||||||
output.union(can_output);
|
output.union(can_output);
|
||||||
}
|
}
|
||||||
|
@ -999,6 +1026,7 @@ fn canonicalize_value_defs<'a>(
|
||||||
let can_defs = CanDefs {
|
let can_defs = CanDefs {
|
||||||
defs,
|
defs,
|
||||||
expects,
|
expects,
|
||||||
|
expects_fx,
|
||||||
def_ordering,
|
def_ordering,
|
||||||
aliases,
|
aliases,
|
||||||
};
|
};
|
||||||
|
@ -1382,6 +1410,7 @@ pub(crate) fn sort_can_defs_new(
|
||||||
let CanDefs {
|
let CanDefs {
|
||||||
defs,
|
defs,
|
||||||
expects,
|
expects,
|
||||||
|
expects_fx,
|
||||||
def_ordering,
|
def_ordering,
|
||||||
aliases,
|
aliases,
|
||||||
} = defs;
|
} = defs;
|
||||||
|
@ -1412,6 +1441,19 @@ pub(crate) fn sort_can_defs_new(
|
||||||
declarations.push_expect(preceding_comment, name, Loc::at(region, condition));
|
declarations.push_expect(preceding_comment, name, Loc::at(region, condition));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let it = expects_fx
|
||||||
|
.conditions
|
||||||
|
.into_iter()
|
||||||
|
.zip(expects_fx.regions)
|
||||||
|
.zip(expects_fx.preceding_comment);
|
||||||
|
|
||||||
|
for ((condition, region), preceding_comment) in it {
|
||||||
|
// an `expect` does not have a user-defined name, but we'll need a name to call the expectation
|
||||||
|
let name = scope.gen_unique_symbol();
|
||||||
|
|
||||||
|
declarations.push_expect_fx(preceding_comment, name, Loc::at(region, condition));
|
||||||
|
}
|
||||||
|
|
||||||
for (symbol, alias) in aliases.into_iter() {
|
for (symbol, alias) in aliases.into_iter() {
|
||||||
output.aliases.insert(symbol, alias);
|
output.aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
@ -1589,6 +1631,7 @@ pub(crate) fn sort_can_defs(
|
||||||
let CanDefs {
|
let CanDefs {
|
||||||
mut defs,
|
mut defs,
|
||||||
expects,
|
expects,
|
||||||
|
expects_fx,
|
||||||
def_ordering,
|
def_ordering,
|
||||||
aliases,
|
aliases,
|
||||||
} = defs;
|
} = defs;
|
||||||
|
@ -1703,6 +1746,10 @@ pub(crate) fn sort_can_defs(
|
||||||
declarations.push(Declaration::Expects(expects));
|
declarations.push(Declaration::Expects(expects));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !expects_fx.conditions.is_empty() {
|
||||||
|
declarations.push(Declaration::ExpectsFx(expects_fx));
|
||||||
|
}
|
||||||
|
|
||||||
(declarations, output)
|
(declarations, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2195,6 +2242,10 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
|
||||||
// Expects should only be added to top-level decls, not to let-exprs!
|
// Expects should only be added to top-level decls, not to let-exprs!
|
||||||
unreachable!("{:?}", &expects)
|
unreachable!("{:?}", &expects)
|
||||||
}
|
}
|
||||||
|
Declaration::ExpectsFx(expects) => {
|
||||||
|
// Expects should only be added to top-level decls, not to let-exprs!
|
||||||
|
unreachable!("{:?}", &expects)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2391,6 +2442,7 @@ fn to_pending_type_def<'a>(
|
||||||
enum PendingValue<'a> {
|
enum PendingValue<'a> {
|
||||||
Def(PendingValueDef<'a>),
|
Def(PendingValueDef<'a>),
|
||||||
Expect(PendingExpect<'a>),
|
Expect(PendingExpect<'a>),
|
||||||
|
ExpectFx(PendingExpect<'a>),
|
||||||
SignatureDefMismatch,
|
SignatureDefMismatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2503,6 +2555,14 @@ fn to_pending_value_def<'a>(
|
||||||
condition,
|
condition,
|
||||||
preceding_comment: *preceding_comment,
|
preceding_comment: *preceding_comment,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
ExpectFx {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => PendingValue::ExpectFx(PendingExpect {
|
||||||
|
condition,
|
||||||
|
preceding_comment: *preceding_comment,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2311,6 +2311,24 @@ impl Declarations {
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_expect_fx(
|
||||||
|
&mut self,
|
||||||
|
preceding_comment: Region,
|
||||||
|
name: Symbol,
|
||||||
|
loc_expr: Loc<Expr>,
|
||||||
|
) -> usize {
|
||||||
|
let index = self.declarations.len();
|
||||||
|
|
||||||
|
self.declarations.push(DeclarationTag::Expectation);
|
||||||
|
self.variables.push(Variable::BOOL);
|
||||||
|
self.symbols.push(Loc::at(preceding_comment, name));
|
||||||
|
self.annotations.push(None);
|
||||||
|
|
||||||
|
self.expressions.push(loc_expr);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push_value_def(
|
pub fn push_value_def(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol: Loc<Symbol>,
|
symbol: Loc<Symbol>,
|
||||||
|
@ -2491,6 +2509,12 @@ impl Declarations {
|
||||||
|
|
||||||
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
||||||
}
|
}
|
||||||
|
ExpectationFx => {
|
||||||
|
let loc_expr =
|
||||||
|
toplevel_expect_to_inline_expect(self.expressions[index].clone());
|
||||||
|
|
||||||
|
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2504,6 +2528,7 @@ roc_error_macros::assert_sizeof_default!(DeclarationTag, 8);
|
||||||
pub enum DeclarationTag {
|
pub enum DeclarationTag {
|
||||||
Value,
|
Value,
|
||||||
Expectation,
|
Expectation,
|
||||||
|
ExpectationFx,
|
||||||
Function(Index<Loc<FunctionDef>>),
|
Function(Index<Loc<FunctionDef>>),
|
||||||
Recursive(Index<Loc<FunctionDef>>),
|
Recursive(Index<Loc<FunctionDef>>),
|
||||||
TailRecursive(Index<Loc<FunctionDef>>),
|
TailRecursive(Index<Loc<FunctionDef>>),
|
||||||
|
@ -2516,14 +2541,14 @@ pub enum DeclarationTag {
|
||||||
|
|
||||||
impl DeclarationTag {
|
impl DeclarationTag {
|
||||||
fn len(self) -> usize {
|
fn len(self) -> usize {
|
||||||
|
use DeclarationTag::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
DeclarationTag::Function(_) => 1,
|
Function(_) | Recursive(_) | TailRecursive(_) => 1,
|
||||||
DeclarationTag::Recursive(_) => 1,
|
Value => 1,
|
||||||
DeclarationTag::TailRecursive(_) => 1,
|
Expectation | ExpectationFx => 1,
|
||||||
DeclarationTag::Value => 1,
|
Destructure(_) => 1,
|
||||||
DeclarationTag::Expectation => 1,
|
MutualRecursion { length, .. } => length as usize + 1,
|
||||||
DeclarationTag::Destructure(_) => 1,
|
|
||||||
DeclarationTag::MutualRecursion { length, .. } => length as usize + 1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -620,6 +620,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
// the declarations of this group will be treaded individually by later iterations
|
// the declarations of this group will be treaded individually by later iterations
|
||||||
}
|
}
|
||||||
Expectation => { /* ignore */ }
|
Expectation => { /* ignore */ }
|
||||||
|
ExpectationFx => { /* ignore */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +743,10 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
let loc_expr = &mut declarations.expressions[index];
|
let loc_expr = &mut declarations.expressions[index];
|
||||||
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
||||||
}
|
}
|
||||||
|
ExpectationFx => {
|
||||||
|
let loc_expr = &mut declarations.expressions[index];
|
||||||
|
fix_values_captured_in_closure_expr(&mut loc_expr.value, &mut VecSet::default());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,16 @@ fn desugar_value_def<'a>(arena: &'a Bump, def: &'a ValueDef<'a>) -> ValueDef<'a>
|
||||||
preceding_comment: *preceding_comment,
|
preceding_comment: *preceding_comment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ExpectFx {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => {
|
||||||
|
let desugared_condition = &*arena.alloc(desugar_expr(arena, condition));
|
||||||
|
ExpectFx {
|
||||||
|
condition: desugared_condition,
|
||||||
|
preceding_comment: *preceding_comment,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,11 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
|
||||||
|
|
||||||
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
||||||
}
|
}
|
||||||
|
ExpectationFx => {
|
||||||
|
let loc_condition = &decls.expressions[index];
|
||||||
|
|
||||||
|
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
|
||||||
|
}
|
||||||
Function(function_index)
|
Function(function_index)
|
||||||
| Recursive(function_index)
|
| Recursive(function_index)
|
||||||
| TailRecursive(function_index) => {
|
| TailRecursive(function_index) => {
|
||||||
|
@ -119,6 +124,12 @@ fn walk_decl<V: Visitor>(visitor: &mut V, decl: &Declaration) {
|
||||||
visitor.visit_expr(condition, *region, Variable::BOOL);
|
visitor.visit_expr(condition, *region, Variable::BOOL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Declaration::ExpectsFx(expects) => {
|
||||||
|
let it = expects.regions.iter().zip(expects.conditions.iter());
|
||||||
|
for (region, condition) in it {
|
||||||
|
visitor.visit_expr(condition, *region, Variable::BOOL);
|
||||||
|
}
|
||||||
|
}
|
||||||
Declaration::Builtin(def) => visitor.visit_def(def),
|
Declaration::Builtin(def) => visitor.visit_def(def),
|
||||||
Declaration::InvalidCycle(_cycles) => {
|
Declaration::InvalidCycle(_cycles) => {
|
||||||
// ignore
|
// ignore
|
||||||
|
|
|
@ -1997,6 +1997,23 @@ pub fn constrain_decls(
|
||||||
|
|
||||||
constraint = constraints.let_constraint([], [], [], expect_constraint, constraint)
|
constraint = constraints.let_constraint([], [], [], expect_constraint, constraint)
|
||||||
}
|
}
|
||||||
|
ExpectationFx => {
|
||||||
|
let loc_expr = &declarations.expressions[index];
|
||||||
|
|
||||||
|
let bool_type = Type::Variable(Variable::BOOL);
|
||||||
|
let expected =
|
||||||
|
Expected::ForReason(Reason::ExpectCondition, bool_type, loc_expr.region);
|
||||||
|
|
||||||
|
let expect_constraint = constrain_expr(
|
||||||
|
constraints,
|
||||||
|
&mut env,
|
||||||
|
loc_expr.region,
|
||||||
|
&loc_expr.value,
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
|
||||||
|
constraint = constraints.let_constraint([], [], [], expect_constraint, constraint)
|
||||||
|
}
|
||||||
Function(function_def_index) => {
|
Function(function_def_index) => {
|
||||||
constraint = constrain_function_def(
|
constraint = constrain_function_def(
|
||||||
constraints,
|
constraints,
|
||||||
|
|
|
@ -159,6 +159,7 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
|
Body(loc_pattern, loc_expr) => loc_pattern.is_multiline() || loc_expr.is_multiline(),
|
||||||
AnnotatedBody { .. } => true,
|
AnnotatedBody { .. } => true,
|
||||||
Expect { condition, .. } => condition.is_multiline(),
|
Expect { condition, .. } => condition.is_multiline(),
|
||||||
|
ExpectFx { condition, .. } => condition.is_multiline(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +234,9 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
||||||
}
|
}
|
||||||
Expect { condition, .. } => fmt_expect(buf, condition, self.is_multiline(), indent),
|
Expect { condition, .. } => fmt_expect(buf, condition, self.is_multiline(), indent),
|
||||||
|
ExpectFx { condition, .. } => {
|
||||||
|
fmt_expect_fx(buf, condition, self.is_multiline(), indent)
|
||||||
|
}
|
||||||
AnnotatedBody {
|
AnnotatedBody {
|
||||||
ann_pattern,
|
ann_pattern,
|
||||||
ann_type,
|
ann_type,
|
||||||
|
@ -303,6 +307,27 @@ fn fmt_expect<'a, 'buf>(
|
||||||
condition.format(buf, return_indent);
|
condition.format(buf, return_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_expect_fx<'a, 'buf>(
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
condition: &'a Loc<Expr<'a>>,
|
||||||
|
is_multiline: bool,
|
||||||
|
indent: u16,
|
||||||
|
) {
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str("expect-fx");
|
||||||
|
|
||||||
|
let return_indent = if is_multiline {
|
||||||
|
buf.newline();
|
||||||
|
indent + INDENT
|
||||||
|
} else {
|
||||||
|
buf.spaces(1);
|
||||||
|
indent
|
||||||
|
};
|
||||||
|
|
||||||
|
condition.format(buf, return_indent);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fmt_value_def<'a, 'buf>(
|
pub fn fmt_value_def<'a, 'buf>(
|
||||||
buf: &mut Buf<'buf>,
|
buf: &mut Buf<'buf>,
|
||||||
def: &roc_parse::ast::ValueDef<'a>,
|
def: &roc_parse::ast::ValueDef<'a>,
|
||||||
|
|
|
@ -547,6 +547,13 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
||||||
condition: arena.alloc(condition.remove_spaces(arena)),
|
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||||
preceding_comment,
|
preceding_comment,
|
||||||
},
|
},
|
||||||
|
ExpectFx {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => ExpectFx {
|
||||||
|
condition: arena.alloc(condition.remove_spaces(arena)),
|
||||||
|
preceding_comment,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,6 +203,10 @@ fn generate_entry_docs<'a>(
|
||||||
ValueDef::Expect { .. } => {
|
ValueDef::Expect { .. } => {
|
||||||
// Don't generate docs for `expect`s
|
// Don't generate docs for `expect`s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValueDef::ExpectFx { .. } => {
|
||||||
|
// Don't generate docs for `expect-fx`s
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Ok(type_index) => match &defs.type_defs[type_index.index()] {
|
Ok(type_index) => match &defs.type_defs[type_index.index()] {
|
||||||
TypeDef::Alias {
|
TypeDef::Alias {
|
||||||
|
|
|
@ -5067,6 +5067,80 @@ fn build_pending_specializations<'a>(
|
||||||
let expr_region = declarations.expressions[index].region;
|
let expr_region = declarations.expressions[index].region;
|
||||||
let region = Region::span_across(&name_region, &expr_region);
|
let region = Region::span_across(&name_region, &expr_region);
|
||||||
|
|
||||||
|
toplevel_expects.insert(symbol, region);
|
||||||
|
procs_base.partial_procs.insert(symbol, proc);
|
||||||
|
}
|
||||||
|
ExpectationFx => {
|
||||||
|
// skip expectations if we're not going to run them
|
||||||
|
match execution_mode {
|
||||||
|
ExecutionMode::Test => { /* fall through */ }
|
||||||
|
ExecutionMode::Check | ExecutionMode::Executable => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark this symbol as a top-level thunk before any other work on the procs
|
||||||
|
module_thunks.push(symbol);
|
||||||
|
|
||||||
|
let expr_var = Variable::EMPTY_RECORD;
|
||||||
|
|
||||||
|
let is_host_exposed = true;
|
||||||
|
|
||||||
|
// If this is an exposed symbol, we need to
|
||||||
|
// register it as such. Otherwise, since it
|
||||||
|
// never gets called by Roc code, it will never
|
||||||
|
// get specialized!
|
||||||
|
if is_host_exposed {
|
||||||
|
let layout_result =
|
||||||
|
layout_cache.raw_from_var(mono_env.arena, expr_var, mono_env.subs);
|
||||||
|
|
||||||
|
// cannot specialize when e.g. main's type contains type variables
|
||||||
|
if let Err(e) = layout_result {
|
||||||
|
match e {
|
||||||
|
LayoutProblem::Erroneous => {
|
||||||
|
let message = "top level function has erroneous type";
|
||||||
|
procs_base.runtime_errors.insert(symbol, message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LayoutProblem::UnresolvedTypeVar(v) => {
|
||||||
|
let message = format!(
|
||||||
|
"top level function has unresolved type variable {:?}",
|
||||||
|
v
|
||||||
|
);
|
||||||
|
procs_base
|
||||||
|
.runtime_errors
|
||||||
|
.insert(symbol, mono_env.arena.alloc(message));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
procs_base.host_specializations.insert_host_exposed(
|
||||||
|
mono_env.subs,
|
||||||
|
LambdaName::no_niche(symbol),
|
||||||
|
annotation,
|
||||||
|
expr_var,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = roc_can::expr::toplevel_expect_to_inline_expect(body);
|
||||||
|
|
||||||
|
let proc = PartialProc {
|
||||||
|
annotation: expr_var,
|
||||||
|
// This is a 0-arity thunk, so it has no arguments.
|
||||||
|
pattern_symbols: &[],
|
||||||
|
// This is a top-level definition, so it cannot capture anything
|
||||||
|
captured_symbols: CapturedSymbols::None,
|
||||||
|
body: body.value,
|
||||||
|
body_var: expr_var,
|
||||||
|
// This is a 0-arity thunk, so it cannot be recursive
|
||||||
|
is_self_recursive: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// extend the region of the expect expression with the region of the preceding
|
||||||
|
// comment, so it is shown in failure/panic messages
|
||||||
|
let name_region = declarations.symbols[index].region;
|
||||||
|
let expr_region = declarations.expressions[index].region;
|
||||||
|
let region = Region::span_across(&name_region, &expr_region);
|
||||||
|
|
||||||
toplevel_expects.insert(symbol, region);
|
toplevel_expects.insert(symbol, region);
|
||||||
procs_base.partial_procs.insert(symbol, proc);
|
procs_base.partial_procs.insert(symbol, proc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,6 +278,10 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st
|
||||||
// at least at the moment this does not happen
|
// at least at the moment this does not happen
|
||||||
panic!("Unexpected expectation in module declarations");
|
panic!("Unexpected expectation in module declarations");
|
||||||
}
|
}
|
||||||
|
ExpectationFx => {
|
||||||
|
// at least at the moment this does not happen
|
||||||
|
panic!("Unexpected expectation in module declarations");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +367,7 @@ fn interface_with_deps() {
|
||||||
def_count += 1;
|
def_count += 1;
|
||||||
}
|
}
|
||||||
MutualRecursion { .. } => { /* do nothing, not a def */ }
|
MutualRecursion { .. } => { /* do nothing, not a def */ }
|
||||||
Expectation => { /* do nothing, not a def */ }
|
Expectation | ExpectationFx => { /* do nothing, not a def */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,6 +335,11 @@ pub enum ValueDef<'a> {
|
||||||
condition: &'a Loc<Expr<'a>>,
|
condition: &'a Loc<Expr<'a>>,
|
||||||
preceding_comment: Region,
|
preceding_comment: Region,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ExpectFx {
|
||||||
|
condition: &'a Loc<Expr<'a>>,
|
||||||
|
preceding_comment: Region,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
|
|
@ -633,6 +633,11 @@ fn parse_defs_end<'a>(
|
||||||
|
|
||||||
let start = state.pos();
|
let start = state.pos();
|
||||||
|
|
||||||
|
let parse_expect_vanilla =
|
||||||
|
crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect);
|
||||||
|
let parse_expect_fx = crate::parser::keyword_e(crate::keyword::EXPECT_FX, EExpect::Expect);
|
||||||
|
let parse_expect = either!(parse_expect_vanilla, parse_expect_fx);
|
||||||
|
|
||||||
match space0_after_e(
|
match space0_after_e(
|
||||||
crate::pattern::loc_pattern_help(min_indent),
|
crate::pattern::loc_pattern_help(min_indent),
|
||||||
min_indent,
|
min_indent,
|
||||||
|
@ -641,14 +646,13 @@ fn parse_defs_end<'a>(
|
||||||
.parse(arena, state.clone())
|
.parse(arena, state.clone())
|
||||||
{
|
{
|
||||||
Err((NoProgress, _, _)) => {
|
Err((NoProgress, _, _)) => {
|
||||||
match crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect)
|
match parse_expect.parse(arena, state) {
|
||||||
.parse(arena, state)
|
|
||||||
{
|
|
||||||
Err((_, _, _)) => {
|
Err((_, _, _)) => {
|
||||||
// a hacky way to get expression-based error messages. TODO fix this
|
// a hacky way to get expression-based error messages. TODO fix this
|
||||||
return Ok((NoProgress, defs, initial));
|
return Ok((NoProgress, defs, initial));
|
||||||
}
|
}
|
||||||
Ok((_, _, state)) => {
|
|
||||||
|
Ok((_, expect_flavor, state)) => {
|
||||||
let parse_def_expr = space0_before_e(
|
let parse_def_expr = space0_before_e(
|
||||||
move |a, s| parse_loc_expr(min_indent + 1, a, s),
|
move |a, s| parse_loc_expr(min_indent + 1, a, s),
|
||||||
min_indent,
|
min_indent,
|
||||||
|
@ -677,10 +681,17 @@ fn parse_defs_end<'a>(
|
||||||
|
|
||||||
let preceding_comment = Region::new(spaces_before_current_start, start);
|
let preceding_comment = Region::new(spaces_before_current_start, start);
|
||||||
|
|
||||||
let value_def = ValueDef::Expect {
|
let value_def = match expect_flavor {
|
||||||
|
Either::First(_) => ValueDef::Expect {
|
||||||
condition: arena.alloc(loc_def_expr),
|
condition: arena.alloc(loc_def_expr),
|
||||||
preceding_comment,
|
preceding_comment,
|
||||||
|
},
|
||||||
|
Either::Second(_) => ValueDef::ExpectFx {
|
||||||
|
condition: arena.alloc(loc_def_expr),
|
||||||
|
preceding_comment,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
defs.push_value_def(value_def, region, spaces_before_current, &[]);
|
defs.push_value_def(value_def, region, spaces_before_current, &[]);
|
||||||
|
|
||||||
global_state = state;
|
global_state = state;
|
||||||
|
|
|
@ -5,5 +5,6 @@ pub const WHEN: &str = "when";
|
||||||
pub const AS: &str = "as";
|
pub const AS: &str = "as";
|
||||||
pub const IS: &str = "is";
|
pub const IS: &str = "is";
|
||||||
pub const EXPECT: &str = "expect";
|
pub const EXPECT: &str = "expect";
|
||||||
|
pub const EXPECT_FX: &str = "expect-fx";
|
||||||
|
|
||||||
pub const KEYWORDS: [&str; 7] = [IF, THEN, ELSE, WHEN, AS, IS, EXPECT];
|
pub const KEYWORDS: [&str; 8] = [IF, THEN, ELSE, WHEN, AS, IS, EXPECT, EXPECT_FX];
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@25-41,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 1),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 1, length = 1),
|
||||||
|
],
|
||||||
|
spaces: [
|
||||||
|
LineComment(
|
||||||
|
" expecting some effects",
|
||||||
|
),
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
ExpectFx {
|
||||||
|
condition: @35-41 BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@35-36 Num(
|
||||||
|
"5",
|
||||||
|
),
|
||||||
|
@37-39 Equals,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@40-41 Num(
|
||||||
|
"2",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
preceding_comment: @25-25,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
# expecting some effects
|
||||||
|
expect-fx 5 == 2
|
|
@ -251,6 +251,7 @@ mod test_parse {
|
||||||
pass/spaced_singleton_list.expr,
|
pass/spaced_singleton_list.expr,
|
||||||
pass/spaces_inside_empty_list.expr,
|
pass/spaces_inside_empty_list.expr,
|
||||||
pass/standalone_module_defs.module,
|
pass/standalone_module_defs.module,
|
||||||
|
pass/expect_fx.module,
|
||||||
pass/string_without_escape.expr,
|
pass/string_without_escape.expr,
|
||||||
pass/sub_var_with_spaces.expr,
|
pass/sub_var_with_spaces.expr,
|
||||||
pass/sub_with_spaces.expr,
|
pass/sub_with_spaces.expr,
|
||||||
|
|
|
@ -129,7 +129,7 @@ pub fn load_types(
|
||||||
// handled by future iterations
|
// handled by future iterations
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Expectation => {
|
Expectation | ExpectationFx => {
|
||||||
// not publicly visible
|
// not publicly visible
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue