mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Canonicalize and constrain statement expr in purity inference mode
This commit is contained in:
parent
460fa693fd
commit
6e6382ab23
14 changed files with 197 additions and 61 deletions
|
@ -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,23 @@ impl Def {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DefKind {
|
||||
/// A def that introduces identifiers
|
||||
Let,
|
||||
/// A standalone statement with an fx variable
|
||||
Stmt(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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Annotation {
|
||||
pub signature: Type,
|
||||
|
@ -195,22 +214,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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1189,29 +1211,29 @@ fn canonicalize_value_defs<'a>(
|
|||
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();
|
||||
if let Some(loc_pattern) = pending_def.loc_pattern() {
|
||||
let mut new_bindings = BindingsFromPattern::new(loc_pattern).peekable();
|
||||
|
||||
if new_bindings.peek().is_none() {
|
||||
env.problem(Problem::NoIdentifiersIntroduced(
|
||||
pending_def.loc_pattern().region,
|
||||
));
|
||||
}
|
||||
|
||||
for (s, r) in new_bindings {
|
||||
// store the top-level defs, used to ensure that closures won't capture them
|
||||
if let PatternType::TopLevelDef = pattern_type {
|
||||
env.top_level_symbols.insert(s);
|
||||
if new_bindings.peek().is_none() {
|
||||
env.problem(Problem::NoIdentifiersIntroduced(loc_pattern.region));
|
||||
}
|
||||
|
||||
symbols_introduced.insert(s, r);
|
||||
for (s, r) in new_bindings {
|
||||
// store the top-level defs, used to ensure that closures won't capture them
|
||||
if let PatternType::TopLevelDef = pattern_type {
|
||||
env.top_level_symbols.insert(s);
|
||||
}
|
||||
|
||||
debug_assert_eq!(env.home, s.module_id());
|
||||
debug_assert!(
|
||||
!symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()),
|
||||
"{s:?}"
|
||||
);
|
||||
symbols_introduced.insert(s, r);
|
||||
|
||||
symbol_to_index.push((s.ident_id(), def_index as u32));
|
||||
debug_assert_eq!(env.home, s.module_id());
|
||||
debug_assert!(
|
||||
!symbol_to_index.iter().any(|(id, _)| *id == s.ident_id()),
|
||||
"{s:?}"
|
||||
);
|
||||
|
||||
symbol_to_index.push((s.ident_id(), def_index as u32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2217,6 +2239,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,
|
||||
|
@ -2234,6 +2257,7 @@ fn single_can_def(
|
|||
},
|
||||
pattern_vars,
|
||||
annotation: def_annotation,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2374,6 +2398,7 @@ fn canonicalize_pending_value_def<'a>(
|
|||
expr_var,
|
||||
Some(Loc::at(loc_ann.region, type_annotation)),
|
||||
vars_by_symbol.clone(),
|
||||
DefKind::Let,
|
||||
);
|
||||
|
||||
DefOutput {
|
||||
|
@ -2409,6 +2434,7 @@ 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) => {
|
||||
|
@ -2421,6 +2447,20 @@ fn canonicalize_pending_value_def<'a>(
|
|||
loc_can_pattern,
|
||||
loc_expr,
|
||||
None,
|
||||
DefKind::Let,
|
||||
)
|
||||
}
|
||||
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 {
|
||||
|
@ -2461,6 +2501,7 @@ fn canonicalize_pending_value_def<'a>(
|
|||
var_store.fresh(),
|
||||
None,
|
||||
SendMap::default(),
|
||||
DefKind::Let,
|
||||
);
|
||||
|
||||
DefOutput {
|
||||
|
@ -2530,6 +2571,7 @@ fn canonicalize_pending_value_def<'a>(
|
|||
var_store.fresh(),
|
||||
opt_loc_can_ann,
|
||||
SendMap::default(),
|
||||
DefKind::Let,
|
||||
);
|
||||
|
||||
DefOutput {
|
||||
|
@ -2568,6 +2610,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;
|
||||
|
||||
|
@ -2686,6 +2729,7 @@ fn canonicalize_pending_body<'a>(
|
|||
expr_var,
|
||||
opt_loc_annotation,
|
||||
vars_by_symbol,
|
||||
kind,
|
||||
);
|
||||
|
||||
DefOutput {
|
||||
|
@ -3075,10 +3119,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.
|
||||
|
@ -3184,15 +3225,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))
|
||||
|
@ -3221,8 +3264,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
|
||||
|
@ -3242,7 +3289,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,
|
||||
|
@ -3257,14 +3306,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(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3279,12 +3326,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;
|
||||
|
@ -3293,10 +3340,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(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"),
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue