Permit binding variables multiple itmes in when branches

This commit is contained in:
Ayaz Hafiz 2022-07-21 11:06:40 -04:00
parent c48c4f3183
commit bf8fc0d0de
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
6 changed files with 78 additions and 31 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
"#
),
"",
);
}
} }