mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-02 19:32:17 +00:00
Permit binding variables multiple itmes in when branches
This commit is contained in:
parent
c48c4f3183
commit
bf8fc0d0de
6 changed files with 78 additions and 31 deletions
|
@ -621,11 +621,11 @@ fn can_annotation_help(
|
||||||
let symbol = match scope.introduce(name.value.into(), region) {
|
let symbol = match scope.introduce(name.value.into(), region) {
|
||||||
Ok(symbol) => symbol,
|
Ok(symbol) => symbol,
|
||||||
|
|
||||||
Err((original_region, shadow, _new_symbol)) => {
|
Err((shadowed_symbol, shadow, _new_symbol)) => {
|
||||||
let problem = Problem::Shadowed(original_region, shadow.clone());
|
let problem = Problem::Shadowed(shadowed_symbol.region, shadow.clone());
|
||||||
|
|
||||||
env.problem(roc_problem::can::Problem::Shadowing {
|
env.problem(roc_problem::can::Problem::Shadowing {
|
||||||
original_region,
|
original_region: shadowed_symbol.region,
|
||||||
shadow,
|
shadow,
|
||||||
kind: ShadowKind::Variable,
|
kind: ShadowKind::Variable,
|
||||||
});
|
});
|
||||||
|
|
|
@ -2312,9 +2312,9 @@ fn to_pending_type_def<'a>(
|
||||||
|
|
||||||
let member_sym = match scope.introduce(member_name.into(), name_region) {
|
let member_sym = match scope.introduce(member_name.into(), name_region) {
|
||||||
Ok(sym) => sym,
|
Ok(sym) => sym,
|
||||||
Err((original_region, shadow, _new_symbol)) => {
|
Err((shadowed_symbol, shadow, _new_symbol)) => {
|
||||||
env.problem(roc_problem::can::Problem::Shadowing {
|
env.problem(roc_problem::can::Problem::Shadowing {
|
||||||
original_region,
|
original_region: shadowed_symbol.region,
|
||||||
shadow,
|
shadow,
|
||||||
kind: ShadowKind::Variable,
|
kind: ShadowKind::Variable,
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::num::{
|
||||||
finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result,
|
finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result,
|
||||||
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
|
int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound,
|
||||||
};
|
};
|
||||||
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern};
|
use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows};
|
||||||
use crate::procedure::References;
|
use crate::procedure::References;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use crate::traverse::{walk_expr, Visitor};
|
use crate::traverse::{walk_expr, Visitor};
|
||||||
|
@ -1187,6 +1187,7 @@ fn canonicalize_closure_body<'a>(
|
||||||
FunctionArg,
|
FunctionArg,
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
PermitShadows(false),
|
||||||
);
|
);
|
||||||
|
|
||||||
can_args.push((
|
can_args.push((
|
||||||
|
@ -1290,6 +1291,7 @@ fn canonicalize_when_branch<'a>(
|
||||||
WhenBranch,
|
WhenBranch,
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
PermitShadows(true),
|
||||||
);
|
);
|
||||||
|
|
||||||
patterns.push(can_pattern);
|
patterns.push(can_pattern);
|
||||||
|
|
|
@ -227,10 +227,25 @@ pub fn canonicalize_def_header_pattern<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => canonicalize_pattern(env, var_store, scope, output, pattern_type, pattern, region),
|
_ => canonicalize_pattern(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
scope,
|
||||||
|
output,
|
||||||
|
pattern_type,
|
||||||
|
pattern,
|
||||||
|
region,
|
||||||
|
PermitShadows(false),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allow binding of symbols that appear shadowed.
|
||||||
|
///
|
||||||
|
/// For example, in the branch `A x | B x -> ...`, both pattern bind `x`; that's not a shadow!
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct PermitShadows(pub bool);
|
||||||
|
|
||||||
pub fn canonicalize_pattern<'a>(
|
pub fn canonicalize_pattern<'a>(
|
||||||
env: &mut Env<'a>,
|
env: &mut Env<'a>,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
@ -239,6 +254,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
pattern_type: PatternType,
|
pattern_type: PatternType,
|
||||||
pattern: &ast::Pattern<'a>,
|
pattern: &ast::Pattern<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
permit_shadows: PermitShadows,
|
||||||
) -> Loc<Pattern> {
|
) -> Loc<Pattern> {
|
||||||
use roc_parse::ast::Pattern::*;
|
use roc_parse::ast::Pattern::*;
|
||||||
use PatternType::*;
|
use PatternType::*;
|
||||||
|
@ -250,15 +266,21 @@ pub fn canonicalize_pattern<'a>(
|
||||||
|
|
||||||
Pattern::Identifier(symbol)
|
Pattern::Identifier(symbol)
|
||||||
}
|
}
|
||||||
Err((original_region, shadow, new_symbol)) => {
|
Err((shadowed_symbol, shadow, new_symbol)) => {
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
if permit_shadows.0 {
|
||||||
original_region,
|
output.references.insert_bound(shadowed_symbol.value);
|
||||||
shadow: shadow.clone(),
|
|
||||||
kind: ShadowKind::Variable,
|
|
||||||
}));
|
|
||||||
output.references.insert_bound(new_symbol);
|
|
||||||
|
|
||||||
Pattern::Shadowed(original_region, shadow, new_symbol)
|
Pattern::Identifier(shadowed_symbol.value)
|
||||||
|
} else {
|
||||||
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
|
original_region: shadowed_symbol.region,
|
||||||
|
shadow: shadow.clone(),
|
||||||
|
kind: ShadowKind::Variable,
|
||||||
|
}));
|
||||||
|
output.references.insert_bound(new_symbol);
|
||||||
|
|
||||||
|
Pattern::Shadowed(shadowed_symbol.region, shadow, new_symbol)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Tag(name) => {
|
Tag(name) => {
|
||||||
|
@ -289,6 +311,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
pattern_type,
|
pattern_type,
|
||||||
&loc_pattern.value,
|
&loc_pattern.value,
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
permit_shadows,
|
||||||
);
|
);
|
||||||
|
|
||||||
can_patterns.push((var_store.fresh(), can_pattern));
|
can_patterns.push((var_store.fresh(), can_pattern));
|
||||||
|
@ -457,6 +480,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
pattern_type,
|
pattern_type,
|
||||||
sub_pattern,
|
sub_pattern,
|
||||||
region,
|
region,
|
||||||
|
permit_shadows,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
RecordDestructure(patterns) => {
|
RecordDestructure(patterns) => {
|
||||||
|
@ -482,9 +506,9 @@ pub fn canonicalize_pattern<'a>(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err((original_region, shadow, new_symbol)) => {
|
Err((shadowed_symbol, shadow, new_symbol)) => {
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region: shadowed_symbol.region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
kind: ShadowKind::Variable,
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
@ -493,8 +517,11 @@ pub fn canonicalize_pattern<'a>(
|
||||||
// are, we're definitely shadowed and will
|
// are, we're definitely shadowed and will
|
||||||
// get a runtime exception as soon as we
|
// get a runtime exception as soon as we
|
||||||
// encounter the first bad pattern.
|
// encounter the first bad pattern.
|
||||||
opt_erroneous =
|
opt_erroneous = Some(Pattern::Shadowed(
|
||||||
Some(Pattern::Shadowed(original_region, shadow, new_symbol));
|
shadowed_symbol.region,
|
||||||
|
shadow,
|
||||||
|
new_symbol,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -511,6 +538,7 @@ pub fn canonicalize_pattern<'a>(
|
||||||
pattern_type,
|
pattern_type,
|
||||||
&loc_guard.value,
|
&loc_guard.value,
|
||||||
loc_guard.region,
|
loc_guard.region,
|
||||||
|
permit_shadows,
|
||||||
);
|
);
|
||||||
|
|
||||||
destructs.push(Loc {
|
destructs.push(Loc {
|
||||||
|
@ -550,9 +578,9 @@ pub fn canonicalize_pattern<'a>(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err((original_region, shadow, new_symbol)) => {
|
Err((shadowed_symbol, shadow, new_symbol)) => {
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
||||||
original_region,
|
original_region: shadowed_symbol.region,
|
||||||
shadow: shadow.clone(),
|
shadow: shadow.clone(),
|
||||||
kind: ShadowKind::Variable,
|
kind: ShadowKind::Variable,
|
||||||
}));
|
}));
|
||||||
|
@ -561,8 +589,11 @@ pub fn canonicalize_pattern<'a>(
|
||||||
// are, we're definitely shadowed and will
|
// are, we're definitely shadowed and will
|
||||||
// get a runtime exception as soon as we
|
// get a runtime exception as soon as we
|
||||||
// encounter the first bad pattern.
|
// encounter the first bad pattern.
|
||||||
opt_erroneous =
|
opt_erroneous = Some(Pattern::Shadowed(
|
||||||
Some(Pattern::Shadowed(original_region, shadow, new_symbol));
|
shadowed_symbol.region,
|
||||||
|
shadow,
|
||||||
|
new_symbol,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,7 +253,7 @@ impl Scope {
|
||||||
&mut self,
|
&mut self,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Result<Symbol, (Region, Loc<Ident>, Symbol)> {
|
) -> Result<Symbol, (Loc<Symbol>, Loc<Ident>, Symbol)> {
|
||||||
self.introduce_str(ident.as_str(), region)
|
self.introduce_str(ident.as_str(), region)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,17 +261,17 @@ impl Scope {
|
||||||
&mut self,
|
&mut self,
|
||||||
ident: &str,
|
ident: &str,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Result<Symbol, (Region, Loc<Ident>, Symbol)> {
|
) -> Result<Symbol, (Loc<Symbol>, Loc<Ident>, Symbol)> {
|
||||||
match self.introduce_help(ident, region) {
|
match self.introduce_help(ident, region) {
|
||||||
Ok(symbol) => Ok(symbol),
|
Ok(symbol) => Ok(symbol),
|
||||||
Err((_, original_region)) => {
|
Err((shadowed_symbol, original_region)) => {
|
||||||
let shadow = Loc {
|
let shadow = Loc {
|
||||||
value: Ident::from(ident),
|
value: Ident::from(ident),
|
||||||
region,
|
region,
|
||||||
};
|
};
|
||||||
let symbol = self.locals.scopeless_symbol(ident, region);
|
let symbol = self.locals.scopeless_symbol(ident, region);
|
||||||
|
|
||||||
Err((original_region, shadow, symbol))
|
Err((Loc::at(original_region, shadowed_symbol), shadow, symbol))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -638,13 +638,13 @@ mod test {
|
||||||
assert!(scope.lookup(&ident, Region::zero()).is_err());
|
assert!(scope.lookup(&ident, Region::zero()).is_err());
|
||||||
|
|
||||||
let first = scope.introduce(ident.clone(), region1).unwrap();
|
let first = scope.introduce(ident.clone(), region1).unwrap();
|
||||||
let (original_region, _ident, shadow_symbol) =
|
let (original, _ident, shadow_symbol) =
|
||||||
scope.introduce(ident.clone(), region2).unwrap_err();
|
scope.introduce(ident.clone(), region2).unwrap_err();
|
||||||
|
|
||||||
scope.register_debug_idents();
|
scope.register_debug_idents();
|
||||||
|
|
||||||
assert_ne!(first, shadow_symbol);
|
assert_ne!(first, shadow_symbol);
|
||||||
assert_eq!(original_region, region1);
|
assert_eq!(original.region, region1);
|
||||||
|
|
||||||
let lookup = scope.lookup(&ident, Region::zero()).unwrap();
|
let lookup = scope.lookup(&ident, Region::zero()).unwrap();
|
||||||
|
|
||||||
|
@ -805,13 +805,13 @@ mod test {
|
||||||
|
|
||||||
scope.import(ident.clone(), symbol, region1).unwrap();
|
scope.import(ident.clone(), symbol, region1).unwrap();
|
||||||
|
|
||||||
let (original_region, _ident, shadow_symbol) =
|
let (original, _ident, shadow_symbol) =
|
||||||
scope.introduce(ident.clone(), region2).unwrap_err();
|
scope.introduce(ident.clone(), region2).unwrap_err();
|
||||||
|
|
||||||
scope.register_debug_idents();
|
scope.register_debug_idents();
|
||||||
|
|
||||||
assert_ne!(symbol, shadow_symbol);
|
assert_ne!(symbol, shadow_symbol);
|
||||||
assert_eq!(original_region, region1);
|
assert_eq!(original.region, region1);
|
||||||
|
|
||||||
let lookup = scope.lookup(&ident, Region::zero()).unwrap();
|
let lookup = scope.lookup(&ident, Region::zero()).unwrap();
|
||||||
|
|
||||||
|
|
|
@ -7357,4 +7357,18 @@ mod solve_expr {
|
||||||
"List (A U8)",
|
"List (A U8)",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shared_pattern_variable_in_when_branches() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when A "" is
|
||||||
|
A x | B x -> x
|
||||||
|
C y | D y -> y
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue