mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Reuse record destructure can for module params
This commit is contained in:
parent
7ac72159e9
commit
3a1d3d4ddb
2 changed files with 164 additions and 143 deletions
|
@ -8,7 +8,9 @@ use crate::env::Env;
|
||||||
use crate::expr::{
|
use crate::expr::{
|
||||||
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
|
ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives,
|
||||||
};
|
};
|
||||||
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows};
|
use crate::pattern::{
|
||||||
|
canonicalize_record_destructure, BindingsFromPattern, Pattern, PermitShadows,
|
||||||
|
};
|
||||||
use crate::procedure::References;
|
use crate::procedure::References;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -149,7 +151,7 @@ pub struct RigidVariables {
|
||||||
pub struct ModuleOutput {
|
pub struct ModuleOutput {
|
||||||
pub aliases: MutMap<Symbol, Alias>,
|
pub aliases: MutMap<Symbol, Alias>,
|
||||||
pub rigid_variables: RigidVariables,
|
pub rigid_variables: RigidVariables,
|
||||||
pub param_patterns: Vec<Pattern>,
|
pub params_pattern: Option<Pattern>,
|
||||||
pub declarations: Declarations,
|
pub declarations: Declarations,
|
||||||
pub exposed_imports: MutMap<Symbol, Region>,
|
pub exposed_imports: MutMap<Symbol, Region>,
|
||||||
pub exposed_symbols: VecSet<Symbol>,
|
pub exposed_symbols: VecSet<Symbol>,
|
||||||
|
@ -387,27 +389,25 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
|
|
||||||
let mut output = Output::default();
|
let mut output = Output::default();
|
||||||
|
|
||||||
let mut param_patterns = Vec::new();
|
let params_pattern = header_type.get_params().as_ref().map(
|
||||||
|
|ModuleParams {
|
||||||
if let Some(ModuleParams { pattern, .. }) = header_type.get_params() {
|
pattern,
|
||||||
param_patterns.reserve_exact(pattern.value.len());
|
before_arrow: _,
|
||||||
|
after_arrow: _,
|
||||||
for param in pattern.value.iter() {
|
}| {
|
||||||
let pattern = canonicalize_pattern(
|
canonicalize_record_destructure(
|
||||||
&mut env,
|
&mut env,
|
||||||
var_store,
|
var_store,
|
||||||
&mut scope,
|
&mut scope,
|
||||||
&mut output,
|
&mut output,
|
||||||
// todo(agus): custom type for param
|
// todo(agus): custom type for param
|
||||||
PatternType::FunctionArg,
|
PatternType::FunctionArg,
|
||||||
¶m.value,
|
&pattern.value,
|
||||||
param.region,
|
pattern.region,
|
||||||
PermitShadows(false),
|
PermitShadows(false),
|
||||||
);
|
)
|
||||||
|
},
|
||||||
param_patterns.push(pattern.value);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs(
|
let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs(
|
||||||
&mut env,
|
&mut env,
|
||||||
|
@ -839,7 +839,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
scope,
|
scope,
|
||||||
aliases,
|
aliases,
|
||||||
rigid_variables,
|
rigid_variables,
|
||||||
param_patterns,
|
params_pattern,
|
||||||
declarations,
|
declarations,
|
||||||
referenced_values,
|
referenced_values,
|
||||||
exposed_imports: can_exposed_imports,
|
exposed_imports: can_exposed_imports,
|
||||||
|
|
|
@ -623,132 +623,16 @@ pub fn canonicalize_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordDestructure(patterns) => {
|
RecordDestructure(patterns) => canonicalize_record_destructure(
|
||||||
let ext_var = var_store.fresh();
|
env,
|
||||||
let whole_var = var_store.fresh();
|
var_store,
|
||||||
let mut destructs = Vec::with_capacity(patterns.len());
|
scope,
|
||||||
let mut opt_erroneous = None;
|
output,
|
||||||
|
pattern_type,
|
||||||
for loc_pattern in patterns.iter() {
|
patterns,
|
||||||
match loc_pattern.value {
|
region,
|
||||||
Identifier { ident: label } => {
|
permit_shadows,
|
||||||
match scope.introduce(label.into(), region) {
|
),
|
||||||
Ok(symbol) => {
|
|
||||||
output.references.insert_bound(symbol);
|
|
||||||
|
|
||||||
destructs.push(Loc {
|
|
||||||
region: loc_pattern.region,
|
|
||||||
value: RecordDestruct {
|
|
||||||
var: var_store.fresh(),
|
|
||||||
label: Lowercase::from(label),
|
|
||||||
symbol,
|
|
||||||
typ: DestructType::Required,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err((shadowed_symbol, shadow, new_symbol)) => {
|
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
|
||||||
original_region: shadowed_symbol.region,
|
|
||||||
shadow: shadow.clone(),
|
|
||||||
kind: ShadowKind::Variable,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// No matter what the other patterns
|
|
||||||
// are, we're definitely shadowed and will
|
|
||||||
// get a runtime exception as soon as we
|
|
||||||
// encounter the first bad pattern.
|
|
||||||
opt_erroneous = Some(Pattern::Shadowed(
|
|
||||||
shadowed_symbol.region,
|
|
||||||
shadow,
|
|
||||||
new_symbol,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
RequiredField(label, loc_guard) => {
|
|
||||||
// a guard does not introduce the label into scope!
|
|
||||||
let symbol =
|
|
||||||
scope.scopeless_symbol(&Ident::from(label), loc_pattern.region);
|
|
||||||
let can_guard = canonicalize_pattern(
|
|
||||||
env,
|
|
||||||
var_store,
|
|
||||||
scope,
|
|
||||||
output,
|
|
||||||
pattern_type,
|
|
||||||
&loc_guard.value,
|
|
||||||
loc_guard.region,
|
|
||||||
permit_shadows,
|
|
||||||
);
|
|
||||||
|
|
||||||
destructs.push(Loc {
|
|
||||||
region: loc_pattern.region,
|
|
||||||
value: RecordDestruct {
|
|
||||||
var: var_store.fresh(),
|
|
||||||
label: Lowercase::from(label),
|
|
||||||
symbol,
|
|
||||||
typ: DestructType::Guard(var_store.fresh(), can_guard),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
OptionalField(label, loc_default) => {
|
|
||||||
// an optional DOES introduce the label into scope!
|
|
||||||
match scope.introduce(label.into(), region) {
|
|
||||||
Ok(symbol) => {
|
|
||||||
let (can_default, expr_output) = canonicalize_expr(
|
|
||||||
env,
|
|
||||||
var_store,
|
|
||||||
scope,
|
|
||||||
loc_default.region,
|
|
||||||
&loc_default.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
// an optional field binds the symbol!
|
|
||||||
output.references.insert_bound(symbol);
|
|
||||||
|
|
||||||
output.union(expr_output);
|
|
||||||
|
|
||||||
destructs.push(Loc {
|
|
||||||
region: loc_pattern.region,
|
|
||||||
value: RecordDestruct {
|
|
||||||
var: var_store.fresh(),
|
|
||||||
label: Lowercase::from(label),
|
|
||||||
symbol,
|
|
||||||
typ: DestructType::Optional(var_store.fresh(), can_default),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err((shadowed_symbol, shadow, new_symbol)) => {
|
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
|
||||||
original_region: shadowed_symbol.region,
|
|
||||||
shadow: shadow.clone(),
|
|
||||||
kind: ShadowKind::Variable,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// No matter what the other patterns
|
|
||||||
// are, we're definitely shadowed and will
|
|
||||||
// get a runtime exception as soon as we
|
|
||||||
// encounter the first bad pattern.
|
|
||||||
opt_erroneous = Some(Pattern::Shadowed(
|
|
||||||
shadowed_symbol.region,
|
|
||||||
shadow,
|
|
||||||
new_symbol,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => unreachable!("Any other pattern should have given a parse error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we encountered an erroneous pattern (e.g. one with shadowing),
|
|
||||||
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
|
|
||||||
opt_erroneous.unwrap_or(Pattern::RecordDestructure {
|
|
||||||
whole_var,
|
|
||||||
ext_var,
|
|
||||||
destructs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
RequiredField(_name, _loc_pattern) => {
|
RequiredField(_name, _loc_pattern) => {
|
||||||
unreachable!("should have been handled in RecordDestructure");
|
unreachable!("should have been handled in RecordDestructure");
|
||||||
|
@ -894,6 +778,143 @@ pub fn canonicalize_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn canonicalize_record_destructure<'a>(
|
||||||
|
env: &mut Env<'a>,
|
||||||
|
var_store: &mut VarStore,
|
||||||
|
scope: &mut Scope,
|
||||||
|
output: &mut Output,
|
||||||
|
pattern_type: PatternType,
|
||||||
|
patterns: &ast::Collection<Loc<ast::Pattern<'a>>>,
|
||||||
|
region: Region,
|
||||||
|
permit_shadows: PermitShadows,
|
||||||
|
) -> Pattern {
|
||||||
|
use ast::Pattern::*;
|
||||||
|
|
||||||
|
let ext_var = var_store.fresh();
|
||||||
|
let whole_var = var_store.fresh();
|
||||||
|
let mut destructs = Vec::with_capacity(patterns.len());
|
||||||
|
let mut opt_erroneous = None;
|
||||||
|
|
||||||
|
for loc_pattern in patterns.iter() {
|
||||||
|
match loc_pattern.value {
|
||||||
|
Identifier { ident: label } => {
|
||||||
|
match scope.introduce(label.into(), region) {
|
||||||
|
Ok(symbol) => {
|
||||||
|
output.references.insert_bound(symbol);
|
||||||
|
|
||||||
|
destructs.push(Loc {
|
||||||
|
region: loc_pattern.region,
|
||||||
|
value: RecordDestruct {
|
||||||
|
var: var_store.fresh(),
|
||||||
|
label: Lowercase::from(label),
|
||||||
|
symbol,
|
||||||
|
typ: DestructType::Required,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err((shadowed_symbol, shadow, new_symbol)) => {
|
||||||
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
|
original_region: shadowed_symbol.region,
|
||||||
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// No matter what the other patterns
|
||||||
|
// are, we're definitely shadowed and will
|
||||||
|
// get a runtime exception as soon as we
|
||||||
|
// encounter the first bad pattern.
|
||||||
|
opt_erroneous = Some(Pattern::Shadowed(
|
||||||
|
shadowed_symbol.region,
|
||||||
|
shadow,
|
||||||
|
new_symbol,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiredField(label, loc_guard) => {
|
||||||
|
// a guard does not introduce the label into scope!
|
||||||
|
let symbol = scope.scopeless_symbol(&Ident::from(label), loc_pattern.region);
|
||||||
|
let can_guard = canonicalize_pattern(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
scope,
|
||||||
|
output,
|
||||||
|
pattern_type,
|
||||||
|
&loc_guard.value,
|
||||||
|
loc_guard.region,
|
||||||
|
permit_shadows,
|
||||||
|
);
|
||||||
|
|
||||||
|
destructs.push(Loc {
|
||||||
|
region: loc_pattern.region,
|
||||||
|
value: RecordDestruct {
|
||||||
|
var: var_store.fresh(),
|
||||||
|
label: Lowercase::from(label),
|
||||||
|
symbol,
|
||||||
|
typ: DestructType::Guard(var_store.fresh(), can_guard),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
OptionalField(label, loc_default) => {
|
||||||
|
// an optional DOES introduce the label into scope!
|
||||||
|
match scope.introduce(label.into(), region) {
|
||||||
|
Ok(symbol) => {
|
||||||
|
let (can_default, expr_output) = canonicalize_expr(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
scope,
|
||||||
|
loc_default.region,
|
||||||
|
&loc_default.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
// an optional field binds the symbol!
|
||||||
|
output.references.insert_bound(symbol);
|
||||||
|
|
||||||
|
output.union(expr_output);
|
||||||
|
|
||||||
|
destructs.push(Loc {
|
||||||
|
region: loc_pattern.region,
|
||||||
|
value: RecordDestruct {
|
||||||
|
var: var_store.fresh(),
|
||||||
|
label: Lowercase::from(label),
|
||||||
|
symbol,
|
||||||
|
typ: DestructType::Optional(var_store.fresh(), can_default),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err((shadowed_symbol, shadow, new_symbol)) => {
|
||||||
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
|
original_region: shadowed_symbol.region,
|
||||||
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// No matter what the other patterns
|
||||||
|
// are, we're definitely shadowed and will
|
||||||
|
// get a runtime exception as soon as we
|
||||||
|
// encounter the first bad pattern.
|
||||||
|
opt_erroneous = Some(Pattern::Shadowed(
|
||||||
|
shadowed_symbol.region,
|
||||||
|
shadow,
|
||||||
|
new_symbol,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => unreachable!("Any other pattern should have given a parse error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we encountered an erroneous pattern (e.g. one with shadowing),
|
||||||
|
// use the resulting RuntimeError. Otherwise, return a successful record destructure.
|
||||||
|
opt_erroneous.unwrap_or(Pattern::RecordDestructure {
|
||||||
|
whole_var,
|
||||||
|
ext_var,
|
||||||
|
destructs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
|
/// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't
|
||||||
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
|
/// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern.
|
||||||
fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern {
|
fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue