Canonicalize and constrain statement expr in purity inference mode

This commit is contained in:
Agus Zubiaga 2024-10-30 19:26:11 -03:00
parent 460fa693fd
commit 6e6382ab23
No known key found for this signature in database
14 changed files with 197 additions and 61 deletions

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,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))
}
}
}