Merge branch 'main' into constrain-early-return-functions

This commit is contained in:
Sam Mohr 2024-11-21 02:45:14 -08:00
commit 20ba4a92de
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
88 changed files with 155 additions and 518 deletions

View file

@ -872,7 +872,7 @@ fn build_loaded_file<'a>(
let built_host_opt =
// Not sure if this is correct for all calls with LinkType::Dylib...
if link_type == LinkType::Dylib || target == Target::Wasm32 {
if link_type == LinkType::None || link_type == LinkType::Dylib || target == Target::Wasm32 {
BuiltHostOpt::None
} else {
let prebuilt_host = determine_built_host_path(&platform_main_roc_path, target, build_host_requested, link_type, linking_strategy, suppress_build_host_warning);

View file

@ -62,7 +62,6 @@ fn print_declarations_help<'a>(
toplevel_function(c, f, symbol, function_def, &body.value)
}
DeclarationTag::Expectation => todo!(),
DeclarationTag::ExpectationFx => todo!(),
DeclarationTag::Destructure(_) => todo!(),
DeclarationTag::MutualRecursion { .. } => {
// the defs will be printed next

View file

@ -342,7 +342,6 @@ pub enum Declaration {
DeclareRec(Vec<Def>, IllegalCycleMark),
Builtin(Def),
Expects(ExpectsOrDbgs),
ExpectsFx(ExpectsOrDbgs),
/// If we know a cycle is illegal during canonicalization.
/// Otherwise we will try to detect this during solving; see [`IllegalCycleMark`].
InvalidCycle(Vec<CycleEntry>),
@ -357,7 +356,6 @@ impl Declaration {
InvalidCycle { .. } => 0,
Builtin(_) => 0,
Expects(_) => 0,
ExpectsFx(_) => 0,
}
}
@ -373,7 +371,7 @@ impl Declaration {
&cycles.first().unwrap().expr_region,
&cycles.last().unwrap().expr_region,
),
Declaration::Expects(expects) | Declaration::ExpectsFx(expects) => Region::span_across(
Declaration::Expects(expects) => Region::span_across(
expects.regions.first().unwrap(),
expects.regions.last().unwrap(),
),
@ -2835,10 +2833,6 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
loc_ret
}
Declaration::ExpectsFx(expects) => {
// Expects should only be added to top-level decls, not to let-exprs!
unreachable!("{:?}", &expects)
}
}
}

View file

@ -1100,15 +1100,6 @@ pub fn desugar_expr<'a>(
region: loc_expr.region,
})
}
Expect(condition, continuation) => {
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
let desugared_continuation = &*env.arena.alloc(desugar_expr(env, scope, continuation));
env.arena.alloc(Loc {
value: Expect(desugared_condition, desugared_continuation),
region: loc_expr.region,
})
}
Dbg => {
// Allow naked dbg, necessary for piping values into dbg with the `Pizza` binop
loc_expr

View file

@ -1188,36 +1188,6 @@ pub fn canonicalize_expr<'a>(
}
}
}
ast::Expr::Expect(condition, continuation) => {
let mut output = Output::default();
let (loc_condition, output1) =
canonicalize_expr(env, var_store, scope, condition.region, &condition.value);
// Get all the lookups that were referenced in the condition,
// so we can print their values later.
let lookups_in_cond = get_lookup_symbols(&loc_condition.value);
let (loc_continuation, output2) = canonicalize_expr(
env,
var_store,
scope,
continuation.region,
&continuation.value,
);
output.union(output1);
output.union(output2);
(
Expect {
loc_condition: Box::new(loc_condition),
loc_continuation: Box::new(loc_continuation),
lookups_in_cond,
},
output,
)
}
ast::Expr::Dbg => {
// Dbg was not desugared as either part of an `Apply` or a `Pizza` binop, so it's
// invalid.
@ -2534,7 +2504,6 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
// Newlines are disallowed inside interpolation, and these all require newlines
ast::Expr::DbgStmt(_, _)
| ast::Expr::LowLevelDbg(_, _, _)
| ast::Expr::Expect(_, _)
| ast::Expr::Return(_, _)
| ast::Expr::When(_, _)
| ast::Expr::Backpassing(_, _, _)
@ -3241,12 +3210,6 @@ impl Declarations {
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
}
ExpectationFx => {
let loc_expr =
toplevel_expect_to_inline_expect_fx(self.expressions[index].clone());
collector.visit_expr(&loc_expr.value, loc_expr.region, var);
}
}
}
@ -3265,7 +3228,6 @@ roc_error_macros::assert_sizeof_default!(DeclarationTag, 8);
pub enum DeclarationTag {
Value,
Expectation,
ExpectationFx,
Function(Index<Loc<FunctionDef>>),
Recursive(Index<Loc<FunctionDef>>),
TailRecursive(Index<Loc<FunctionDef>>),
@ -3283,7 +3245,7 @@ impl DeclarationTag {
match self {
Function(_) | Recursive(_) | TailRecursive(_) => 1,
Value => 1,
Expectation | ExpectationFx => 1,
Expectation => 1,
Destructure(_) => 1,
MutualRecursion { length, .. } => length as usize + 1,
}
@ -3484,15 +3446,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
/// This is supposed to happen just before monomorphization:
/// all type errors and such are generated from the user source,
/// but this transformation means that we don't need special codegen for toplevel expects
pub fn toplevel_expect_to_inline_expect_pure(loc_expr: Loc<Expr>) -> Loc<Expr> {
toplevel_expect_to_inline_expect_help(loc_expr, false)
}
pub fn toplevel_expect_to_inline_expect_fx(loc_expr: Loc<Expr>) -> Loc<Expr> {
toplevel_expect_to_inline_expect_help(loc_expr, true)
}
fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: bool) -> Loc<Expr> {
pub fn toplevel_expect_to_inline_expect_pure(mut loc_expr: Loc<Expr>) -> Loc<Expr> {
enum StoredDef {
NonRecursive(Region, Box<Def>),
Recursive(Region, Vec<Def>, IllegalCycleMark),
@ -3530,7 +3484,6 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: b
}
let expect_region = loc_expr.region;
assert!(!has_effects);
let expect = Expr::Expect {
loc_condition: Box::new(loc_expr),
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),

View file

@ -609,7 +609,6 @@ pub fn canonicalize_module_defs<'a>(
// the declarations of this group will be treaded individually by later iterations
}
Expectation => { /* ignore */ }
ExpectationFx => { /* ignore */ }
}
}
@ -747,14 +746,6 @@ pub fn canonicalize_module_defs<'a>(
&mut fix_closures_closure_captures,
);
}
ExpectationFx => {
let loc_expr = &mut declarations.expressions[index];
fix_values_captured_in_closure_expr(
&mut loc_expr.value,
&mut fix_closures_no_capture_symbols,
&mut fix_closures_closure_captures,
);
}
}
}

View file

@ -155,49 +155,6 @@ pub fn unwrap_suffixed_expression<'a>(
Expr::LowLevelDbg(..) => unwrap_low_level_dbg(arena, loc_expr, maybe_def_pat),
Expr::Expect(condition, continuation) => {
if is_expr_suffixed(&condition.value) {
// we cannot unwrap a suffixed expression within expect
// e.g. expect (foo! "bar")
return Err(EUnwrapped::Malformed);
}
match unwrap_suffixed_expression(arena, continuation, maybe_def_pat) {
Ok(unwrapped_expr) => {
let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
return Ok(new_expect);
}
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: unwrapped_expr,
target,
}) => {
let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
Err(EUnwrapped::UnwrappedDefExpr {
loc_expr: new_expect,
target,
})
}
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: unwrapped_expr,
sub_pat,
sub_new,
target,
}) => {
let new_expect = arena
.alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr)));
Err(EUnwrapped::UnwrappedSubExpr {
sub_arg: new_expect,
sub_pat,
sub_new,
target,
})
}
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
}
}
// we only need to unwrap some expressions, leave the rest as is
_ => Ok(loc_expr),
}

View file

@ -107,7 +107,7 @@ pub fn walk_decls<V: Visitor>(visitor: &mut V, decls: &Declarations) {
annotation: decls.annotations[index].as_ref(),
}
}
Expectation | ExpectationFx => {
Expectation => {
let loc_condition = &decls.expressions[index];
DeclarationInfo::Expectation { loc_condition }

View file

@ -2754,34 +2754,6 @@ pub fn constrain_decls(
expected,
);
constraint = constraints.let_constraint(
[],
[],
[],
expect_constraint,
constraint,
Generalizable(false),
)
}
ExpectationFx => {
let loc_expr = &declarations.expressions[index];
let bool_type = constraints.push_variable(Variable::BOOL);
let expected = constraints.push_expected_type(Expected::ForReason(
Reason::ExpectCondition,
bool_type,
loc_expr.region,
));
let expect_constraint = constrain_expr(
types,
constraints,
&mut env,
loc_expr.region,
&loc_expr.value,
expected,
);
constraint = constraints.let_constraint(
[],
[],

View file

@ -64,9 +64,6 @@ impl<'a> Formattable for Expr<'a> {
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
}
Expect(condition, continuation) => {
condition.is_multiline() || continuation.is_multiline()
}
DbgStmt(condition, _) => condition.is_multiline(),
LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting"
@ -445,9 +442,6 @@ impl<'a> Formattable for Expr<'a> {
buf.push(')');
}
}
Expect(condition, continuation) => {
fmt_expect(buf, condition, continuation, self.is_multiline(), indent);
}
Dbg => {
buf.indent(indent);
buf.push_str("dbg");
@ -1071,33 +1065,6 @@ fn fmt_dbg_stmt<'a>(
continuation.format(buf, indent);
}
fn fmt_expect<'a>(
buf: &mut Buf,
condition: &'a Loc<Expr<'a>>,
continuation: &'a Loc<Expr<'a>>,
is_multiline: bool,
indent: u16,
) {
buf.ensure_ends_with_newline();
buf.indent(indent);
buf.push_str("expect");
let return_indent = if is_multiline {
buf.newline();
indent + INDENT
} else {
buf.spaces(1);
indent
};
condition.format(buf, return_indent);
// Always put a blank line after the `expect` line(s)
buf.ensure_ends_with_blank_line();
continuation.format(buf, indent);
}
fn fmt_return<'a>(
buf: &mut Buf,
return_value: &'a Loc<Expr<'a>>,

View file

@ -2657,7 +2657,7 @@ fn update<'a>(
let subs = solved_subs.into_inner();
if !toplevel_expects.pure.is_empty() || !toplevel_expects.fx.is_empty() {
if !toplevel_expects.pure.is_empty() {
state.toplevel_expects.insert(module_id, toplevel_expects);
}
@ -6010,78 +6010,6 @@ fn build_pending_specializations<'a>(
toplevel_expects.pure.insert(symbol, region);
procs_base.partial_procs.insert(symbol, proc);
}
ExpectationFx => {
// skip expectations if we're not going to run them
if !build_expects {
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),
None,
expr_var,
);
}
let body = roc_can::expr::toplevel_expect_to_inline_expect_fx(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.fx.insert(symbol, region);
procs_base.partial_procs.insert(symbol, proc);
}
}
}

View file

@ -167,7 +167,6 @@ pub(crate) struct LateSpecializationsModule<'a> {
#[derive(Debug, Default)]
pub struct ToplevelExpects {
pub pure: VecMap<Symbol, Region>,
pub fx: VecMap<Symbol, Region>,
}
#[derive(Debug)]

View file

@ -359,10 +359,6 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st
// at least at the moment this does not happen
panic!("Unexpected expectation in module declarations");
}
ExpectationFx => {
// at least at the moment this does not happen
panic!("Unexpected expectation in module declarations");
}
};
}
@ -462,7 +458,7 @@ fn module_with_deps() {
def_count += 1;
}
MutualRecursion { .. } => { /* do nothing, not a def */ }
Expectation | ExpectationFx => { /* do nothing, not a def */ }
Expectation => { /* do nothing, not a def */ }
}
}

View file

@ -101,7 +101,7 @@ impl<'a> LowerParams<'a> {
self.lower_expr(&mut decls.expressions[index].value);
}
Destructure(_) | Expectation | ExpectationFx => {
Destructure(_) | Expectation => {
self.lower_expr(&mut decls.expressions[index].value);
}
MutualRecursion { .. } => {}

View file

@ -489,7 +489,6 @@ pub enum Expr<'a> {
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>),
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
Dbg,
DbgStmt(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
@ -671,7 +670,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
Expr::Tag(_) => false,
Expr::OpaqueRef(_) => false,
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
Expr::Expect(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::Dbg => false,
Expr::DbgStmt(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
@ -932,11 +930,6 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
expr_stack.push(&a.value);
expr_stack.push(&b.value);
}
Expect(condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
expr_stack.push(&cont.value);
}
DbgStmt(condition, cont) => {
expr_stack.reserve(2);
expr_stack.push(&condition.value);
@ -2482,7 +2475,6 @@ impl<'a> Malformed for Expr<'a> {
Closure(args, body) => args.iter().any(|arg| arg.is_malformed()) || body.is_malformed(),
Defs(defs, body) => defs.is_malformed() || body.is_malformed(),
Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(),
Expect(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
Dbg => false,
DbgStmt(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),

View file

@ -2175,7 +2175,6 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::Defs(_, _)
| Expr::If { .. }
| Expr::When(_, _)
| Expr::Expect(_, _)
| Expr::Dbg
| Expr::DbgStmt(_, _)
| Expr::LowLevelDbg(_, _, _)

View file

@ -735,10 +735,6 @@ impl<'a> Normalize<'a> for Expr<'a> {
arena.alloc(b.normalize(arena)),
arena.alloc(c.normalize(arena)),
),
Expr::Expect(a, b) => Expr::Expect(
arena.alloc(a.normalize(arena)),
arena.alloc(b.normalize(arena)),
),
Expr::Dbg => Expr::Dbg,
Expr::DbgStmt(a, b) => Expr::DbgStmt(
arena.alloc(a.normalize(arena)),

View file

@ -49,8 +49,8 @@ pub enum Architecture {
impl std::fmt::Display for Architecture {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let arch_str = match self {
Architecture::Aarch32 => "arm",
Architecture::Aarch64 => "arm64",
Architecture::Aarch32 => "aarch32",
Architecture::Aarch64 => "aarch64",
Architecture::Wasm32 => "wasm32",
Architecture::X86_32 => "x86_32",
Architecture::X86_64 => "x86_64",