mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'main' into specialize-exprs
This commit is contained in:
commit
2e96aca0fd
797 changed files with 17394 additions and 12632 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::abilities::SpecializationId;
|
||||
use crate::annotation::{freshen_opaque_def, IntroducedVariables};
|
||||
use crate::builtins::builtin_defs_map;
|
||||
use crate::def::{can_defs_with_return, Annotation, Def};
|
||||
use crate::def::{can_defs_with_return, Annotation, Def, DefKind};
|
||||
use crate::env::Env;
|
||||
use crate::num::{
|
||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result,
|
||||
|
@ -12,7 +12,7 @@ use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitS
|
|||
use crate::procedure::{QualifiedReference, References};
|
||||
use crate::scope::{Scope, SymbolLookup};
|
||||
use crate::traverse::{walk_expr, Visitor};
|
||||
use roc_collections::soa::Index;
|
||||
use roc_collections::soa::index_push_new;
|
||||
use roc_collections::{SendMap, VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::CalledVia;
|
||||
|
@ -27,6 +27,7 @@ use roc_region::all::{Loc, Region};
|
|||
use roc_types::num::SingleQuoteBound;
|
||||
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable};
|
||||
use roc_types::types::{Alias, Category, IndexOrField, LambdaSet, OptAbleVar, Type};
|
||||
use soa::Index;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -38,7 +39,7 @@ pub type PendingDerives = VecMap<Symbol, (Type, Vec<Loc<Symbol>>)>;
|
|||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Output {
|
||||
pub references: References,
|
||||
pub tail_call: Option<Symbol>,
|
||||
pub tail_calls: Vec<Symbol>,
|
||||
pub introduced_variables: IntroducedVariables,
|
||||
pub aliases: VecMap<Symbol, Alias>,
|
||||
pub non_closures: VecSet<Symbol>,
|
||||
|
@ -49,8 +50,8 @@ impl Output {
|
|||
pub fn union(&mut self, other: Self) {
|
||||
self.references.union_mut(&other.references);
|
||||
|
||||
if let (None, Some(later)) = (self.tail_call, other.tail_call) {
|
||||
self.tail_call = Some(later);
|
||||
if self.tail_calls.is_empty() && !other.tail_calls.is_empty() {
|
||||
self.tail_calls = other.tail_calls;
|
||||
}
|
||||
|
||||
self.introduced_variables
|
||||
|
@ -202,7 +203,7 @@ pub enum Expr {
|
|||
/// This is *only* for calling functions, not for tag application.
|
||||
/// The Tag variant contains any applied values inside it.
|
||||
Call(
|
||||
Box<(Variable, Loc<Expr>, Variable, Variable)>,
|
||||
Box<(Variable, Loc<Expr>, Variable, Variable, Variable)>,
|
||||
Vec<(Variable, Loc<Expr>)>,
|
||||
CalledVia,
|
||||
),
|
||||
|
@ -318,13 +319,6 @@ pub enum Expr {
|
|||
lookups_in_cond: Vec<ExpectLookup>,
|
||||
},
|
||||
|
||||
// not parsed, but is generated when lowering toplevel effectful expects
|
||||
ExpectFx {
|
||||
loc_condition: Box<Loc<Expr>>,
|
||||
loc_continuation: Box<Loc<Expr>>,
|
||||
lookups_in_cond: Vec<ExpectLookup>,
|
||||
},
|
||||
|
||||
Dbg {
|
||||
source_location: Box<str>,
|
||||
source: Box<str>,
|
||||
|
@ -334,6 +328,11 @@ pub enum Expr {
|
|||
symbol: Symbol,
|
||||
},
|
||||
|
||||
Return {
|
||||
return_value: Box<Loc<Expr>>,
|
||||
return_var: Variable,
|
||||
},
|
||||
|
||||
/// Rendered as empty box in editor
|
||||
TypedHole(Variable),
|
||||
|
||||
|
@ -406,8 +405,8 @@ impl Expr {
|
|||
Category::OpaqueWrap(opaque_name)
|
||||
}
|
||||
Self::Expect { .. } => Category::Expect,
|
||||
Self::ExpectFx { .. } => Category::Expect,
|
||||
Self::Crash { .. } => Category::Crash,
|
||||
Self::Return { .. } => Category::Return,
|
||||
|
||||
Self::Dbg { .. } => Category::Expect,
|
||||
|
||||
|
@ -448,6 +447,8 @@ pub struct ClosureData {
|
|||
pub function_type: Variable,
|
||||
pub closure_type: Variable,
|
||||
pub return_type: Variable,
|
||||
pub fx_type: Variable,
|
||||
pub early_returns: Vec<(Variable, Region)>,
|
||||
pub name: Symbol,
|
||||
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||
pub recursive: Recursive,
|
||||
|
@ -524,6 +525,8 @@ impl StructAccessorData {
|
|||
function_type: function_var,
|
||||
closure_type: closure_var,
|
||||
return_type: field_var,
|
||||
fx_type: Variable::PURE,
|
||||
early_returns: vec![],
|
||||
name,
|
||||
captured_symbols: vec![],
|
||||
recursive: Recursive::NotRecursive,
|
||||
|
@ -597,6 +600,8 @@ impl OpaqueWrapFunctionData {
|
|||
function_type: function_var,
|
||||
closure_type: closure_var,
|
||||
return_type: opaque_var,
|
||||
fx_type: Variable::PURE,
|
||||
early_returns: vec![],
|
||||
name: function_name,
|
||||
captured_symbols: vec![],
|
||||
recursive: Recursive::NotRecursive,
|
||||
|
@ -762,7 +767,6 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
let output = Output {
|
||||
references,
|
||||
tail_call: None,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -830,7 +834,6 @@ pub fn canonicalize_expr<'a>(
|
|||
|
||||
let output = Output {
|
||||
references,
|
||||
tail_call: None,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -947,17 +950,19 @@ pub fn canonicalize_expr<'a>(
|
|||
output.union(fn_expr_output);
|
||||
|
||||
// Default: We're not tail-calling a symbol (by name), we're tail-calling a function value.
|
||||
output.tail_call = None;
|
||||
output.tail_calls = vec![];
|
||||
|
||||
let expr = match fn_expr.value {
|
||||
Var(symbol, _) => {
|
||||
output.references.insert_call(symbol);
|
||||
|
||||
// we're tail-calling a symbol by name, check if it's the tail-callable symbol
|
||||
output.tail_call = match &env.tailcallable_symbol {
|
||||
Some(tc_sym) if *tc_sym == symbol => Some(symbol),
|
||||
Some(_) | None => None,
|
||||
};
|
||||
if env
|
||||
.tailcallable_symbol
|
||||
.is_some_and(|tc_sym| tc_sym == symbol)
|
||||
{
|
||||
output.tail_calls.push(symbol);
|
||||
}
|
||||
|
||||
Call(
|
||||
Box::new((
|
||||
|
@ -965,6 +970,7 @@ pub fn canonicalize_expr<'a>(
|
|||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
args,
|
||||
*application_style,
|
||||
|
@ -1004,6 +1010,7 @@ pub fn canonicalize_expr<'a>(
|
|||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
args,
|
||||
*application_style,
|
||||
|
@ -1056,7 +1063,7 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
ast::Expr::Defs(loc_defs, loc_ret) => {
|
||||
// The body expression gets a new scope for canonicalization,
|
||||
scope.inner_scope(|inner_scope| {
|
||||
scope.inner_def_scope(|inner_scope| {
|
||||
let defs: Defs = (*loc_defs).clone();
|
||||
can_defs_with_return(env, var_store, inner_scope, env.arena.alloc(defs), loc_ret)
|
||||
})
|
||||
|
@ -1083,12 +1090,12 @@ pub fn canonicalize_expr<'a>(
|
|||
canonicalize_expr(env, var_store, scope, loc_cond.region, &loc_cond.value);
|
||||
|
||||
// the condition can never be a tail-call
|
||||
output.tail_call = None;
|
||||
output.tail_calls = vec![];
|
||||
|
||||
let mut can_branches = Vec::with_capacity(branches.len());
|
||||
|
||||
for branch in branches.iter() {
|
||||
let (can_when_branch, branch_references) = scope.inner_scope(|inner_scope| {
|
||||
let (can_when_branch, branch_references) = scope.inner_def_scope(|inner_scope| {
|
||||
canonicalize_when_branch(
|
||||
env,
|
||||
var_store,
|
||||
|
@ -1108,7 +1115,7 @@ pub fn canonicalize_expr<'a>(
|
|||
// if code gen mistakenly thinks this is a tail call just because its condition
|
||||
// happened to be one. (The condition gave us our initial output value.)
|
||||
if branches.is_empty() {
|
||||
output.tail_call = None;
|
||||
output.tail_calls = vec![];
|
||||
}
|
||||
|
||||
// Incorporate all three expressions into a combined Output value.
|
||||
|
@ -1170,6 +1177,11 @@ pub fn canonicalize_expr<'a>(
|
|||
ast::Expr::TrySuffix { .. } => internal_error!(
|
||||
"a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"
|
||||
),
|
||||
ast::Expr::Try => {
|
||||
// Treat remaining `try` keywords as normal variables so that we can continue to support `Result.try`
|
||||
canonicalize_var_lookup(env, var_store, scope, "", "try", region)
|
||||
}
|
||||
|
||||
ast::Expr::Tag(tag) => {
|
||||
let variant_var = var_store.fresh();
|
||||
let ext_var = var_store.fresh();
|
||||
|
@ -1224,36 +1236,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.
|
||||
|
@ -1306,6 +1288,40 @@ pub fn canonicalize_expr<'a>(
|
|||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::Return(return_expr, after_return) => {
|
||||
let mut output = Output::default();
|
||||
|
||||
if let Some(after_return) = after_return {
|
||||
let region_with_return =
|
||||
Region::span_across(&return_expr.region, &after_return.region);
|
||||
|
||||
env.problem(Problem::StatementsAfterReturn {
|
||||
region: region_with_return,
|
||||
});
|
||||
}
|
||||
|
||||
let (loc_return_expr, output1) = canonicalize_expr(
|
||||
env,
|
||||
var_store,
|
||||
scope,
|
||||
return_expr.region,
|
||||
&return_expr.value,
|
||||
);
|
||||
|
||||
output.union(output1);
|
||||
|
||||
let return_var = var_store.fresh();
|
||||
|
||||
scope.early_returns.push((return_var, return_expr.region));
|
||||
|
||||
(
|
||||
Return {
|
||||
return_value: Box::new(loc_return_expr),
|
||||
return_var,
|
||||
},
|
||||
output,
|
||||
)
|
||||
}
|
||||
ast::Expr::If {
|
||||
if_thens,
|
||||
final_else: final_else_branch,
|
||||
|
@ -1385,10 +1401,6 @@ pub fn canonicalize_expr<'a>(
|
|||
Output::default(),
|
||||
)
|
||||
}
|
||||
ast::Expr::MalformedClosure => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
(RuntimeError(MalformedClosure(region)), Output::default())
|
||||
}
|
||||
ast::Expr::MalformedIdent(name, bad_ident) => {
|
||||
use roc_problem::can::RuntimeError::*;
|
||||
|
||||
|
@ -1541,7 +1553,7 @@ pub fn canonicalize_closure<'a>(
|
|||
loc_body_expr: &'a Loc<ast::Expr<'a>>,
|
||||
opt_def_name: Option<Symbol>,
|
||||
) -> (ClosureData, Output) {
|
||||
scope.inner_scope(|inner_scope| {
|
||||
scope.inner_function_scope(|inner_scope| {
|
||||
canonicalize_closure_body(
|
||||
env,
|
||||
var_store,
|
||||
|
@ -1656,6 +1668,17 @@ fn canonicalize_closure_body<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
let mut final_expr = &loc_body_expr;
|
||||
while let Expr::LetRec(_, inner, _) | Expr::LetNonRec(_, inner) = &final_expr.value {
|
||||
final_expr = inner;
|
||||
}
|
||||
|
||||
if let Expr::Return { return_value, .. } = &final_expr.value {
|
||||
env.problem(Problem::ReturnAtEndOfFunction {
|
||||
region: return_value.region,
|
||||
});
|
||||
}
|
||||
|
||||
// store the references of this function in the Env. This information is used
|
||||
// when we canonicalize a surrounding def (if it exists)
|
||||
env.closures.insert(symbol, output.references.clone());
|
||||
|
@ -1669,10 +1692,14 @@ fn canonicalize_closure_body<'a>(
|
|||
output.non_closures.insert(symbol);
|
||||
}
|
||||
|
||||
let return_type_var = var_store.fresh();
|
||||
|
||||
let closure_data = ClosureData {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
return_type: return_type_var,
|
||||
fx_type: var_store.fresh(),
|
||||
early_returns: scope.early_returns.clone(),
|
||||
name: symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
|
@ -1955,10 +1982,6 @@ fn canonicalize_field<'a>(
|
|||
SpaceBefore(sub_field, _) | SpaceAfter(sub_field, _) => {
|
||||
canonicalize_field(env, var_store, scope, sub_field)
|
||||
}
|
||||
|
||||
Malformed(_string) => {
|
||||
internal_error!("TODO canonicalize malformed record field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2075,7 +2098,8 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
| other @ TypedHole { .. }
|
||||
| other @ ForeignCall { .. }
|
||||
| other @ OpaqueWrapFunction(_)
|
||||
| other @ Crash { .. } => other,
|
||||
| other @ Crash { .. }
|
||||
| other @ Return { .. } => other,
|
||||
|
||||
List {
|
||||
elem_var,
|
||||
|
@ -2203,28 +2227,6 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
}
|
||||
}
|
||||
|
||||
ExpectFx {
|
||||
loc_condition,
|
||||
loc_continuation,
|
||||
lookups_in_cond,
|
||||
} => {
|
||||
let loc_condition = Loc {
|
||||
region: loc_condition.region,
|
||||
value: inline_calls(var_store, loc_condition.value),
|
||||
};
|
||||
|
||||
let loc_continuation = Loc {
|
||||
region: loc_continuation.region,
|
||||
value: inline_calls(var_store, loc_continuation.value),
|
||||
};
|
||||
|
||||
ExpectFx {
|
||||
loc_condition: Box::new(loc_condition),
|
||||
loc_continuation: Box::new(loc_continuation),
|
||||
lookups_in_cond,
|
||||
}
|
||||
}
|
||||
|
||||
Dbg {
|
||||
source_location,
|
||||
source,
|
||||
|
@ -2266,6 +2268,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
expr_var: def.expr_var,
|
||||
pattern_vars: def.pattern_vars,
|
||||
annotation: def.annotation,
|
||||
kind: def.kind,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2287,6 +2290,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
expr_var: def.expr_var,
|
||||
pattern_vars: def.pattern_vars,
|
||||
annotation: def.annotation,
|
||||
kind: def.kind,
|
||||
};
|
||||
|
||||
let loc_expr = Loc {
|
||||
|
@ -2301,6 +2305,8 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
fx_type,
|
||||
early_returns,
|
||||
recursive,
|
||||
name,
|
||||
captured_symbols,
|
||||
|
@ -2317,6 +2323,8 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
function_type,
|
||||
closure_type,
|
||||
return_type,
|
||||
fx_type,
|
||||
early_returns,
|
||||
recursive,
|
||||
name,
|
||||
captured_symbols,
|
||||
|
@ -2424,10 +2432,11 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
}
|
||||
|
||||
Call(boxed_tuple, args, called_via) => {
|
||||
let (fn_var, loc_expr, closure_var, expr_var) = *boxed_tuple;
|
||||
let (fn_var, loc_expr, closure_var, expr_var, fx_var) = *boxed_tuple;
|
||||
|
||||
match loc_expr.value {
|
||||
Var(symbol, _) if symbol.is_builtin() => {
|
||||
// NOTE: This assumes builtins are not effectful!
|
||||
match builtin_defs_map(symbol, var_store) {
|
||||
Some(Def {
|
||||
loc_expr:
|
||||
|
@ -2471,6 +2480,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
expr_var,
|
||||
pattern_vars,
|
||||
annotation: None,
|
||||
kind: DefKind::Let,
|
||||
};
|
||||
|
||||
loc_answer = Loc {
|
||||
|
@ -2495,7 +2505,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
_ => {
|
||||
// For now, we only inline calls to builtins. Leave this alone!
|
||||
Call(
|
||||
Box::new((fn_var, loc_expr, closure_var, expr_var)),
|
||||
Box::new((fn_var, loc_expr, closure_var, expr_var, fx_var)),
|
||||
args,
|
||||
called_via,
|
||||
)
|
||||
|
@ -2534,15 +2544,15 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::Expr::RecordUpdater(_)
|
||||
| ast::Expr::Crash
|
||||
| ast::Expr::Dbg
|
||||
| ast::Expr::Try
|
||||
| ast::Expr::Underscore(_)
|
||||
| ast::Expr::MalformedIdent(_, _)
|
||||
| ast::Expr::Tag(_)
|
||||
| ast::Expr::OpaqueRef(_)
|
||||
| ast::Expr::MalformedClosure => true,
|
||||
| ast::Expr::OpaqueRef(_) => true,
|
||||
// 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(_, _, _)
|
||||
| ast::Expr::SpaceBefore(_, _)
|
||||
|
@ -2563,9 +2573,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::StrSegment::Plaintext(_) => true,
|
||||
// Disallow nested interpolation. Alternatively, we could allow it but require
|
||||
// a comment above it apologizing to the next person who has to read the code.
|
||||
ast::StrSegment::Interpolated(_) | ast::StrSegment::DeprecatedInterpolated(_) => {
|
||||
false
|
||||
}
|
||||
ast::StrSegment::Interpolated(_) => false,
|
||||
})
|
||||
}
|
||||
ast::Expr::Record(fields) => fields.iter().all(|loc_field| match loc_field.value {
|
||||
|
@ -2574,7 +2582,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _) | ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
}),
|
||||
ast::Expr::Tuple(fields) => fields
|
||||
|
@ -2625,7 +2633,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _)
|
||||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
|
@ -2638,7 +2646,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
|||
| ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => {
|
||||
loc_comments.is_empty() && is_valid_interpolation(&loc_val.value)
|
||||
}
|
||||
ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::LabelOnly(_) => true,
|
||||
ast::AssignedField::SpaceBefore(_, _)
|
||||
| ast::AssignedField::SpaceAfter(_, _) => false,
|
||||
})
|
||||
|
@ -2696,7 +2704,7 @@ fn flatten_str_lines<'a>(
|
|||
);
|
||||
}
|
||||
},
|
||||
Interpolated(loc_expr) | DeprecatedInterpolated(loc_expr) => {
|
||||
Interpolated(loc_expr) => {
|
||||
if is_valid_interpolation(loc_expr.value) {
|
||||
// Interpolations desugar to Str.concat calls
|
||||
output.references.insert_call(Symbol::STR_CONCAT);
|
||||
|
@ -2763,6 +2771,7 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec<StrSegment>) ->
|
|||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
vec![
|
||||
(var_store.fresh(), empty_string),
|
||||
|
@ -2799,6 +2808,7 @@ fn desugar_str_segments(var_store: &mut VarStore, segments: Vec<StrSegment>) ->
|
|||
fn_expr,
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
)),
|
||||
vec![
|
||||
(var_store.fresh(), loc_new_expr),
|
||||
|
@ -2845,6 +2855,7 @@ impl Declarations {
|
|||
pub fn new() -> Self {
|
||||
Self::with_capacity(0)
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
declarations: Vec::with_capacity(capacity),
|
||||
|
@ -2891,6 +2902,8 @@ impl Declarations {
|
|||
let function_def = FunctionDef {
|
||||
closure_type: loc_closure_data.value.closure_type,
|
||||
return_type: loc_closure_data.value.return_type,
|
||||
fx_type: loc_closure_data.value.fx_type,
|
||||
early_returns: loc_closure_data.value.early_returns,
|
||||
captured_symbols: loc_closure_data.value.captured_symbols,
|
||||
arguments: loc_closure_data.value.arguments,
|
||||
};
|
||||
|
@ -2900,7 +2913,7 @@ impl Declarations {
|
|||
|
||||
let loc_function_def = Loc::at(loc_closure_data.region, function_def);
|
||||
|
||||
let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def);
|
||||
let function_def_index = index_push_new(&mut self.function_bodies, loc_function_def);
|
||||
|
||||
let tag = match loc_closure_data.value.recursive {
|
||||
Recursive::NotRecursive | Recursive::Recursive => {
|
||||
|
@ -2942,6 +2955,8 @@ impl Declarations {
|
|||
let function_def = FunctionDef {
|
||||
closure_type: loc_closure_data.value.closure_type,
|
||||
return_type: loc_closure_data.value.return_type,
|
||||
fx_type: loc_closure_data.value.fx_type,
|
||||
early_returns: loc_closure_data.value.early_returns,
|
||||
captured_symbols: loc_closure_data.value.captured_symbols,
|
||||
arguments: loc_closure_data.value.arguments,
|
||||
};
|
||||
|
@ -2951,7 +2966,7 @@ impl Declarations {
|
|||
|
||||
let loc_function_def = Loc::at(loc_closure_data.region, function_def);
|
||||
|
||||
let function_def_index = Index::push_new(&mut self.function_bodies, loc_function_def);
|
||||
let function_def_index = index_push_new(&mut self.function_bodies, loc_function_def);
|
||||
|
||||
if let Some(annotation) = host_annotation {
|
||||
self.host_exposed_annotations
|
||||
|
@ -2991,24 +3006,6 @@ impl Declarations {
|
|||
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::ExpectationFx);
|
||||
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(
|
||||
&mut self,
|
||||
symbol: Loc<Symbol>,
|
||||
|
@ -3057,7 +3054,7 @@ impl Declarations {
|
|||
pattern_vars,
|
||||
};
|
||||
|
||||
let destructure_def_index = Index::push_new(&mut self.destructs, destruct_def);
|
||||
let destructure_def_index = index_push_new(&mut self.destructs, destruct_def);
|
||||
|
||||
self.declarations
|
||||
.push(DeclarationTag::Destructure(destructure_def_index));
|
||||
|
@ -3122,6 +3119,8 @@ impl Declarations {
|
|||
let function_def = FunctionDef {
|
||||
closure_type: closure_data.closure_type,
|
||||
return_type: closure_data.return_type,
|
||||
fx_type: closure_data.fx_type,
|
||||
early_returns: closure_data.early_returns,
|
||||
captured_symbols: closure_data.captured_symbols,
|
||||
arguments: closure_data.arguments,
|
||||
};
|
||||
|
@ -3129,7 +3128,7 @@ impl Declarations {
|
|||
let loc_function_def = Loc::at(def.loc_expr.region, function_def);
|
||||
|
||||
let function_def_index =
|
||||
Index::push_new(&mut self.function_bodies, loc_function_def);
|
||||
index_push_new(&mut self.function_bodies, loc_function_def);
|
||||
|
||||
self.declarations[index] = DeclarationTag::Function(function_def_index);
|
||||
self.expressions[index] = *closure_data.loc_body;
|
||||
|
@ -3162,6 +3161,8 @@ impl Declarations {
|
|||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
fx_type: var_store.fresh(),
|
||||
early_returns: vec![],
|
||||
name: self.symbols[index].value,
|
||||
captured_symbols: vec![],
|
||||
recursive: Recursive::NotRecursive,
|
||||
|
@ -3174,6 +3175,8 @@ impl Declarations {
|
|||
let function_def = FunctionDef {
|
||||
closure_type: loc_closure_data.value.closure_type,
|
||||
return_type: loc_closure_data.value.return_type,
|
||||
fx_type: loc_closure_data.value.fx_type,
|
||||
early_returns: loc_closure_data.value.early_returns,
|
||||
captured_symbols: loc_closure_data.value.captured_symbols,
|
||||
arguments: loc_closure_data.value.arguments,
|
||||
};
|
||||
|
@ -3181,7 +3184,7 @@ impl Declarations {
|
|||
let loc_function_def = Loc::at(region, function_def);
|
||||
|
||||
let function_def_index =
|
||||
Index::push_new(&mut self.function_bodies, loc_function_def);
|
||||
index_push_new(&mut self.function_bodies, loc_function_def);
|
||||
|
||||
if let Some(annotation) = &mut self.annotations[index] {
|
||||
annotation.convert_to_fn(new_args_len, var_store);
|
||||
|
@ -3255,12 +3258,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3279,7 +3276,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>>),
|
||||
|
@ -3297,7 +3293,7 @@ impl DeclarationTag {
|
|||
match self {
|
||||
Function(_) | Recursive(_) | TailRecursive(_) => 1,
|
||||
Value => 1,
|
||||
Expectation | ExpectationFx => 1,
|
||||
Expectation => 1,
|
||||
Destructure(_) => 1,
|
||||
MutualRecursion { length, .. } => length as usize + 1,
|
||||
}
|
||||
|
@ -3308,6 +3304,8 @@ impl DeclarationTag {
|
|||
pub struct FunctionDef {
|
||||
pub closure_type: Variable,
|
||||
pub return_type: Variable,
|
||||
pub fx_type: Variable,
|
||||
pub early_returns: Vec<(Variable, Region)>,
|
||||
pub captured_symbols: Vec<(Symbol, Variable)>,
|
||||
pub arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
|
||||
}
|
||||
|
@ -3441,9 +3439,6 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
Expr::Expect {
|
||||
loc_continuation, ..
|
||||
}
|
||||
| Expr::ExpectFx {
|
||||
loc_continuation, ..
|
||||
}
|
||||
| Expr::Dbg {
|
||||
loc_continuation, ..
|
||||
} => {
|
||||
|
@ -3452,6 +3447,9 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
// Intentionally ignore the lookups in the nested `expect` condition itself,
|
||||
// because they couldn't possibly influence the outcome of this `expect`!
|
||||
}
|
||||
Expr::Return { return_value, .. } => {
|
||||
stack.push(&return_value.value);
|
||||
}
|
||||
Expr::Crash { msg, .. } => stack.push(&msg.value),
|
||||
Expr::Num(_, _, _, _)
|
||||
| Expr::Float(_, _, _, _, _)
|
||||
|
@ -3496,15 +3494,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),
|
||||
|
@ -3542,18 +3532,10 @@ fn toplevel_expect_to_inline_expect_help(mut loc_expr: Loc<Expr>, has_effects: b
|
|||
}
|
||||
|
||||
let expect_region = loc_expr.region;
|
||||
let expect = if has_effects {
|
||||
Expr::ExpectFx {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
lookups_in_cond,
|
||||
}
|
||||
} else {
|
||||
Expr::Expect {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
lookups_in_cond,
|
||||
}
|
||||
let expect = Expr::Expect {
|
||||
loc_condition: Box::new(loc_expr),
|
||||
loc_continuation: Box::new(Loc::at_zero(Expr::EmptyRecord)),
|
||||
lookups_in_cond,
|
||||
};
|
||||
|
||||
let mut loc_expr = Loc::at(expect_region, expect);
|
||||
|
@ -3585,11 +3567,6 @@ impl crate::traverse::Visitor for ExpectCollector {
|
|||
lookups_in_cond,
|
||||
loc_condition,
|
||||
..
|
||||
}
|
||||
| Expr::ExpectFx {
|
||||
lookups_in_cond,
|
||||
loc_condition,
|
||||
..
|
||||
} => {
|
||||
self.expects
|
||||
.insert(loc_condition.region, lookups_in_cond.to_vec());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue