Merge branch 'main' into specialize-exprs

This commit is contained in:
Agus Zubiaga 2024-11-23 01:48:51 -03:00
commit 2e96aca0fd
No known key found for this signature in database
797 changed files with 17394 additions and 12632 deletions

View file

@ -683,7 +683,7 @@ impl IAbilitiesStore<Pending> {
}
mod serialize {
use roc_collections::{MutMap, VecMap};
use roc_collections::{soa::slice_extend_new, MutMap, VecMap};
use roc_module::symbol::Symbol;
use roc_region::all::Region;
use roc_serialize::bytes;
@ -1034,11 +1034,11 @@ mod serialize {
specialization_lambda_sets,
} in spec_info
{
let regions = SubsSlice::extend_new(
let regions = slice_extend_new(
&mut spec_lambda_sets_regions,
specialization_lambda_sets.keys().copied(),
);
let vars = SubsSlice::extend_new(
let vars = slice_extend_new(
&mut spec_lambda_sets_vars,
specialization_lambda_sets.values().copied(),
);
@ -1168,11 +1168,11 @@ mod serialize {
symbol,
specialization_lambda_sets,
}) => {
let regions = SubsSlice::extend_new(
let regions = slice_extend_new(
&mut spec_lambda_sets_regions,
specialization_lambda_sets.keys().copied(),
);
let vars = SubsSlice::extend_new(
let vars = slice_extend_new(
&mut spec_lambda_sets_vars,
specialization_lambda_sets.values().copied(),
);

View file

@ -2,10 +2,12 @@ use crate::env::Env;
use crate::procedure::{QualifiedReference, References};
use crate::scope::{PendingAbilitiesInScope, Scope, SymbolLookup};
use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
use roc_module::ident::{Ident, Lowercase, TagName};
use roc_module::ident::{Ident, IdentSuffix, Lowercase, TagName};
use roc_module::symbol::Symbol;
use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader};
use roc_problem::can::ShadowKind;
use roc_parse::ast::{
AssignedField, ExtractSpaces, FunctionArrow, Pattern, Tag, TypeAnnotation, TypeHeader,
};
use roc_problem::can::{Problem, ShadowKind};
use roc_region::all::{Loc, Region};
use roc_types::subs::{VarStore, Variable};
use roc_types::types::{
@ -448,7 +450,7 @@ pub fn find_type_def_symbols(
stack.push(&t.value);
}
}
Function(arguments, result) => {
Function(arguments, _arrow, result) => {
for t in arguments.iter() {
stack.push(&t.value);
}
@ -480,7 +482,6 @@ pub fn find_type_def_symbols(
AssignedField::LabelOnly(_) => {}
AssignedField::SpaceBefore(inner, _)
| AssignedField::SpaceAfter(inner, _) => inner_stack.push(inner),
AssignedField::Malformed(_) => {}
}
}
@ -505,7 +506,6 @@ pub fn find_type_def_symbols(
Tag::SpaceBefore(inner, _) | Tag::SpaceAfter(inner, _) => {
inner_stack.push(inner)
}
Tag::Malformed(_) => {}
}
}
@ -554,7 +554,7 @@ fn can_annotation_help(
use roc_parse::ast::TypeAnnotation::*;
match annotation {
Function(argument_types, return_type) => {
Function(argument_types, arrow, return_type) => {
let mut args = Vec::new();
for arg in *argument_types {
@ -589,7 +589,12 @@ fn can_annotation_help(
introduced_variables.insert_lambda_set(lambda_set);
let closure = Type::Variable(lambda_set);
Type::Function(args, Box::new(closure), Box::new(ret))
let fx_type = match arrow {
FunctionArrow::Pure => Type::Pure,
FunctionArrow::Effectful => Type::Effectful,
};
Type::Function(args, Box::new(closure), Box::new(ret), Box::new(fx_type))
}
Apply(module_name, ident, type_arguments) => {
let symbol = match make_apply_symbol(env, region, scope, module_name, ident, references)
@ -1348,7 +1353,7 @@ fn can_assigned_fields<'a>(
// field names we've seen so far in this record
let mut seen = std::collections::HashMap::with_capacity(fields.len());
'outer: for loc_field in fields.iter() {
for loc_field in fields.iter() {
let mut field = &loc_field.value;
// use this inner loop to unwrap the SpaceAfter/SpaceBefore
@ -1371,6 +1376,8 @@ fn can_assigned_fields<'a>(
);
let label = Lowercase::from(field_name.value);
check_record_field_suffix(env, label.suffix(), &field_type, &loc_field.region);
field_types.insert(label.clone(), RigidRequired(field_type));
break 'inner label;
@ -1389,6 +1396,8 @@ fn can_assigned_fields<'a>(
);
let label = Lowercase::from(field_name.value);
check_record_field_suffix(env, label.suffix(), &field_type, &loc_field.region);
field_types.insert(label.clone(), RigidOptional(field_type));
break 'inner label;
@ -1419,12 +1428,6 @@ fn can_assigned_fields<'a>(
field = nested;
continue 'inner;
}
Malformed(string) => {
malformed(env, region, string);
// completely skip this element, advance to the next tag
continue 'outer;
}
}
};
@ -1443,6 +1446,23 @@ fn can_assigned_fields<'a>(
field_types
}
fn check_record_field_suffix(
env: &mut Env,
suffix: IdentSuffix,
field_type: &Type,
region: &Region,
) {
match (suffix, field_type) {
(IdentSuffix::None, Type::Function(_, _, _, fx)) if **fx == Type::Effectful => env
.problems
.push(Problem::UnsuffixedEffectfulRecordField(*region)),
(IdentSuffix::Bang, Type::Function(_, _, _, fx)) if **fx == Type::Pure => {
env.problems.push(Problem::SuffixedPureRecordField(*region))
}
_ => {}
}
}
// TODO trim down these arguments!
#[allow(clippy::too_many_arguments)]
fn can_assigned_tuple_elems(
@ -1494,7 +1514,7 @@ fn can_tags<'a>(
// tag names we've seen so far in this tag union
let mut seen = std::collections::HashMap::with_capacity(tags.len());
'outer: for loc_tag in tags.iter() {
for loc_tag in tags.iter() {
let mut tag = &loc_tag.value;
// use this inner loop to unwrap the SpaceAfter/SpaceBefore
@ -1533,12 +1553,6 @@ fn can_tags<'a>(
tag = nested;
continue 'inner;
}
Tag::Malformed(string) => {
malformed(env, region, string);
// completely skip this element, advance to the next tag
continue 'outer;
}
}
};

View file

@ -116,7 +116,7 @@ map_symbol_to_lowlevel_and_arity! {
StrIsEmpty; STR_IS_EMPTY; 1,
StrStartsWith; STR_STARTS_WITH; 2,
StrEndsWith; STR_ENDS_WITH; 2,
StrSplit; STR_SPLIT; 2,
StrSplitOn; STR_SPLIT_ON; 2,
StrCountUtf8Bytes; STR_COUNT_UTF8_BYTES; 1,
StrFromUtf8; STR_FROM_UTF8_LOWLEVEL; 1,
StrToUtf8; STR_TO_UTF8; 1,
@ -418,6 +418,7 @@ fn defn(
expr_var: var_store.fresh(),
pattern_vars: SendMap::default(),
annotation: None,
kind: crate::def::DefKind::Let,
}
}
@ -446,6 +447,8 @@ fn defn_help(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: ret_var,
fx_type: Variable::PURE,
early_returns: vec![],
name: fn_name,
captured_symbols: Vec::new(),
recursive: Recursive::NotRecursive,
@ -546,6 +549,7 @@ fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel)
expr_var: record_var,
pattern_vars: SendMap::default(),
annotation: None,
kind: crate::def::DefKind::Let,
};
let body = LetNonRec(Box::new(def), Box::new(no_region(cont)));

View file

@ -5,12 +5,13 @@ use std::sync::Arc;
use crate::abilities::SpecializationId;
use crate::exhaustive::{ExhaustiveContext, SketchedRows};
use crate::expected::{Expected, PExpected};
use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_module::ident::TagName;
use roc_collections::soa::{index_push_new, slice_extend_new};
use roc_module::ident::{IdentSuffix, TagName};
use roc_module::symbol::{ModuleId, Symbol};
use roc_region::all::{Loc, Region};
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, Variable};
use roc_types::types::{Category, PatternCategory, TypeTag, Types};
use soa::{EitherIndex, Index, Slice};
pub struct Constraints {
pub constraints: Vec<Constraint>,
@ -28,6 +29,8 @@ pub struct Constraints {
pub eq: Vec<Eq>,
pub pattern_eq: Vec<PatternEq>,
pub cycles: Vec<Cycle>,
pub fx_call_constraints: Vec<FxCallConstraint>,
pub fx_suffix_constraints: Vec<FxSuffixConstraint>,
}
impl std::fmt::Debug for Constraints {
@ -49,6 +52,8 @@ impl std::fmt::Debug for Constraints {
.field("eq", &self.eq)
.field("pattern_eq", &self.pattern_eq)
.field("cycles", &self.cycles)
.field("fx_call_constraints", &self.fx_call_constraints)
.field("fx_suffix_constraints", &self.fx_suffix_constraints)
.finish()
}
}
@ -80,6 +85,8 @@ impl Constraints {
let eq = Vec::new();
let pattern_eq = Vec::new();
let cycles = Vec::new();
let fx_call_constraints = Vec::with_capacity(16);
let fx_suffix_constraints = Vec::new();
categories.extend([
Category::Record,
@ -96,6 +103,7 @@ impl Constraints {
Category::List,
Category::Str,
Category::Character,
Category::Return,
]);
pattern_categories.extend([
@ -128,6 +136,8 @@ impl Constraints {
eq,
pattern_eq,
cycles,
fx_call_constraints,
fx_suffix_constraints,
}
}
@ -149,6 +159,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);
@ -205,11 +216,11 @@ impl Constraints {
}
pub fn push_expected_type(&mut self, expected: Expected<TypeOrVar>) -> ExpectedTypeIndex {
Index::push_new(&mut self.expectations, expected)
index_push_new(&mut self.expectations, expected)
}
pub fn push_pat_expected_type(&mut self, expected: PExpected<TypeOrVar>) -> PExpectedTypeIndex {
Index::push_new(&mut self.pattern_expectations, expected)
index_push_new(&mut self.pattern_expectations, expected)
}
#[inline(always)]
@ -229,7 +240,7 @@ impl Constraints {
Category::List => Self::CATEGORY_LIST,
Category::Str => Self::CATEGORY_STR,
Category::Character => Self::CATEGORY_CHARACTER,
other => Index::push_new(&mut self.categories, other),
other => index_push_new(&mut self.categories, other),
}
}
@ -247,7 +258,7 @@ impl Constraints {
PatternCategory::Int => Self::PCATEGORY_INT,
PatternCategory::Float => Self::PCATEGORY_FLOAT,
PatternCategory::Character => Self::PCATEGORY_CHARACTER,
other => Index::push_new(&mut self.pattern_categories, other),
other => index_push_new(&mut self.pattern_categories, other),
}
}
@ -320,7 +331,7 @@ impl Constraints {
category: PatternCategory,
region: Region,
) -> Constraint {
let category_index = Index::push_new(&mut self.pattern_categories, category);
let category_index = index_push_new(&mut self.pattern_categories, category);
Constraint::PatternPresence(type_index, expected_index, category_index, region)
}
@ -337,7 +348,7 @@ impl Constraints {
category: PatternCategory,
region: Region,
) -> Constraint {
let category_index = Index::push_new(&mut self.pattern_categories, category);
let category_index = index_push_new(&mut self.pattern_categories, category);
let includes_tag = IncludesTag {
type_index,
@ -347,7 +358,7 @@ impl Constraints {
region,
};
let includes_tag_index = Index::push_new(&mut self.includes_tags, includes_tag);
let includes_tag_index = index_push_new(&mut self.includes_tags, includes_tag);
Constraint::IncludesTag(includes_tag_index)
}
@ -571,6 +582,59 @@ impl Constraints {
Constraint::Lookup(symbol, expected_index, region)
}
pub fn fx_call(
&mut self,
call_fx_var: Variable,
call_kind: FxCallKind,
call_region: Region,
expectation: Option<FxExpectation>,
) -> Constraint {
let constraint = FxCallConstraint {
call_fx_var,
call_kind,
call_region,
expectation,
};
let constraint_index = index_push_new(&mut self.fx_call_constraints, constraint);
Constraint::FxCall(constraint_index)
}
pub fn fx_pattern_suffix(
&mut self,
symbol: Symbol,
type_index: TypeOrVar,
region: Region,
) -> Constraint {
let constraint = FxSuffixConstraint {
kind: FxSuffixKind::Pattern(symbol),
type_index,
region,
};
let constraint_index = index_push_new(&mut self.fx_suffix_constraints, constraint);
Constraint::FxSuffix(constraint_index)
}
pub fn fx_record_field_unsuffixed(&mut self, variable: Variable, region: Region) -> Constraint {
let type_index = Self::push_type_variable(variable);
let constraint = FxSuffixConstraint {
kind: FxSuffixKind::UnsuffixedRecordField,
type_index,
region,
};
let constraint_index = index_push_new(&mut self.fx_suffix_constraints, constraint);
Constraint::FxSuffix(constraint_index)
}
pub fn flex_to_pure(&mut self, fx_var: Variable) -> Constraint {
Constraint::FlexToPure(fx_var)
}
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
match constraint {
Constraint::SaveTheEnvironment => true,
@ -595,6 +659,10 @@ impl Constraints {
| Constraint::Store(..)
| Constraint::Lookup(..)
| Constraint::Pattern(..)
| Constraint::ExpectEffectful(..)
| Constraint::FxCall(_)
| Constraint::FxSuffix(_)
| Constraint::FlexToPure(_)
| Constraint::True
| Constraint::IsOpenType(_)
| Constraint::IncludesTag(_)
@ -614,7 +682,7 @@ impl Constraints {
filename: &'static str,
line_number: u32,
) -> Constraint {
let string_index = Index::push_new(&mut self.strings, filename);
let string_index = index_push_new(&mut self.strings, filename);
Constraint::Store(type_index, variable, string_index, line_number)
}
@ -632,19 +700,19 @@ impl Constraints {
exhaustive: ExhaustiveMark,
) -> Constraint {
let real_var = Self::push_type_variable(real_var);
let sketched_rows = Index::push_new(&mut self.sketched_rows, sketched_rows);
let sketched_rows = index_push_new(&mut self.sketched_rows, sketched_rows);
let equality = match category_and_expectation {
Ok((category, expected)) => {
let category = Index::push_new(&mut self.categories, category);
let category = index_push_new(&mut self.categories, category);
let equality = Eq(real_var, expected, category, real_region);
let equality = Index::push_new(&mut self.eq, equality);
let equality = index_push_new(&mut self.eq, equality);
Ok(equality)
}
Err((category, expected)) => {
let category = Index::push_new(&mut self.pattern_categories, category);
let category = index_push_new(&mut self.pattern_categories, category);
let equality = PatternEq(real_var, expected, category, real_region);
let equality = Index::push_new(&mut self.pattern_eq, equality);
let equality = index_push_new(&mut self.pattern_eq, equality);
Err(equality)
}
};
@ -662,18 +730,18 @@ impl Constraints {
I: IntoIterator<Item = (Symbol, Region)>,
I1: IntoIterator<Item = Region>,
{
let def_names = Slice::extend_new(&mut self.loc_symbols, loc_symbols);
let def_names = slice_extend_new(&mut self.loc_symbols, loc_symbols);
// we add a dummy symbol to these regions, so we can store the data in the loc_symbols vec
let it = expr_regions.into_iter().map(|r| (Symbol::ATTR_ATTR, r));
let expr_regions = Slice::extend_new(&mut self.loc_symbols, it);
let expr_regions = slice_extend_new(&mut self.loc_symbols, it);
let expr_regions = Slice::new(expr_regions.start() as _, expr_regions.len() as _);
let cycle = Cycle {
def_names,
expr_regions,
};
let cycle_index = Index::push_new(&mut self.cycles, cycle);
let cycle_index = index_push_new(&mut self.cycles, cycle);
Constraint::CheckCycle(cycle_index, cycle_mark)
}
@ -767,6 +835,14 @@ pub enum Constraint {
Index<PatternCategory>,
Region,
),
/// Check call fx against enclosing function fx
FxCall(Index<FxCallConstraint>),
/// Require idents to be accurately suffixed
FxSuffix(Index<FxSuffixConstraint>),
/// Set an fx var as pure if flex (no effectful functions were called)
FlexToPure(Variable),
/// Expect statement or ignored def to be effectful
ExpectEffectful(Variable, ExpectEffectfulReason, Region),
/// Used for things that always unify, e.g. blanks and runtime errors
True,
SaveTheEnvironment,
@ -839,6 +915,56 @@ pub struct Cycle {
pub expr_regions: Slice<Region>,
}
#[derive(Debug)]
pub struct FxCallConstraint {
pub call_fx_var: Variable,
pub call_kind: FxCallKind,
pub call_region: Region,
pub expectation: Option<FxExpectation>,
}
#[derive(Debug, Clone, Copy)]
pub struct FxExpectation {
pub fx_var: Variable,
pub ann_region: Option<Region>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FxCallKind {
Call(Option<Symbol>),
Stmt,
Ignored,
}
#[derive(Debug, Clone, Copy)]
pub struct FxSuffixConstraint {
pub type_index: TypeOrVar,
pub kind: FxSuffixKind,
pub region: Region,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FxSuffixKind {
Let(Symbol),
Pattern(Symbol),
UnsuffixedRecordField,
}
impl FxSuffixKind {
pub fn suffix(&self) -> IdentSuffix {
match self {
Self::Let(symbol) | Self::Pattern(symbol) => symbol.suffix(),
Self::UnsuffixedRecordField => IdentSuffix::None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExpectEffectfulReason {
Stmt,
Ignored,
}
/// Custom impl to limit vertical space used by the debug output
impl std::fmt::Debug for Constraint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -855,6 +981,18 @@ impl std::fmt::Debug for Constraint {
Self::Pattern(arg0, arg1, arg2, arg3) => {
write!(f, "Pattern({arg0:?}, {arg1:?}, {arg2:?}, {arg3:?})")
}
Self::FxCall(arg0) => {
write!(f, "FxCall({arg0:?})")
}
Self::FxSuffix(arg0) => {
write!(f, "FxSuffix({arg0:?})")
}
Self::ExpectEffectful(arg0, arg1, arg2) => {
write!(f, "EffectfulStmt({arg0:?}, {arg1:?}, {arg2:?})")
}
Self::FlexToPure(arg0) => {
write!(f, "FlexToPure({arg0:?})")
}
Self::True => write!(f, "True"),
Self::SaveTheEnvironment => write!(f, "SaveTheEnvironment"),
Self::Let(arg0, arg1) => f.debug_tuple("Let").field(arg0).field(arg1).finish(),

View file

@ -1,10 +1,11 @@
use crate::{
def::Def,
def::{Def, DefKind},
expr::{
ClosureData, Expr, Field, OpaqueWrapFunctionData, StructAccessorData, WhenBranchPattern,
},
pattern::{DestructType, ListPatterns, Pattern, RecordDestruct, TupleDestruct},
};
use roc_collections::soa::{index_push_new, slice_extend_new};
use roc_module::{
ident::{Lowercase, TagName},
symbol::Symbol,
@ -12,7 +13,7 @@ use roc_module::{
use roc_types::{
subs::{
self, AliasVariables, Descriptor, GetSubsSlice, OptVariable, RecordFields, Subs, SubsIndex,
SubsSlice, TupleElems, UnionLambdas, UnionTags, Variable, VariableSubsSlice,
SubsSlice, TupleElems, UnionLambdas, UnionTags, Variable,
},
types::{RecordField, Uls},
};
@ -155,12 +156,12 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
#[inline(always)]
fn clone_name(&mut self, name: SubsIndex<Lowercase>) -> SubsIndex<Lowercase> {
SubsIndex::push_new(&mut self.target.field_names, self.source[name].clone())
index_push_new(&mut self.target.field_names, self.source[name].clone())
}
#[inline(always)]
fn clone_field_names(&mut self, field_names: SubsSlice<Lowercase>) -> SubsSlice<Lowercase> {
SubsSlice::extend_new(
slice_extend_new(
&mut self.target.field_names,
self.source.get_subs_slice(field_names).iter().cloned(),
)
@ -171,7 +172,7 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
&mut self,
tuple_elem_indices: SubsSlice<usize>,
) -> SubsSlice<usize> {
SubsSlice::extend_new(
slice_extend_new(
&mut self.target.tuple_elem_indices,
self.source
.get_subs_slice(tuple_elem_indices)
@ -182,7 +183,7 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
#[inline(always)]
fn clone_tag_names(&mut self, tag_names: SubsSlice<TagName>) -> SubsSlice<TagName> {
SubsSlice::extend_new(
slice_extend_new(
&mut self.target.tag_names,
self.source.get_subs_slice(tag_names).iter().cloned(),
)
@ -190,7 +191,7 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
#[inline(always)]
fn clone_lambda_names(&mut self, lambda_names: SubsSlice<Symbol>) -> SubsSlice<Symbol> {
SubsSlice::extend_new(
slice_extend_new(
&mut self.target.symbol_names,
self.source.get_subs_slice(lambda_names).iter().cloned(),
)
@ -201,7 +202,7 @@ impl<'a> CopyEnv for AcrossSubs<'a> {
&mut self,
record_fields: SubsSlice<RecordField<()>>,
) -> SubsSlice<RecordField<()>> {
SubsSlice::extend_new(
slice_extend_new(
&mut self.target.record_fields,
self.source.get_subs_slice(record_fields).iter().copied(),
)
@ -377,6 +378,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
expr_var,
pattern_vars,
annotation,
kind,
}| Def {
loc_pattern: loc_pattern.map(|p| deep_copy_pattern_help(env, copied, p)),
loc_expr: loc_expr.map(|e| go_help!(e)),
@ -385,6 +387,11 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
// Annotation should only be used in constraining, don't clone before
// constraining :)
annotation: annotation.clone(),
kind: match kind {
DefKind::Let => DefKind::Let,
DefKind::Stmt(v) => DefKind::Stmt(sub!(*v)),
DefKind::Ignored(v) => DefKind::Ignored(sub!(*v)),
},
},
)
.collect(),
@ -398,6 +405,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
expr_var,
pattern_vars,
annotation,
kind,
} = &**def;
let def = Def {
loc_pattern: loc_pattern.map(|p| deep_copy_pattern_help(env, copied, p)),
@ -407,18 +415,20 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
// Annotation should only be used in constraining, don't clone before
// constraining :)
annotation: annotation.clone(),
kind: *kind,
};
LetNonRec(Box::new(def), Box::new(body.map(|e| go_help!(e))))
}
Call(f, args, called_via) => {
let (fn_var, fn_expr, clos_var, ret_var) = &**f;
let (fn_var, fn_expr, clos_var, ret_var, fx_var) = &**f;
Call(
Box::new((
sub!(*fn_var),
fn_expr.map(|e| go_help!(e)),
sub!(*clos_var),
sub!(*ret_var),
sub!(*fx_var),
)),
args.iter()
.map(|(var, expr)| (sub!(*var), expr.map(|e| go_help!(e))))
@ -455,6 +465,8 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
function_type,
closure_type,
return_type,
fx_type,
early_returns,
name,
captured_symbols,
recursive,
@ -464,6 +476,11 @@ 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),
fx_type: sub!(*fx_type),
early_returns: early_returns
.iter()
.map(|(var, region)| (sub!(*var), *region))
.collect(),
name: *name,
captured_symbols: captured_symbols
.iter()
@ -677,14 +694,12 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
lookups_in_cond: lookups_in_cond.to_vec(),
},
ExpectFx {
loc_condition,
loc_continuation,
lookups_in_cond,
} => ExpectFx {
loc_condition: Box::new(loc_condition.map(|e| go_help!(e))),
loc_continuation: Box::new(loc_continuation.map(|e| go_help!(e))),
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 {
@ -919,8 +934,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
macro_rules! clone_var_slice {
($slice:expr) => {{
let new_arguments =
VariableSubsSlice::reserve_into_subs(env.target(), $slice.len());
let new_arguments = env.target().reserve_into_vars($slice.len());
for (target_index, var_index) in (new_arguments.indices()).zip($slice) {
let var = env.source()[var_index];
let copy_var = env.get_copy(var).into_variable().unwrap_or(var);
@ -961,7 +975,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
// Everything else is a mechanical descent.
Structure(flat_type) => match flat_type {
EmptyRecord | EmptyTuple | EmptyTagUnion => Structure(flat_type),
EmptyRecord | EmptyTagUnion | EffectfulFunc => Structure(flat_type),
Apply(symbol, arguments) => {
descend_slice!(arguments);
@ -970,15 +984,21 @@ fn deep_copy_type_vars<C: CopyEnv>(
Structure(Apply(symbol, new_arguments))
})
}
Func(arguments, closure_var, ret_var) => {
Func(arguments, closure_var, ret_var, fx_var) => {
descend_slice!(arguments);
let new_closure_var = descend_var!(closure_var);
let new_ret_var = descend_var!(ret_var);
let new_fx_var = descend_var!(fx_var);
perform_clone!({
let new_arguments = clone_var_slice!(arguments);
Structure(Func(new_arguments, new_closure_var, new_ret_var))
Structure(Func(
new_arguments,
new_closure_var,
new_ret_var,
new_fx_var,
))
})
}
Record(fields, ext_var) => {
@ -994,9 +1014,9 @@ fn deep_copy_type_vars<C: CopyEnv>(
let new_fields = {
RecordFields {
length: fields.length,
field_names_start: new_field_names.start,
variables_start: new_variables.start,
field_types_start: new_record_fields.start,
field_names_start: new_field_names.start(),
variables_start: new_variables.start(),
field_types_start: new_record_fields.start(),
}
};
@ -1015,8 +1035,8 @@ fn deep_copy_type_vars<C: CopyEnv>(
let new_elems = {
TupleElems {
length: elems.length,
variables_start: new_variables.start,
elem_index_start: new_elem_indices.start,
variables_start: new_variables.start(),
elem_index_start: new_elem_indices.start(),
}
};
@ -1032,8 +1052,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
}
perform_clone!({
let new_variable_slices =
SubsSlice::reserve_variable_slices(env.target(), tags.len());
let new_variable_slices = env.target().reserve_variable_slices(tags.len());
let it = (new_variable_slices.indices()).zip(tags.variables());
for (target_index, index) in it {
let slice = env.source()[index];
@ -1058,8 +1077,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
}
perform_clone!({
let new_variable_slices =
SubsSlice::reserve_variable_slices(env.target(), tags.len());
let new_variable_slices = env.target().reserve_variable_slices(tags.len());
let it = (new_variable_slices.indices()).zip(tags.variables());
for (target_index, index) in it {
let slice = env.source()[index];
@ -1109,7 +1127,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
perform_clone!({
let new_variables = clone_var_slice!(arguments.all_variables());
let new_arguments = AliasVariables {
variables_start: new_variables.start,
variables_start: new_variables.start(),
..arguments
};
@ -1135,8 +1153,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
let new_ambient_function = descend_var!(ambient_function);
perform_clone!({
let new_variable_slices =
SubsSlice::reserve_variable_slices(env.target(), solved.len());
let new_variable_slices = env.target().reserve_variable_slices(solved.len());
let it = (new_variable_slices.indices()).zip(solved.variables());
for (target_index, index) in it {
let slice = env.source()[index];
@ -1150,8 +1167,7 @@ fn deep_copy_type_vars<C: CopyEnv>(
let new_solved =
UnionLambdas::from_slices(new_solved_labels, new_variable_slices);
let new_unspecialized =
SubsSlice::reserve_uls_slice(env.target(), unspecialized.len());
let new_unspecialized = env.target().reserve_uls_slice(unspecialized.len());
for (target_index, uls_index) in
(new_unspecialized.into_iter()).zip(unspecialized.into_iter())
{
@ -1171,6 +1187,8 @@ fn deep_copy_type_vars<C: CopyEnv>(
})
}
ErasedLambda => ErasedLambda,
Pure => Pure,
Effectful => Effectful,
RangedNumber(range) => {
perform_clone!(RangedNumber(range))
@ -1192,13 +1210,14 @@ mod test {
};
use super::{deep_copy_expr_across_subs, deep_copy_type_vars};
use roc_collections::soa::{index_push_new, slice_extend_new};
use roc_error_macros::internal_error;
use roc_module::{ident::TagName, symbol::Symbol};
use roc_region::all::Loc;
use roc_types::{
subs::{
self, Content, Content::*, Descriptor, FlatType, GetSubsSlice, Mark, OptVariable, Rank,
Subs, SubsIndex, SubsSlice, Variable,
Subs, Variable,
},
types::Uls,
};
@ -1217,7 +1236,7 @@ mod test {
fn copy_flex_var() {
let mut subs = Subs::new();
let field_name = SubsIndex::push_new(&mut subs.field_names, "a".into());
let field_name = index_push_new(&mut subs.field_names, "a".into());
let var = new_var(&mut subs, FlexVar(Some(field_name)));
let mut copied = vec![];
@ -1238,7 +1257,7 @@ mod test {
fn copy_rigid_var() {
let mut subs = Subs::new();
let field_name = SubsIndex::push_new(&mut subs.field_names, "a".into());
let field_name = index_push_new(&mut subs.field_names, "a".into());
let var = new_var(&mut subs, RigidVar(field_name));
let mut copied = vec![];
@ -1259,8 +1278,8 @@ mod test {
fn copy_flex_able_var() {
let mut subs = Subs::new();
let field_name = SubsIndex::push_new(&mut subs.field_names, "a".into());
let abilities = SubsSlice::extend_new(&mut subs.symbol_names, [Symbol::UNDERSCORE]);
let field_name = index_push_new(&mut subs.field_names, "a".into());
let abilities = slice_extend_new(&mut subs.symbol_names, [Symbol::UNDERSCORE]);
let var = new_var(&mut subs, FlexAbleVar(Some(field_name), abilities));
let mut copied = vec![];
@ -1282,8 +1301,8 @@ mod test {
fn copy_rigid_able_var() {
let mut subs = Subs::new();
let field_name = SubsIndex::push_new(&mut subs.field_names, "a".into());
let abilities = SubsSlice::extend_new(&mut subs.symbol_names, [Symbol::UNDERSCORE]);
let field_name = index_push_new(&mut subs.field_names, "a".into());
let abilities = slice_extend_new(&mut subs.symbol_names, [Symbol::UNDERSCORE]);
let var = new_var(&mut subs, RigidAbleVar(field_name, abilities));
let mut copied = vec![];
@ -1304,8 +1323,8 @@ mod test {
fn copy_deep_expr() {
let mut subs = Subs::new();
let a = SubsIndex::push_new(&mut subs.field_names, "a".into());
let b = SubsIndex::push_new(&mut subs.field_names, "b".into());
let a = index_push_new(&mut subs.field_names, "a".into());
let b = index_push_new(&mut subs.field_names, "b".into());
let var1 = new_var(&mut subs, FlexVar(Some(a)));
let var2 = new_var(&mut subs, FlexVar(Some(b)));
@ -1390,8 +1409,8 @@ mod test {
let mut source = Subs::new();
let mut target = Subs::new();
let a = SubsIndex::push_new(&mut source.field_names, "a".into());
let b = SubsIndex::push_new(&mut source.field_names, "b".into());
let a = index_push_new(&mut source.field_names, "a".into());
let b = index_push_new(&mut source.field_names, "b".into());
let var1 = new_var(&mut source, FlexVar(Some(a)));
let var2 = new_var(&mut source, FlexVar(Some(b)));
@ -1472,7 +1491,7 @@ mod test {
let mut target = Subs::new();
let a = new_var(&mut source, FlexVar(None));
let uls = SubsSlice::extend_new(
let uls = slice_extend_new(
&mut source.unspecialized_lambda_sets,
vec![Uls(a, Symbol::UNDERSCORE, 3)],
);

View file

@ -1,6 +1,6 @@
//! Pretty-prints the canonical AST back to check our work - do things look reasonable?
use crate::def::Def;
use crate::def::{Def, DefKind};
use crate::expr::Expr::{self, *};
use crate::expr::{
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData,
@ -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
@ -107,9 +106,14 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> {
expr_var: _,
pattern_vars: _,
annotation: _,
kind,
} = d;
def_help(c, f, &loc_pattern.value, &loc_expr.value)
match kind {
DefKind::Let => def_help(c, f, &loc_pattern.value, &loc_expr.value),
DefKind::Ignored(_) => def_help(c, f, &loc_pattern.value, &loc_expr.value),
DefKind::Stmt(_) => expr(c, EPrec::Free, f, &loc_expr.value),
}
}
fn def_symbol_help<'a>(
@ -267,7 +271,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
.append(expr(c, Free, f, &body.value))
.group(),
Call(fun, args, _) => {
let (_, fun, _, _) = &**fun;
let (_, fun, _, _, _) = &**fun;
maybe_paren!(
Free,
p,
@ -448,7 +452,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

@ -10,6 +10,7 @@ use crate::annotation::IntroducedVariables;
use crate::annotation::OwnedNamedOrAble;
use crate::derive;
use crate::env::Env;
use crate::env::FxMode;
use crate::expr::canonicalize_record;
use crate::expr::get_lookup_symbols;
use crate::expr::AnnotatedMark;
@ -69,6 +70,7 @@ pub struct Def {
pub expr_var: Variable,
pub pattern_vars: SendMap<Symbol, Variable>,
pub annotation: Option<Annotation>,
pub kind: DefKind,
}
impl Def {
@ -89,6 +91,38 @@ impl Def {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DefKind {
/// A def that introduces identifiers
Let,
/// A standalone statement with an fx variable
Stmt(Variable),
/// Ignored result, must be effectful
Ignored(Variable),
}
impl DefKind {
pub fn map_var<F: Fn(Variable) -> Variable>(self, f: F) -> Self {
match self {
DefKind::Let => DefKind::Let,
DefKind::Stmt(v) => DefKind::Stmt(f(v)),
DefKind::Ignored(v) => DefKind::Ignored(f(v)),
}
}
pub fn from_pattern(var_store: &mut VarStore, pattern: &Loc<Pattern>) -> Self {
if BindingsFromPattern::new(pattern)
.peekable()
.peek()
.is_none()
{
DefKind::Ignored(var_store.fresh())
} else {
DefKind::Let
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Annotation {
pub signature: Type,
@ -127,6 +161,7 @@ impl Annotation {
arg_types,
Box::new(Type::Variable(var_store.fresh())),
Box::new(self.signature.clone()),
Box::new(Type::Variable(var_store.fresh())),
);
}
}
@ -136,7 +171,6 @@ pub(crate) struct CanDefs {
defs: Vec<Option<Def>>,
dbgs: ExpectsOrDbgs,
expects: ExpectsOrDbgs,
expects_fx: ExpectsOrDbgs,
def_ordering: DefOrdering,
aliases: VecMap<Symbol, Alias>,
}
@ -194,22 +228,25 @@ enum PendingValueDef<'a> {
Option<Loc<ast::TypeAnnotation<'a>>>,
Loc<ast::StrLiteral<'a>>,
),
/// A standalone statement
Stmt(&'a Loc<ast::Expr<'a>>),
}
impl PendingValueDef<'_> {
fn loc_pattern(&self) -> &Loc<Pattern> {
fn loc_pattern(&self) -> Option<&Loc<Pattern>> {
match self {
PendingValueDef::AnnotationOnly(loc_pattern, _) => loc_pattern,
PendingValueDef::Body(loc_pattern, _) => loc_pattern,
PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern,
PendingValueDef::AnnotationOnly(loc_pattern, _) => Some(loc_pattern),
PendingValueDef::Body(loc_pattern, _) => Some(loc_pattern),
PendingValueDef::TypedBody(_, loc_pattern, _, _) => Some(loc_pattern),
PendingValueDef::ImportParams {
loc_pattern,
symbol: _,
variable: _,
module_id: _,
opt_provided: _,
} => loc_pattern,
PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern,
} => Some(loc_pattern),
PendingValueDef::IngestedFile(loc_pattern, _, _) => Some(loc_pattern),
PendingValueDef::Stmt(_) => None,
}
}
}
@ -305,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>),
@ -320,7 +356,6 @@ impl Declaration {
InvalidCycle { .. } => 0,
Builtin(_) => 0,
Expects(_) => 0,
ExpectsFx(_) => 0,
}
}
@ -336,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(),
),
@ -677,10 +712,6 @@ fn canonicalize_claimed_ability_impl<'a>(
});
Err(())
}
AssignedField::Malformed(_) => {
// An error will already have been reported
Err(())
}
AssignedField::SpaceBefore(_, _)
| AssignedField::SpaceAfter(_, _)
| AssignedField::IgnoredValue(_, _, _) => {
@ -1136,7 +1167,6 @@ fn canonicalize_value_defs<'a>(
let mut pending_value_defs = Vec::with_capacity(value_defs.len());
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 imports_introduced = Vec::with_capacity(value_defs.len());
@ -1156,9 +1186,6 @@ fn canonicalize_value_defs<'a>(
PendingValue::Expect(pending_expect) => {
pending_expects.push(pending_expect);
}
PendingValue::ExpectFx(pending_expect) => {
pending_expect_fx.push(pending_expect);
}
PendingValue::ModuleImport(PendingModuleImport {
module_id,
region,
@ -1181,19 +1208,18 @@ fn canonicalize_value_defs<'a>(
}
PendingValue::InvalidIngestedFile => { /* skip */ }
PendingValue::ImportNameConflict => { /* skip */ }
PendingValue::StmtAfterExpr => { /* skip */ }
}
}
let mut symbol_to_index: Vec<(IdentId, u32)> = Vec::with_capacity(pending_value_defs.len());
for (def_index, pending_def) in pending_value_defs.iter().enumerate() {
let mut new_bindings = BindingsFromPattern::new(pending_def.loc_pattern()).peekable();
let Some(loc_pattern) = pending_def.loc_pattern() else {
continue;
};
if new_bindings.peek().is_none() {
env.problem(Problem::NoIdentifiersIntroduced(
pending_def.loc_pattern().region,
));
}
let new_bindings = BindingsFromPattern::new(loc_pattern).peekable();
for (s, r) in new_bindings {
// store the top-level defs, used to ensure that closures won't capture them
@ -1230,6 +1256,14 @@ fn canonicalize_value_defs<'a>(
output = temp_output.output;
if let (PatternType::TopLevelDef, DefKind::Ignored(_)) =
(pattern_type, temp_output.def.kind)
{
env.problems.push(Problem::NoIdentifiersIntroduced(
temp_output.def.loc_pattern.region,
))
}
defs.push(Some(temp_output.def));
def_ordering.insert_symbol_references(def_id as u32, &temp_output.references)
@ -1237,7 +1271,6 @@ fn canonicalize_value_defs<'a>(
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());
for pending in pending_dbgs {
let (loc_can_condition, can_output) = canonicalize_expr(
@ -1267,25 +1300,10 @@ fn canonicalize_value_defs<'a>(
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);
}
let can_defs = CanDefs {
defs,
dbgs,
expects,
expects_fx,
def_ordering,
aliases,
};
@ -1681,7 +1699,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,
@ -1693,7 +1711,6 @@ pub(crate) fn sort_can_defs_new(
defs,
dbgs: _,
expects,
expects_fx,
def_ordering,
aliases,
} = defs;
@ -1724,19 +1741,6 @@ pub(crate) fn sort_can_defs_new(
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() {
output.aliases.insert(symbol, alias);
}
@ -1974,7 +1978,6 @@ pub(crate) fn sort_can_defs(
mut defs,
dbgs,
expects,
expects_fx,
def_ordering,
aliases,
} = defs;
@ -2108,10 +2111,6 @@ pub(crate) fn sort_can_defs(
declarations.push(Declaration::Expects(expects));
}
if !expects_fx.conditions.is_empty() {
declarations.push(Declaration::ExpectsFx(expects_fx));
}
(declarations, output)
}
@ -2215,6 +2214,7 @@ fn single_can_def(
expr_var: Variable,
opt_loc_annotation: Option<Loc<crate::annotation::Annotation>>,
pattern_vars: SendMap<Symbol, Variable>,
kind: DefKind,
) -> Def {
let def_annotation = opt_loc_annotation.map(|loc_annotation| Annotation {
signature: loc_annotation.value.typ,
@ -2232,6 +2232,7 @@ fn single_can_def(
},
pattern_vars,
annotation: def_annotation,
kind,
}
}
@ -2354,6 +2355,8 @@ fn canonicalize_pending_value_def<'a>(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
fx_type: var_store.fresh(),
early_returns: scope.early_returns.clone(),
name: symbol,
captured_symbols: Vec::new(),
recursive: Recursive::NotRecursive,
@ -2370,6 +2373,7 @@ fn canonicalize_pending_value_def<'a>(
expr_var,
Some(Loc::at(loc_ann.region, type_annotation)),
vars_by_symbol.clone(),
DefKind::Let,
);
DefOutput {
@ -2405,10 +2409,13 @@ fn canonicalize_pending_value_def<'a>(
loc_can_pattern,
loc_expr,
Some(Loc::at(loc_ann.region, type_annotation)),
DefKind::Let,
)
}
Body(loc_can_pattern, loc_expr) => {
//
let def_kind = DefKind::from_pattern(var_store, &loc_can_pattern);
canonicalize_pending_body(
env,
output,
@ -2417,6 +2424,20 @@ fn canonicalize_pending_value_def<'a>(
loc_can_pattern,
loc_expr,
None,
def_kind,
)
}
Stmt(loc_expr) => {
let fx_var = var_store.fresh();
canonicalize_pending_body(
env,
output,
scope,
var_store,
Loc::at(loc_expr.region, Pattern::Underscore),
loc_expr,
None,
DefKind::Stmt(fx_var),
)
}
ImportParams {
@ -2457,6 +2478,7 @@ fn canonicalize_pending_value_def<'a>(
var_store.fresh(),
None,
SendMap::default(),
DefKind::Let,
);
DefOutput {
@ -2526,6 +2548,7 @@ fn canonicalize_pending_value_def<'a>(
var_store.fresh(),
opt_loc_can_ann,
SendMap::default(),
DefKind::Let,
);
DefOutput {
@ -2564,6 +2587,7 @@ fn canonicalize_pending_body<'a>(
loc_expr: &'a Loc<ast::Expr>,
opt_loc_annotation: Option<Loc<crate::annotation::Annotation>>,
kind: DefKind,
) -> DefOutput {
let mut loc_value = &loc_expr.value;
@ -2571,6 +2595,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.)
//
@ -2602,9 +2628,15 @@ fn canonicalize_pending_body<'a>(
env.tailcallable_symbol = outer_tailcallable;
// The closure is self tail recursive iff it tail calls itself (by defined name).
let is_recursive = match can_output.tail_call {
Some(tail_symbol) if tail_symbol == *defined_symbol => Recursive::TailRecursive,
_ => Recursive::NotRecursive,
let is_recursive = if !can_output.tail_calls.is_empty()
&& can_output
.tail_calls
.iter()
.all(|tail_symbol| tail_symbol == defined_symbol)
{
Recursive::TailRecursive
} else {
Recursive::NotRecursive
};
closure_data.recursive = is_recursive;
@ -2664,7 +2696,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);
@ -2675,6 +2706,7 @@ fn canonicalize_pending_body<'a>(
expr_var,
opt_loc_annotation,
vars_by_symbol,
kind,
);
DefOutput {
@ -2801,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)
}
}
}
@ -3004,11 +3032,11 @@ enum PendingValue<'a> {
Def(PendingValueDef<'a>),
Dbg(PendingExpectOrDbg<'a>),
Expect(PendingExpectOrDbg<'a>),
ExpectFx(PendingExpectOrDbg<'a>),
ModuleImport(PendingModuleImport<'a>),
SignatureDefMismatch,
InvalidIngestedFile,
ImportNameConflict,
StmtAfterExpr,
}
struct PendingExpectOrDbg<'a> {
@ -3063,10 +3091,7 @@ fn to_pending_value_def<'a>(
loc_pattern.region,
);
PendingValue::Def(PendingValueDef::AnnotationOnly(
loc_can_pattern,
loc_ann,
))
PendingValue::Def(PendingValueDef::AnnotationOnly(loc_can_pattern, loc_ann))
}
Body(loc_pattern, loc_expr) => {
// This takes care of checking for shadowing and adding idents to scope.
@ -3146,14 +3171,6 @@ fn to_pending_value_def<'a>(
preceding_comment: *preceding_comment,
}),
ExpectFx {
condition,
preceding_comment,
} => PendingValue::ExpectFx(PendingExpectOrDbg {
condition,
preceding_comment: *preceding_comment,
}),
ModuleImport(module_import) => {
let qualified_module_name: QualifiedModuleName = module_import.name.value.into();
let module_name = qualified_module_name.module.clone();
@ -3172,15 +3189,17 @@ fn to_pending_value_def<'a>(
// Generate a symbol for the module params def
// We do this even if params weren't provided so that solve can report if they are missing
let params_sym = scope.gen_unique_symbol();
let params_region = module_import.params.map(|p| p.params.region).unwrap_or(region);
let params_region = module_import
.params
.map(|p| p.params.region)
.unwrap_or(region);
let params_var = var_store.fresh();
let params =
PendingModuleImportParams {
symbol: params_sym,
variable: params_var,
loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)),
opt_provided: module_import.params.map(|p| p.params.value),
};
let params = PendingModuleImportParams {
symbol: params_sym,
variable: params_var,
loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)),
opt_provided: module_import.params.map(|p| p.params.value),
};
let provided_params = if module_import.params.is_some() {
// Only add params to scope if they are provided
Some((params_var, params_sym))
@ -3209,8 +3228,12 @@ fn to_pending_value_def<'a>(
.map(|kw| kw.item.items)
.unwrap_or_default();
if exposed_names.is_empty() && !env.home.is_builtin() && module_id.is_automatically_imported() {
env.problems.push(Problem::ExplicitBuiltinImport(module_id, region));
if exposed_names.is_empty()
&& !env.home.is_builtin()
&& module_id.is_automatically_imported()
{
env.problems
.push(Problem::ExplicitBuiltinImport(module_id, region));
}
let exposed_ids = env
@ -3230,7 +3253,9 @@ fn to_pending_value_def<'a>(
let symbol = Symbol::new(module_id, ident_id);
exposed_symbols.push((symbol, loc_name.region));
if let Err((_shadowed_symbol, existing_symbol_region)) = scope.import_symbol(ident, symbol, loc_name.region) {
if let Err((_shadowed_symbol, existing_symbol_region)) =
scope.import_symbol(ident, symbol, loc_name.region)
{
if symbol.is_automatically_imported() {
env.problem(Problem::ExplicitBuiltinTypeImport(
symbol,
@ -3245,14 +3270,12 @@ fn to_pending_value_def<'a>(
}
}
}
None => {
env.problem(Problem::RuntimeError(RuntimeError::ValueNotExposed {
module_name: module_name.clone(),
ident,
region: loc_name.region,
exposed_values: exposed_ids.exposed_values(),
}))
}
None => env.problem(Problem::RuntimeError(RuntimeError::ValueNotExposed {
module_name: module_name.clone(),
ident,
region: loc_name.region,
exposed_values: exposed_ids.exposed_values(),
})),
}
}
@ -3267,12 +3290,12 @@ fn to_pending_value_def<'a>(
let loc_name = ingested_file.name.item;
let symbol = match scope.introduce(loc_name.value.into(), loc_name.region) {
Ok(symbol ) => symbol,
Ok(symbol) => symbol,
Err((original, shadow, _)) => {
env.problem(Problem::Shadowing {
original_region: original.region,
shadow,
kind: ShadowKind::Variable
kind: ShadowKind::Variable,
});
return PendingValue::InvalidIngestedFile;
@ -3281,9 +3304,20 @@ fn to_pending_value_def<'a>(
let loc_pattern = Loc::at(loc_name.region, Pattern::Identifier(symbol));
PendingValue::Def(PendingValueDef::IngestedFile(loc_pattern, ingested_file.annotation.map(|ann| ann.annotation), ingested_file.path))
PendingValue::Def(PendingValueDef::IngestedFile(
loc_pattern,
ingested_file.annotation.map(|ann| ann.annotation),
ingested_file.path,
))
}
StmtAfterExpr => PendingValue::StmtAfterExpr,
Stmt(expr) => {
if env.fx_mode == FxMode::Task {
internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar")
}
PendingValue::Def(PendingValueDef::Stmt(expr))
}
Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"),
}
}

View file

@ -1,6 +1,6 @@
#![allow(clippy::manual_map)]
use crate::env::Env;
use crate::env::{Env, FxMode};
use crate::scope::Scope;
use crate::suffixed::{apply_try_function, unwrap_suffixed_expression, EUnwrapped};
use bumpalo::collections::Vec;
@ -11,8 +11,8 @@ use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{
AssignedField, Collection, Defs, ModuleImportParams, Pattern, StrLiteral, StrSegment,
TypeAnnotation, ValueDef, WhenBranch,
is_expr_suffixed, AssignedField, Collection, Defs, ModuleImportParams, Pattern, StrLiteral,
StrSegment, TypeAnnotation, ValueDef, WhenBranch,
};
use roc_problem::can::Problem;
use roc_region::all::{Loc, Region};
@ -23,6 +23,10 @@ use roc_region::all::{Loc, Region};
//
// Thank you, Markus!
/// Desugar a single binary operation.
///
/// When using this function, don't desugar `left` and `right` before calling so that
/// we can properly desugar `|> try` expressions!
fn new_op_call_expr<'a>(
env: &mut Env<'a>,
scope: &mut Scope,
@ -33,10 +37,43 @@ fn new_op_call_expr<'a>(
let region = Region::span_across(&left.region, &right.region);
let value = match loc_op.value {
// Rewrite the Pizza operator into an Apply
Pizza => {
// Rewrite the Pizza operator into an Apply
// Allow `left |> try (optional)` to desugar to `try left (optional)`
let right_without_spaces = without_spaces(&right.value);
match right_without_spaces {
Try => {
let desugared_left = desugar_expr(env, scope, left);
return desugar_try_expr(env, scope, desugared_left);
}
Apply(&Loc { value: Try, .. }, arguments, _called_via) => {
let try_fn = desugar_expr(env, scope, arguments.first().unwrap());
match &right.value {
let mut args = Vec::with_capacity_in(arguments.len(), env.arena);
args.push(desugar_expr(env, scope, left));
args.extend(
arguments
.iter()
.skip(1)
.map(|a| desugar_expr(env, scope, a)),
);
return desugar_try_expr(
env,
scope,
env.arena.alloc(Loc::at(
right.region,
Expr::Apply(try_fn, args.into_bump_slice(), CalledVia::Try),
)),
);
}
_ => {}
}
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
match right.value {
Apply(function, arguments, _called_via) => {
let mut args = Vec::with_capacity_in(1 + arguments.len(), env.arena);
@ -55,6 +92,9 @@ fn new_op_call_expr<'a>(
}
}
binop => {
let left = desugar_expr(env, scope, left);
let right = desugar_expr(env, scope, right);
// This is a normal binary operator like (+), so desugar it
// into the appropriate function call.
let (module_name, ident) = binop_to_function(binop);
@ -73,6 +113,13 @@ fn new_op_call_expr<'a>(
Loc { region, value }
}
fn without_spaces<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
match expr {
Expr::SpaceBefore(inner, _) | Expr::SpaceAfter(inner, _) => without_spaces(inner),
_ => expr,
}
}
fn desugar_value_def<'a>(
env: &mut Env<'a>,
scope: &mut Scope,
@ -120,16 +167,6 @@ fn desugar_value_def<'a>(
preceding_comment: *preceding_comment,
}
}
ExpectFx {
condition,
preceding_comment,
} => {
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
ExpectFx {
condition: desugared_condition,
preceding_comment: *preceding_comment,
}
}
ModuleImport(roc_parse::ast::ModuleImport {
before_name,
name,
@ -153,11 +190,29 @@ fn desugar_value_def<'a>(
}
IngestedFileImport(_) => *def,
StmtAfterExpr => internal_error!(
"StmtAfterExpression is only created during desugaring, so it shouldn't exist here."
),
Stmt(stmt_expr) => {
if env.fx_mode == FxMode::PurityInference {
// In purity inference mode, statements aren't fully desugared here
// so we can provide better errors
return Stmt(desugar_expr(env, scope, stmt_expr));
}
// desugar `stmt_expr!` to
// _ : {}
// _ = stmt_expr!
let desugared_expr = desugar_expr(env, scope, stmt_expr);
if !is_expr_suffixed(&desugared_expr.value) {
env.problems.push(Problem::StmtAfterExpr(stmt_expr.region));
return ValueDef::StmtAfterExpr;
}
let region = stmt_expr.region;
let new_pat = env
.arena
@ -174,7 +229,7 @@ fn desugar_value_def<'a>(
)),
lines_between: &[],
body_pattern: new_pat,
body_expr: desugar_expr(env, scope, stmt_expr),
body_expr: desugared_expr,
}
}
}
@ -248,8 +303,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
body_pattern,
body_expr,
} => {
// note called_from_def is passed as `false` as this is a top_level_def
match unwrap_suffixed_expression(arena, body_expr, None) {
match unwrap_suffixed_expression(arena, body_expr, Some(ann_pattern)) {
Ok(new_expr) => AnnotatedBody {
ann_pattern,
ann_type,
@ -280,6 +334,19 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
),
},
),
// When the last expression is suffixed, it will try to unwrap the def, but because we
// have an annotated def we can simply ignore the try and return it as is without
// creating an intermediate identifier
Err(EUnwrapped::UnwrappedDefExpr { loc_expr, .. }) => desugar_value_def_suffixed(
arena,
AnnotatedBody {
ann_pattern,
ann_type,
lines_between,
body_pattern,
body_expr: loc_expr,
},
),
Err(..) => AnnotatedBody {
ann_pattern,
ann_type,
@ -290,9 +357,22 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>)
}
}
// TODO support desugaring of Dbg, Expect, and ExpectFx
Dbg { .. } | Expect { .. } | ExpectFx { .. } => value_def,
ModuleImport { .. } | IngestedFileImport(_) => value_def,
Expect {
condition,
preceding_comment,
} => match unwrap_suffixed_expression(arena, condition, None) {
Ok(new_condition) => ValueDef::Expect {
condition: new_condition,
preceding_comment,
},
Err(..) => {
internal_error!("Unable to desugar the suffix inside an Expect value def");
}
},
// TODO support desugaring of Dbg
Dbg { .. } => value_def,
ModuleImport { .. } | IngestedFileImport(_) | StmtAfterExpr => value_def,
Stmt(..) => {
internal_error!(
@ -315,10 +395,8 @@ pub fn desugar_expr<'a>(
| NonBase10Int { .. }
| SingleQuote(_)
| AccessorFunction(_)
| Var { .. }
| Underscore { .. }
| MalformedIdent(_, _)
| MalformedClosure
| MalformedSuffixed(..)
| PrecedenceConflict { .. }
| EmptyRecordBuilder(_)
@ -326,7 +404,25 @@ pub fn desugar_expr<'a>(
| OptionalFieldInRecordBuilder { .. }
| Tag(_)
| OpaqueRef(_)
| Crash => loc_expr,
| Crash
| Try => loc_expr,
Var { module_name, ident } => {
if env.fx_mode == FxMode::Task && ident.ends_with('!') {
env.arena.alloc(Loc::at(
Region::new(loc_expr.region.start(), loc_expr.region.end().sub(1)),
TrySuffix {
expr: env.arena.alloc(Var {
module_name,
ident: ident.trim_end_matches('!'),
}),
target: roc_parse::ast::TryTarget::Task,
},
))
} else {
loc_expr
}
}
Str(str_literal) => match str_literal {
StrLiteral::PlainLine(_) => loc_expr,
@ -605,7 +701,6 @@ pub fn desugar_expr<'a>(
AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => {
unreachable!("Should have been desugared in `desugar_field`")
}
AssignedField::Malformed(_name) => continue,
};
field_data.push(FieldData {
@ -845,6 +940,31 @@ pub fn desugar_expr<'a>(
})
}
}
Apply(
Loc {
value: Try,
region: _,
},
loc_args,
_called_via,
) => {
let result_expr = if loc_args.len() == 1 {
desugar_expr(env, scope, loc_args[0])
} else {
let function = desugar_expr(env, scope, loc_args.first().unwrap());
let mut desugared_args = Vec::with_capacity_in(loc_args.len() - 1, env.arena);
for loc_arg in &loc_args[1..] {
desugared_args.push(desugar_expr(env, scope, loc_arg));
}
env.arena.alloc(Loc::at(
loc_expr.region,
Expr::Apply(function, desugared_args.into_bump_slice(), CalledVia::Try),
))
};
env.arena.alloc(desugar_try_expr(env, scope, result_expr))
}
Apply(loc_fn, loc_args, called_via) => {
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), env.arena);
@ -980,21 +1100,12 @@ 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
}
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 {
@ -1002,12 +1113,87 @@ 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));
env.arena.alloc(Loc {
// Do not desugar after_return since it isn't run anyway
value: Return(desugared_return_value, *after_return),
region: loc_expr.region,
})
}
// note this only exists after desugaring
LowLevelDbg(_, _, _) => loc_expr,
}
}
pub fn desugar_try_expr<'a>(
env: &mut Env<'a>,
scope: &mut Scope,
result_expr: &'a Loc<Expr<'a>>,
) -> Loc<Expr<'a>> {
let region = result_expr.region;
let ok_symbol = env.arena.alloc(scope.gen_unique_symbol_name().to_string());
let err_symbol = env.arena.alloc(scope.gen_unique_symbol_name().to_string());
let ok_branch = env.arena.alloc(WhenBranch {
patterns: env.arena.alloc([Loc::at(
region,
Pattern::Apply(
env.arena.alloc(Loc::at(region, Pattern::Tag("Ok"))),
env.arena
.alloc([Loc::at(region, Pattern::Identifier { ident: ok_symbol })]),
),
)]),
value: Loc::at(
region,
Expr::Var {
module_name: "",
ident: ok_symbol,
},
),
guard: None,
});
let err_branch = env.arena.alloc(WhenBranch {
patterns: env.arena.alloc([Loc::at(
region,
Pattern::Apply(
env.arena.alloc(Loc::at(region, Pattern::Tag("Err"))),
env.arena
.alloc([Loc::at(region, Pattern::Identifier { ident: err_symbol })]),
),
)]),
value: Loc::at(
region,
Expr::Return(
env.arena.alloc(Loc::at(
region,
Expr::Apply(
env.arena.alloc(Loc::at(region, Expr::Tag("Err"))),
&*env.arena.alloc([&*env.arena.alloc(Loc::at(
region,
Expr::Var {
module_name: "",
ident: err_symbol,
},
))]),
CalledVia::Try,
),
)),
None,
),
),
guard: None,
});
Loc::at(
region,
Expr::When(result_expr, &*env.arena.alloc([&*ok_branch, &*err_branch])),
)
}
fn desugar_str_segments<'a>(
env: &mut Env<'a>,
scope: &mut Scope,
@ -1020,20 +1206,6 @@ fn desugar_str_segments<'a>(
StrSegment::Plaintext(_) | StrSegment::Unicode(_) | StrSegment::EscapedChar(_) => {
*segment
}
StrSegment::DeprecatedInterpolated(loc_expr) => {
let loc_desugared = desugar_expr(
env,
scope,
env.arena.alloc(Loc {
region: loc_expr.region,
value: *loc_expr.value,
}),
);
StrSegment::DeprecatedInterpolated(Loc {
region: loc_desugared.region,
value: env.arena.alloc(loc_desugared.value),
})
}
StrSegment::Interpolated(loc_expr) => {
let loc_desugared = desugar_expr(
env,
@ -1123,8 +1295,6 @@ fn desugar_field<'a>(
}
SpaceBefore(field, _spaces) => desugar_field(env, scope, field),
SpaceAfter(field, _spaces) => desugar_field(env, scope, field),
Malformed(string) => Malformed(string),
}
}
@ -1387,7 +1557,7 @@ fn desugar_bin_ops<'a>(
let mut op_stack: Vec<Loc<BinOp>> = Vec::with_capacity_in(lefts.len(), env.arena);
for (loc_expr, loc_op) in lefts {
arg_stack.push(desugar_expr(env, scope, loc_expr));
arg_stack.push(loc_expr);
match run_binop_step(
env,
scope,
@ -1401,7 +1571,7 @@ fn desugar_bin_ops<'a>(
}
}
let mut expr = desugar_expr(env, scope, right);
let mut expr = right;
for (left, loc_op) in arg_stack.into_iter().zip(op_stack.into_iter()).rev() {
expr = env

View file

@ -1,4 +1,4 @@
use crate::def::Def;
use crate::def::{Def, DefKind};
use crate::expr::{AnnotatedMark, ClosureData, Expr, Recursive};
use crate::pattern::Pattern;
use crate::scope::Scope;
@ -33,7 +33,7 @@ pub fn build_host_exposed_def(
let def_body = {
match typ.shallow_structural_dealias() {
Type::Function(args, _, _) => {
Type::Function(args, _, _, fx) if **fx == Type::Pure => {
for i in 0..args.len() {
let name = format!("closure_arg_{ident}_{i}");
@ -72,6 +72,8 @@ pub fn build_host_exposed_def(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
fx_type: var_store.fresh(),
early_returns: vec![],
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
@ -98,6 +100,8 @@ pub fn build_host_exposed_def(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
fx_type: var_store.fresh(),
early_returns: vec![],
name: symbol,
captured_symbols: std::vec::Vec::new(),
recursive: Recursive::NotRecursive,
@ -105,6 +109,47 @@ pub fn build_host_exposed_def(
loc_body: Box::new(Loc::at_zero(body)),
})
}
Type::Function(args, _, _, fx) if **fx == Type::Effectful => {
for i in 0..args.len() {
let name = format!("{ident}_arg_{i}");
let arg_symbol = {
let ident = name.clone().into();
scope.introduce(ident, Region::zero()).unwrap()
};
let arg_var = var_store.fresh();
arguments.push((
arg_var,
AnnotatedMark::new(var_store),
Loc::at_zero(Pattern::Identifier(arg_symbol)),
));
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol, arg_var)));
}
let ident_without_bang = ident.trim_end_matches('!');
let foreign_symbol_name = format!("roc_fx_{ident_without_bang}");
let foreign_call = Expr::ForeignCall {
foreign_symbol: foreign_symbol_name.into(),
args: linked_symbol_arguments,
ret_var: var_store.fresh(),
};
Expr::Closure(ClosureData {
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
fx_type: var_store.fresh(),
early_returns: vec![],
name: symbol,
captured_symbols: std::vec::Vec::new(),
recursive: Recursive::NotRecursive,
arguments,
loc_body: Box::new(Loc::at_zero(foreign_call)),
})
}
_ => {
// not a function
@ -126,6 +171,8 @@ pub fn build_host_exposed_def(
function_type: var_store.fresh(),
closure_type: var_store.fresh(),
return_type: var_store.fresh(),
fx_type: var_store.fresh(),
early_returns: vec![],
name: task_closure_symbol,
captured_symbols,
recursive: Recursive::NotRecursive,
@ -164,6 +211,7 @@ pub fn build_host_exposed_def(
expr_var,
pattern_vars,
annotation: Some(def_annotation),
kind: DefKind::Let,
}
}
@ -175,11 +223,13 @@ fn build_fresh_opaque_variables(
let ok_var = var_store.fresh();
let err_var = var_store.fresh();
let result_var = var_store.fresh();
let fx_var = var_store.fresh();
let actual = Type::Function(
vec![Type::EmptyRec],
Box::new(Type::Variable(closure_var)),
Box::new(Type::Variable(result_var)),
Box::new(Type::Variable(fx_var)),
);
let type_arguments = vec![

View file

@ -45,6 +45,8 @@ pub struct Env<'a> {
pub opt_shorthand: Option<&'a str>,
pub fx_mode: FxMode,
pub src: &'a str,
/// Lazily calculated line info. This data is only needed if the code contains calls to `dbg`,
@ -54,6 +56,7 @@ pub struct Env<'a> {
}
impl<'a> Env<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
arena: &'a Bump,
src: &'a str,
@ -62,6 +65,7 @@ impl<'a> Env<'a> {
dep_idents: &'a IdentIdsByModule,
qualified_module_ids: &'a PackageModuleIds<'a>,
opt_shorthand: Option<&'a str>,
fx_mode: FxMode,
) -> Env<'a> {
Env {
arena,
@ -79,6 +83,7 @@ impl<'a> Env<'a> {
home_params_record: None,
opt_shorthand,
line_info: arena.alloc(None),
fx_mode,
}
}
@ -237,3 +242,9 @@ impl<'a> Env<'a> {
self.line_info.as_ref().unwrap()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FxMode {
PurityInference,
Task,
}

View file

@ -141,7 +141,9 @@ fn index_var(
| Content::RigidAbleVar(_, _)
| Content::LambdaSet(_)
| Content::ErasedLambda
| Content::RangedNumber(..) => return Err(TypeError),
| Content::RangedNumber(..)
| Content::Pure
| Content::Effectful => return Err(TypeError),
Content::Error => return Err(TypeError),
Content::RecursionVar {
structure,
@ -150,7 +152,8 @@ fn index_var(
var = *structure;
}
Content::Structure(structure) => match structure {
FlatType::Func(_, _, _) => return Err(TypeError),
FlatType::Func(_, _, _, _) => return Err(TypeError),
FlatType::EffectfulFunc => return Err(TypeError),
FlatType::Apply(Symbol::LIST_LIST, args) => {
match (subs.get_subs_slice(*args), ctor) {
([elem_var], IndexCtor::List) => {
@ -237,9 +240,6 @@ fn index_var(
};
return Ok(std::iter::repeat(Variable::NULL).take(num_fields).collect());
}
FlatType::EmptyTuple => {
return Ok(std::iter::repeat(Variable::NULL).take(0).collect());
}
FlatType::EmptyTagUnion => {
internal_error!("empty tag unions are not indexable")
}

View file

@ -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());

View file

@ -14,6 +14,7 @@ pub mod copy;
pub mod def;
mod derive;
pub mod desugar;
pub mod effect_module;
pub mod env;
pub mod exhaustive;
pub mod expected;
@ -25,7 +26,6 @@ pub mod procedure;
pub mod scope;
pub mod string;
pub mod suffixed;
pub mod task_module;
pub mod traverse;
pub use derive::DERIVED_REGION;

View file

@ -2,9 +2,9 @@ use std::path::Path;
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
use crate::annotation::{canonicalize_annotation, AnnotationFor};
use crate::def::{canonicalize_defs, report_unused_imports, Def};
use crate::def::{canonicalize_defs, report_unused_imports, Def, DefKind};
use crate::desugar::desugar_record_destructures;
use crate::env::Env;
use crate::env::{Env, FxMode};
use crate::expr::{
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
};
@ -226,6 +226,7 @@ pub fn canonicalize_module_defs<'a>(
symbols_from_requires: &[(Loc<Symbol>, Loc<TypeAnnotation<'a>>)],
var_store: &mut VarStore,
opt_shorthand: Option<&'a str>,
fx_mode: FxMode,
) -> ModuleOutput {
let mut can_exposed_imports = MutMap::default();
@ -247,6 +248,7 @@ pub fn canonicalize_module_defs<'a>(
dep_idents,
qualified_module_ids,
opt_shorthand,
fx_mode,
);
for (name, alias) in aliases.into_iter() {
@ -369,6 +371,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 +433,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,
@ -527,7 +535,7 @@ pub fn canonicalize_module_defs<'a>(
aliases: Default::default(),
};
let hosted_def = crate::task_module::build_host_exposed_def(
let hosted_def = crate::effect_module::build_host_exposed_def(
&mut scope, *symbol, &ident, var_store, annotation,
);
@ -580,7 +588,7 @@ pub fn canonicalize_module_defs<'a>(
aliases: Default::default(),
};
let hosted_def = crate::task_module::build_host_exposed_def(
let hosted_def = crate::effect_module::build_host_exposed_def(
&mut scope, *symbol, &ident, var_store, annotation,
);
@ -601,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 */ }
}
}
@ -649,6 +656,7 @@ pub fn canonicalize_module_defs<'a>(
expr_var: var_store.fresh(),
pattern_vars,
annotation: None,
kind: DefKind::Let,
};
declarations.push_def(def);
@ -738,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,
);
}
}
}
@ -947,11 +947,6 @@ fn fix_values_captured_in_closure_expr(
loc_continuation,
..
}
| ExpectFx {
loc_condition,
loc_continuation,
..
}
| Dbg {
loc_message: loc_condition,
loc_continuation,
@ -969,6 +964,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

@ -1110,7 +1110,7 @@ fn flatten_str_lines(lines: &[&[StrSegment<'_>]]) -> Pattern {
Unicode(loc_digits) => {
todo!("parse unicode digits {:?}", loc_digits);
}
Interpolated(loc_expr) | DeprecatedInterpolated(loc_expr) => {
Interpolated(loc_expr) => {
return Pattern::UnsupportedPattern(loc_expr.region);
}
EscapedChar(escaped) => buf.push(escaped.unescape()),

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,8 @@ impl Scope {
self.aliases.contains_key(&name)
}
pub fn inner_scope<F, T>(&mut self, f: F) -> T
/// Enter an inner scope within a definition, e.g. a def or when block.
pub fn inner_def_scope<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Scope) -> T,
{
@ -462,6 +466,20 @@ impl Scope {
result
}
/// Enter an inner scope within a child function, e.g. a closure body.
pub fn inner_function_scope<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Scope) -> T,
{
let early_returns_snapshot = std::mem::take(&mut self.early_returns);
let result = self.inner_def_scope(f);
self.early_returns = early_returns_snapshot;
result
}
pub fn register_debug_idents(&self) {
self.home.register_debug_idents(&self.locals.ident_ids)
}
@ -868,7 +886,7 @@ mod test {
}
#[test]
fn inner_scope_does_not_influence_outer() {
fn inner_def_scope_does_not_influence_outer() {
let _register_module_debug_names = ModuleIds::default();
let mut scope = Scope::new(
ModuleId::ATTR,
@ -882,7 +900,7 @@ mod test {
assert!(scope.lookup(&ident, region).is_err());
scope.inner_scope(|inner| {
scope.inner_def_scope(|inner| {
assert!(inner.introduce(ident.clone(), region).is_ok());
});
@ -908,7 +926,7 @@ mod test {
}
#[test]
fn idents_with_inner_scope() {
fn idents_with_inner_def_scope() {
let _register_module_debug_names = ModuleIds::default();
let mut scope = Scope::new(
ModuleId::ATTR,
@ -943,7 +961,7 @@ mod test {
&[ident1.clone(), ident2.clone(), ident3.clone(),]
);
scope.inner_scope(|inner| {
scope.inner_def_scope(|inner| {
let ident4 = Ident::from("Ångström");
let ident5 = Ident::from("Sirály");

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),
}
@ -325,7 +282,14 @@ pub fn unwrap_suffixed_expression_apply_help<'a>(
let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via)));
return init_unwrapped_err(arena, new_apply, maybe_def_pat, target);
match maybe_def_pat {
Some(..) => {
return Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target });
}
None => {
return init_unwrapped_err(arena, new_apply, maybe_def_pat, target);
}
}
}
// function is another expression
@ -673,9 +637,10 @@ 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{..} | 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)),
StmtAfterExpr => None,
};
match maybe_suffixed_value_def {

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,
@ -101,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 }
@ -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,
@ -283,7 +292,7 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
visitor.visit_expr(&body.value, body.region, var);
}
Expr::Call(f, args, _called_via) => {
let (fn_var, loc_fn, _closure_var, _ret_var) = &**f;
let (fn_var, loc_fn, _closure_var, _ret_var, _fx_var) = &**f;
walk_call(visitor, *fn_var, loc_fn, args);
}
Expr::Crash { msg, .. } => {
@ -376,18 +385,6 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
Variable::NULL,
);
}
Expr::ExpectFx {
loc_condition,
loc_continuation,
lookups_in_cond: _,
} => {
visitor.visit_expr(&loc_condition.value, loc_condition.region, Variable::BOOL);
visitor.visit_expr(
&loc_continuation.value,
loc_continuation.region,
Variable::NULL,
);
}
Expr::Dbg {
variable,
source: _,
@ -403,6 +400,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 */ }
}