Implement return keyword

This commit is contained in:
Sam Mohr 2024-10-20 04:50:12 -07:00
parent 20a539a96d
commit b3e60f9d3a
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
39 changed files with 594 additions and 80 deletions

View file

@ -446,6 +446,7 @@ fn defn_help(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: ret_var,
early_returns: vec![],
name: fn_name,
captured_symbols: Vec::new(),
recursive: Recursive::NotRecursive,

View file

@ -96,6 +96,7 @@ impl Constraints {
Category::List,
Category::Str,
Category::Character,
Category::Return,
]);
pattern_categories.extend([
@ -149,6 +150,7 @@ impl Constraints {
pub const CATEGORY_LIST: Index<Category> = Index::new(11);
pub const CATEGORY_STR: Index<Category> = Index::new(12);
pub const CATEGORY_CHARACTER: Index<Category> = Index::new(13);
pub const CATEGORY_RETURN: Index<Category> = Index::new(14);
pub const PCATEGORY_RECORD: Index<PatternCategory> = Index::new(0);
pub const PCATEGORY_EMPTYRECORD: Index<PatternCategory> = Index::new(1);

View file

@ -455,6 +455,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
function_type,
closure_type,
return_type,
early_returns,
name,
captured_symbols,
recursive,
@ -464,6 +465,10 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
function_type: sub!(*function_type),
closure_type: sub!(*closure_type),
return_type: sub!(*return_type),
early_returns: early_returns
.iter()
.map(|(var, region)| (sub!(*var), *region))
.collect(),
name: *name,
captured_symbols: captured_symbols
.iter()
@ -687,6 +692,14 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
lookups_in_cond: lookups_in_cond.to_vec(),
},
Return {
return_value,
return_var,
} => Return {
return_value: Box::new(return_value.map(|e| go_help!(e))),
return_var: sub!(*return_var),
},
Dbg {
source_location,
source,

View file

@ -449,6 +449,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
Dbg { .. } => todo!(),
Expect { .. } => todo!(),
ExpectFx { .. } => todo!(),
Return { .. } => todo!(),
TypedHole(_) => todo!(),
RuntimeError(_) => todo!(),
}

View file

@ -134,31 +134,32 @@ impl Annotation {
#[derive(Debug)]
pub(crate) struct CanDefs {
defs: Vec<Option<Def>>,
dbgs: ExpectsOrDbgs,
expects: ExpectsOrDbgs,
expects_fx: ExpectsOrDbgs,
dbgs: OrderDependentStatements,
expects: OrderDependentStatements,
expects_fx: OrderDependentStatements,
returns: OrderDependentStatements,
def_ordering: DefOrdering,
aliases: VecMap<Symbol, Alias>,
}
#[derive(Clone, Debug)]
pub struct ExpectsOrDbgs {
pub conditions: Vec<Expr>,
pub struct OrderDependentStatements {
pub expressions: Vec<Expr>,
pub regions: Vec<Region>,
pub preceding_comment: Vec<Region>,
}
impl ExpectsOrDbgs {
impl OrderDependentStatements {
fn with_capacity(capacity: usize) -> Self {
Self {
conditions: Vec::with_capacity(capacity),
expressions: Vec::with_capacity(capacity),
regions: 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.expressions.push(loc_can_condition.value);
self.regions.push(loc_can_condition.region);
self.preceding_comment.push(preceding_comment);
}
@ -303,9 +304,10 @@ impl PendingTypeDef<'_> {
pub enum Declaration {
Declare(Def),
DeclareRec(Vec<Def>, IllegalCycleMark),
Return(Expr, Region),
Builtin(Def),
Expects(ExpectsOrDbgs),
ExpectsFx(ExpectsOrDbgs),
Expects(OrderDependentStatements),
ExpectsFx(OrderDependentStatements),
/// If we know a cycle is illegal during canonicalization.
/// Otherwise we will try to detect this during solving; see [`IllegalCycleMark`].
InvalidCycle(Vec<CycleEntry>),
@ -317,6 +319,7 @@ impl Declaration {
match self {
Declare(_) => 1,
DeclareRec(defs, _) => defs.len(),
Return(_, _) => 0,
InvalidCycle { .. } => 0,
Builtin(_) => 0,
Expects(_) => 0,
@ -340,6 +343,7 @@ impl Declaration {
expects.regions.first().unwrap(),
expects.regions.last().unwrap(),
),
Declaration::Return(_return_expr, return_region) => *return_region,
}
}
}
@ -1137,6 +1141,7 @@ fn canonicalize_value_defs<'a>(
let mut pending_dbgs = 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());
let mut pending_returns = Vec::with_capacity(value_defs.len());
let mut imports_introduced = Vec::with_capacity(value_defs.len());
@ -1159,6 +1164,9 @@ fn canonicalize_value_defs<'a>(
PendingValue::ExpectFx(pending_expect) => {
pending_expect_fx.push(pending_expect);
}
PendingValue::Return(pending_return) => {
pending_returns.push(pending_return);
}
PendingValue::ModuleImport(PendingModuleImport {
module_id,
region,
@ -1235,9 +1243,10 @@ fn canonicalize_value_defs<'a>(
def_ordering.insert_symbol_references(def_id as u32, &temp_output.references)
}
let mut dbgs = ExpectsOrDbgs::with_capacity(pending_dbgs.len());
let mut expects = ExpectsOrDbgs::with_capacity(pending_expects.len());
let mut expects_fx = ExpectsOrDbgs::with_capacity(pending_expects.len());
let mut dbgs = OrderDependentStatements::with_capacity(pending_dbgs.len());
let mut expects = OrderDependentStatements::with_capacity(pending_expects.len());
let mut expects_fx = OrderDependentStatements::with_capacity(pending_expects.len());
let mut returns = OrderDependentStatements::with_capacity(pending_returns.len());
for pending in pending_dbgs {
let (loc_can_condition, can_output) = canonicalize_expr(
@ -1281,11 +1290,21 @@ fn canonicalize_value_defs<'a>(
output.union(can_output);
}
for pending in pending_returns {
let (loc_return_expr, can_output) =
canonicalize_expr(env, var_store, scope, pending.region, &pending.value);
returns.push(loc_return_expr, Region::zero());
output.union(can_output);
}
let can_defs = CanDefs {
defs,
dbgs,
expects,
expects_fx,
returns,
def_ordering,
aliases,
};
@ -1681,7 +1700,7 @@ impl DefOrdering {
}
#[inline(always)]
pub(crate) fn sort_can_defs_new(
pub(crate) fn sort_top_level_can_defs(
env: &mut Env<'_>,
scope: &mut Scope,
var_store: &mut VarStore,
@ -1694,10 +1713,17 @@ pub(crate) fn sort_can_defs_new(
dbgs: _,
expects,
expects_fx,
returns,
def_ordering,
aliases,
} = defs;
for return_region in returns.regions {
env.problem(Problem::ReturnOutsideOfFunction {
region: return_region,
});
}
// TODO: inefficient, but I want to make this what CanDefs contains in the future
let mut defs: Vec<_> = defs.into_iter().map(|x| x.unwrap()).collect();
@ -1712,7 +1738,7 @@ pub(crate) fn sort_can_defs_new(
// because of the ordering of declarations, expects should come first because they are
// independent, but can rely on all other top-level symbols in the module
let it = expects
.conditions
.expressions
.into_iter()
.zip(expects.regions)
.zip(expects.preceding_comment);
@ -1725,7 +1751,7 @@ pub(crate) fn sort_can_defs_new(
}
let it = expects_fx
.conditions
.expressions
.into_iter()
.zip(expects_fx.regions)
.zip(expects_fx.preceding_comment);
@ -1829,6 +1855,7 @@ pub(crate) fn sort_can_defs_new(
None,
);
}
// TODO: do I need to handle returns here?
_ => {
declarations.push_value_def(
Loc::at(def.loc_pattern.region, symbol),
@ -1975,6 +2002,7 @@ pub(crate) fn sort_can_defs(
dbgs,
expects,
expects_fx,
returns,
def_ordering,
aliases,
} = defs;
@ -2100,18 +2128,24 @@ pub(crate) fn sort_can_defs(
}
}
if !dbgs.conditions.is_empty() {
if !dbgs.expressions.is_empty() {
declarations.push(Declaration::Expects(dbgs));
}
if !expects.conditions.is_empty() {
if !expects.expressions.is_empty() {
declarations.push(Declaration::Expects(expects));
}
if !expects_fx.conditions.is_empty() {
if !expects_fx.expressions.is_empty() {
declarations.push(Declaration::ExpectsFx(expects_fx));
}
if !returns.expressions.is_empty() {
for (return_expr, return_region) in returns.expressions.into_iter().zip(returns.regions) {
declarations.push(Declaration::Return(return_expr, return_region));
}
}
(declarations, output)
}
@ -2354,6 +2388,7 @@ fn canonicalize_pending_value_def<'a>(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
early_returns: scope.early_returns.clone(),
name: symbol,
captured_symbols: Vec::new(),
recursive: Recursive::NotRecursive,
@ -2571,6 +2606,8 @@ fn canonicalize_pending_body<'a>(
loc_value = value;
}
let expr_var = var_store.fresh();
// We treat closure definitions `foo = \a, b -> ...` differently from other body expressions,
// because they need more bookkeeping (for tail calls, closure captures, etc.)
//
@ -2664,7 +2701,6 @@ fn canonicalize_pending_body<'a>(
}
};
let expr_var = var_store.fresh();
let mut vars_by_symbol = SendMap::default();
pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var);
@ -2731,7 +2767,7 @@ pub fn can_defs_with_return<'a>(
let mut loc_expr: Loc<Expr> = ret_expr;
for declaration in declarations.into_iter().rev() {
loc_expr = decl_to_let(declaration, loc_expr);
loc_expr = decl_to_let_or_return(declaration, loc_expr, var_store);
}
(loc_expr.value, output)
@ -2758,7 +2794,11 @@ pub fn report_unused_imports(
}
}
fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
fn decl_to_let_or_return<'a>(
decl: Declaration,
loc_ret: Loc<Expr>,
var_store: &mut VarStore,
) -> Loc<Expr> {
match decl {
Declaration::Declare(def) => {
let region = Region::span_across(&def.loc_pattern.region, &loc_ret.region);
@ -2770,6 +2810,17 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
let expr = Expr::LetRec(defs, Box::new(loc_ret), cycle_mark);
Loc::at(region, expr)
}
Declaration::Return(return_expr, return_region) => {
let region = Region::span_across(&return_region, &loc_ret.region);
let return_var = var_store.fresh();
let expr = Expr::Return {
return_value: Box::new(Loc::at(return_region, return_expr)),
return_var,
};
Loc::at(region, expr)
}
Declaration::InvalidCycle(entries) => {
Loc::at_zero(Expr::RuntimeError(RuntimeError::CircularDef(entries)))
}
@ -2780,7 +2831,7 @@ fn decl_to_let(decl: Declaration, loc_ret: Loc<Expr>) -> Loc<Expr> {
Declaration::Expects(expects) => {
let mut loc_ret = loc_ret;
let conditions = expects.conditions.into_iter().rev();
let conditions = expects.expressions.into_iter().rev();
let condition_regions = expects.regions.into_iter().rev();
let expect_regions = expects.preceding_comment.into_iter().rev();
@ -3005,6 +3056,7 @@ enum PendingValue<'a> {
Dbg(PendingExpectOrDbg<'a>),
Expect(PendingExpectOrDbg<'a>),
ExpectFx(PendingExpectOrDbg<'a>),
Return(&'a Loc<ast::Expr<'a>>),
ModuleImport(PendingModuleImport<'a>),
SignatureDefMismatch,
InvalidIngestedFile,
@ -3154,6 +3206,8 @@ fn to_pending_value_def<'a>(
preceding_comment: *preceding_comment,
}),
Return(return_expr) => PendingValue::Return(return_expr),
ModuleImport(module_import) => {
let qualified_module_name: QualifiedModuleName = module_import.name.value.into();
let module_name = qualified_module_name.module.clone();

View file

@ -177,6 +177,12 @@ fn desugar_value_def<'a>(
body_expr: desugar_expr(env, scope, stmt_expr),
}
}
Return(return_expr) => {
let desugared_return_expr = &*env.arena.alloc(desugar_expr(env, scope, return_expr));
Return(&desugared_return_expr)
}
}
}
@ -315,9 +321,15 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
}
},
// TODO support desugaring of Dbg, Expect, and ExpectFx
// TODO support desugaring of Dbg and ExpectFx
Dbg { .. } | ExpectFx { .. } => value_def,
ModuleImport { .. } | IngestedFileImport(_) => value_def,
Return(ret_expr) => match unwrap_suffixed_expression(arena, ret_expr, None) {
Ok(new_ret_expr) => ValueDef::Return(new_ret_expr),
Err(..) => {
internal_error!("Unable to desugar the suffix inside a Return value def");
}
},
Stmt(..) => {
internal_error!(
@ -1008,6 +1020,7 @@ pub fn desugar_expr<'a>(
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,
@ -1019,7 +1032,6 @@ pub fn desugar_expr<'a>(
}
DbgStmt(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 {
@ -1027,6 +1039,16 @@ pub fn desugar_expr<'a>(
region: loc_expr.region,
})
}
Return(return_value, after_return) => {
let desugared_return_value = &*env.arena.alloc(desugar_expr(env, scope, return_value));
let desugared_after_return =
after_return.map(|ar| *env.arena.alloc(desugar_expr(env, scope, ar)));
env.arena.alloc(Loc {
value: Return(desugared_return_value, desugared_after_return),
region: loc_expr.region,
})
}
// note this only exists after desugaring
LowLevelDbg(_, _, _) => loc_expr,

View file

@ -286,6 +286,11 @@ pub enum Expr {
symbol: Symbol,
},
Return {
return_value: Box<Loc<Expr>>,
return_var: Variable,
},
/// Rendered as empty box in editor
TypedHole(Variable),
@ -360,6 +365,7 @@ impl Expr {
Self::Expect { .. } => Category::Expect,
Self::ExpectFx { .. } => Category::Expect,
Self::Crash { .. } => Category::Crash,
Self::Return { .. } => Category::Return,
Self::Dbg { .. } => Category::Expect,
@ -400,6 +406,7 @@ pub struct ClosureData {
pub function_type: Variable,
pub closure_type: Variable,
pub return_type: Variable,
pub early_returns: Vec<(Variable, Region)>,
pub name: Symbol,
pub captured_symbols: Vec<(Symbol, Variable)>,
pub recursive: Recursive,
@ -476,6 +483,7 @@ impl StructAccessorData {
function_type: function_var,
closure_type: closure_var,
return_type: field_var,
early_returns: vec![],
name,
captured_symbols: vec![],
recursive: Recursive::NotRecursive,
@ -549,6 +557,7 @@ impl OpaqueWrapFunctionData {
function_type: function_var,
closure_type: closure_var,
return_type: opaque_var,
early_returns: vec![],
name: function_name,
captured_symbols: vec![],
recursive: Recursive::NotRecursive,
@ -1008,7 +1017,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_scope(false, |inner_scope| {
let defs: Defs = (*loc_defs).clone();
can_defs_with_return(env, var_store, inner_scope, env.arena.alloc(defs), loc_ret)
})
@ -1040,16 +1049,17 @@ pub fn canonicalize_expr<'a>(
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| {
canonicalize_when_branch(
env,
var_store,
inner_scope,
region,
branch,
&mut output,
)
});
let (can_when_branch, branch_references) =
scope.inner_scope(false, |inner_scope| {
canonicalize_when_branch(
env,
var_store,
inner_scope,
region,
branch,
&mut output,
)
});
output.references.union_mut(&branch_references);
@ -1258,6 +1268,37 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::Return(return_expr, after_return) => {
let mut output = Output::default();
let (loc_return_expr, output1) = canonicalize_expr(
env,
var_store,
scope,
return_expr.region,
&return_expr.value,
);
if let Some(after_return) = after_return {
env.problem(Problem::StatementsAfterReturn {
region: after_return.region,
});
}
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,
@ -1493,7 +1534,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_scope(true, |inner_scope| {
canonicalize_closure_body(
env,
var_store,
@ -1621,10 +1662,13 @@ 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,
early_returns: scope.early_returns.clone(),
name: symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
@ -2027,7 +2071,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,
@ -2253,6 +2298,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
function_type,
closure_type,
return_type,
early_returns,
recursive,
name,
captured_symbols,
@ -2269,6 +2315,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
function_type,
closure_type,
return_type,
early_returns,
recursive,
name,
captured_symbols,
@ -2495,6 +2542,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
ast::Expr::DbgStmt(_, _)
| ast::Expr::LowLevelDbg(_, _, _)
| ast::Expr::Expect(_, _)
| ast::Expr::Return(_, _)
| ast::Expr::When(_, _)
| ast::Expr::Backpassing(_, _, _)
| ast::Expr::SpaceBefore(_, _)
@ -2795,6 +2843,7 @@ impl Declarations {
pub fn new() -> Self {
Self::with_capacity(0)
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
declarations: Vec::with_capacity(capacity),
@ -2841,6 +2890,7 @@ impl Declarations {
let function_def = FunctionDef {
closure_type: loc_closure_data.value.closure_type,
return_type: loc_closure_data.value.return_type,
early_returns: loc_closure_data.value.early_returns,
captured_symbols: loc_closure_data.value.captured_symbols,
arguments: loc_closure_data.value.arguments,
};
@ -2892,6 +2942,7 @@ impl Declarations {
let function_def = FunctionDef {
closure_type: loc_closure_data.value.closure_type,
return_type: loc_closure_data.value.return_type,
early_returns: loc_closure_data.value.early_returns,
captured_symbols: loc_closure_data.value.captured_symbols,
arguments: loc_closure_data.value.arguments,
};
@ -3072,6 +3123,7 @@ impl Declarations {
let function_def = FunctionDef {
closure_type: closure_data.closure_type,
return_type: closure_data.return_type,
early_returns: closure_data.early_returns,
captured_symbols: closure_data.captured_symbols,
arguments: closure_data.arguments,
};
@ -3112,6 +3164,7 @@ impl Declarations {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
early_returns: vec![],
name: self.symbols[index].value,
captured_symbols: vec![],
recursive: Recursive::NotRecursive,
@ -3124,6 +3177,7 @@ impl Declarations {
let function_def = FunctionDef {
closure_type: loc_closure_data.value.closure_type,
return_type: loc_closure_data.value.return_type,
early_returns: loc_closure_data.value.early_returns,
captured_symbols: loc_closure_data.value.captured_symbols,
arguments: loc_closure_data.value.arguments,
};
@ -3258,6 +3312,7 @@ impl DeclarationTag {
pub struct FunctionDef {
pub closure_type: Variable,
pub return_type: Variable,
pub early_returns: Vec<(Variable, Region)>,
pub captured_symbols: Vec<(Symbol, Variable)>,
pub arguments: Vec<(Variable, AnnotatedMark, Loc<Pattern>)>,
}
@ -3402,6 +3457,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(_, _, _, _, _)

View file

@ -369,6 +369,12 @@ pub fn canonicalize_module_defs<'a>(
PatternType::TopLevelDef,
);
for (_early_return_var, early_return_region) in &scope.early_returns {
env.problem(Problem::ReturnOutsideOfFunction {
region: *early_return_region,
});
}
let pending_derives = output.pending_derives;
// See if any of the new idents we defined went unused.
@ -425,7 +431,7 @@ pub fn canonicalize_module_defs<'a>(
..Default::default()
};
let (mut declarations, mut output) = crate::def::sort_can_defs_new(
let (mut declarations, mut output) = crate::def::sort_top_level_can_defs(
&mut env,
&mut scope,
var_store,
@ -969,6 +975,14 @@ fn fix_values_captured_in_closure_expr(
);
}
Return { return_value, .. } => {
fix_values_captured_in_closure_expr(
&mut return_value.value,
no_capture_symbols,
closure_captures,
);
}
Crash { msg, ret_var: _ } => {
fix_values_captured_in_closure_expr(
&mut msg.value,

View file

@ -48,6 +48,8 @@ pub struct Scope {
/// Ignored variables (variables that start with an underscore).
/// We won't intern them because they're only used during canonicalization for error reporting.
ignored_locals: VecMap<String, Region>,
pub early_returns: Vec<(Variable, Region)>,
}
impl Scope {
@ -73,6 +75,7 @@ impl Scope {
modules: ScopeModules::new(home, module_name),
imported_symbols: default_imports,
ignored_locals: VecMap::default(),
early_returns: Vec::default(),
}
}
@ -429,7 +432,7 @@ impl Scope {
self.aliases.contains_key(&name)
}
pub fn inner_scope<F, T>(&mut self, f: F) -> T
pub fn inner_scope<F, T>(&mut self, entering_function: bool, f: F) -> T
where
F: FnOnce(&mut Scope) -> T,
{
@ -446,6 +449,11 @@ impl Scope {
let locals_snapshot = self.locals.in_scope.len();
let imported_symbols_snapshot = self.imported_symbols.len();
let imported_modules_snapshot = self.modules.len();
let early_returns_snapshot = if entering_function {
std::mem::replace(&mut self.early_returns, Vec::new())
} else {
Vec::new()
};
let result = f(self);
@ -453,6 +461,9 @@ impl Scope {
self.ignored_locals.truncate(ignored_locals_count);
self.imported_symbols.truncate(imported_symbols_snapshot);
self.modules.truncate(imported_modules_snapshot);
if entering_function {
self.early_returns = early_returns_snapshot;
}
// anything added in the inner scope is no longer in scope now
for i in locals_snapshot..self.locals.in_scope.len() {
@ -882,7 +893,7 @@ mod test {
assert!(scope.lookup(&ident, region).is_err());
scope.inner_scope(|inner| {
scope.inner_scope(false, |inner| {
assert!(inner.introduce(ident.clone(), region).is_ok());
});
@ -943,7 +954,7 @@ mod test {
&[ident1.clone(), ident2.clone(), ident3.clone(),]
);
scope.inner_scope(|inner| {
scope.inner_scope(false, |inner| {
let ident4 = Ident::from("Ångström");
let ident5 = Ident::from("Sirály");

View file

@ -680,7 +680,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>(
};
let maybe_suffixed_value_def = match current_value_def {
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None,
Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Return(_) | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None,
AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } => Some((body_pattern, body_expr, Some((ann_pattern, ann_type)))),
Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)),
};

View file

@ -72,6 +72,7 @@ pub fn build_host_exposed_def(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
early_returns: vec![],
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
@ -98,6 +99,7 @@ pub fn build_host_exposed_def(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
early_returns: vec![],
name: symbol,
captured_symbols: std::vec::Vec::new(),
recursive: Recursive::NotRecursive,
@ -126,6 +128,7 @@ pub fn build_host_exposed_def(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
early_returns: vec![],
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,

View file

@ -22,6 +22,10 @@ pub enum DeclarationInfo<'a> {
pattern: Pattern,
annotation: Option<&'a Annotation>,
},
Return {
loc_expr: &'a Loc<Expr>,
expr_var: Variable,
},
Expectation {
loc_condition: &'a Loc<Expr>,
},
@ -50,6 +54,7 @@ impl<'a> DeclarationInfo<'a> {
loc_expr,
..
} => Region::span_across(&loc_symbol.region, &loc_expr.region),
Return { loc_expr, .. } => loc_expr.region,
Expectation { loc_condition } => loc_condition.region,
Function {
loc_symbol,
@ -67,6 +72,7 @@ impl<'a> DeclarationInfo<'a> {
fn var(&self) -> Variable {
match self {
DeclarationInfo::Value { expr_var, .. } => *expr_var,
DeclarationInfo::Return { expr_var, .. } => *expr_var,
DeclarationInfo::Expectation { .. } => Variable::BOOL,
DeclarationInfo::Function { expr_var, .. } => *expr_var,
DeclarationInfo::Destructure { expr_var, .. } => *expr_var,
@ -185,6 +191,9 @@ pub fn walk_decl<V: Visitor>(visitor: &mut V, decl: DeclarationInfo<'_>) {
Expectation { loc_condition } => {
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
}
Return { loc_expr, expr_var } => {
visitor.visit_expr(&loc_expr.value, loc_expr.region, expr_var);
}
Function {
loc_symbol,
loc_body,
@ -403,6 +412,12 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
Variable::NULL,
);
}
Expr::Return {
return_value,
return_var,
} => {
visitor.visit_expr(&return_value.value, return_value.region, *return_var);
}
Expr::TypedHole(_) => { /* terminal */ }
Expr::RuntimeError(..) => { /* terminal */ }
}