From 6da37329e7783285619a48e3e335bd96213040cb Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 23 Dec 2019 13:52:24 +0100 Subject: [PATCH] extract logic for identifiers --- src/can/pattern.rs | 146 ++++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/src/can/pattern.rs b/src/can/pattern.rs index 49f95a46ef..1134e95907 100644 --- a/src/can/pattern.rs +++ b/src/can/pattern.rs @@ -75,73 +75,9 @@ pub fn canonicalize_pattern<'a>( let can_pattern = match &pattern { &Identifier(ref name) => { - let lowercase_ident = Ident::Unqualified((*name).into()); - - // We use shadowable_idents for this, and not scope, because for assignments - // they are different. When canonicalizing a particular assignment, that new - // ident is in scope (for recursion) but not shadowable. - // - // For example, when canonicalizing (fibonacci = ...), `fibonacci` should be in scope - // so that it can refer to itself without getting a naming problem, but it should not - // be in the collection of shadowable idents because you can't shadow yourself! - match shadowable_idents.get(&lowercase_ident) { - Some((_, region)) => { - let loc_shadowed_ident = Located { - region: *region, - value: lowercase_ident, - }; - - // This is already in scope, meaning it's about to be shadowed. - // Shadowing is not allowed! - env.problem(Problem::Shadowing(loc_shadowed_ident.clone())); - - // Change this Pattern to a Shadowed variant, so that - // codegen knows to generate a runtime exception here. - Pattern::Shadowed(loc_shadowed_ident) - } - None => { - // Make sure we aren't shadowing something in the home module's scope. - let qualified_ident = - Ident::Qualified(env.home.clone(), lowercase_ident.name()); - - match scope.idents.get(&qualified_ident) { - Some((_, region)) => { - let loc_shadowed_ident = Located { - region: *region, - value: qualified_ident, - }; - - // This is already in scope, meaning it's about to be shadowed. - // Shadowing is not allowed! - env.problem(Problem::Shadowing(loc_shadowed_ident.clone())); - - // Change this Pattern to a Shadowed variant, so that - // codegen knows to generate a runtime exception here. - Pattern::Shadowed(loc_shadowed_ident) - } - None => { - let new_ident = qualified_ident.clone(); - let new_name = qualified_ident.name(); - let symbol = scope.symbol(&new_name); - - // This is a fresh identifier that wasn't already in scope. - // Add it to scope! - let symbol_and_region = (symbol.clone(), region); - - // Add this to both scope.idents *and* shadowable_idents. - // The latter is relevant when recursively canonicalizing - // tag application patterns, which can bring multiple - // new idents into scope. For example, it's important that - // we catch (Blah foo foo) -> … as being an example of shadowing. - scope - .idents - .insert(new_ident.clone(), symbol_and_region.clone()); - shadowable_idents.insert(new_ident, symbol_and_region); - - Pattern::Identifier(symbol) - } - } - } + match canonicalize_pattern_identifier(name, env, scope, region, shadowable_idents) { + Ok(symbol) => Pattern::Identifier(symbol), + Err(loc_shadowed_ident) => Pattern::Shadowed(loc_shadowed_ident), } } &GlobalTag(name) => { @@ -247,6 +183,82 @@ pub fn canonicalize_pattern<'a>( } } +pub fn canonicalize_pattern_identifier<'a>( + name: &'a &str, + env: &'a mut Env, + scope: &mut Scope, + region: Region, + shadowable_idents: &'a mut ImMap, +) -> Result> { + let lowercase_ident = Ident::Unqualified((*name).into()); + + // We use shadowable_idents for this, and not scope, because for assignments + // they are different. When canonicalizing a particular assignment, that new + // ident is in scope (for recursion) but not shadowable. + // + // For example, when canonicalizing (fibonacci = ...), `fibonacci` should be in scope + // so that it can refer to itself without getting a naming problem, but it should not + // be in the collection of shadowable idents because you can't shadow yourself! + match shadowable_idents.get(&lowercase_ident) { + Some((_, region)) => { + let loc_shadowed_ident = Located { + region: *region, + value: lowercase_ident, + }; + + // This is already in scope, meaning it's about to be shadowed. + // Shadowing is not allowed! + env.problem(Problem::Shadowing(loc_shadowed_ident.clone())); + + // Change this Pattern to a Shadowed variant, so that + // codegen knows to generate a runtime exception here. + Err(loc_shadowed_ident) + } + None => { + // Make sure we aren't shadowing something in the home module's scope. + let qualified_ident = Ident::Qualified(env.home.clone(), lowercase_ident.name()); + + match scope.idents.get(&qualified_ident) { + Some((_, region)) => { + let loc_shadowed_ident = Located { + region: *region, + value: qualified_ident, + }; + + // This is already in scope, meaning it's about to be shadowed. + // Shadowing is not allowed! + env.problem(Problem::Shadowing(loc_shadowed_ident.clone())); + + // Change this Pattern to a Shadowed variant, so that + // codegen knows to generate a runtime exception here. + Err(loc_shadowed_ident) + } + None => { + let new_ident = qualified_ident.clone(); + let new_name = qualified_ident.name(); + let symbol = scope.symbol(&new_name); + + // This is a fresh identifier that wasn't already in scope. + // Add it to scope! + let symbol_and_region = (symbol.clone(), region); + + // Add this to both scope.idents *and* shadowable_idents. + // The latter is relevant when recursively canonicalizing + // tag application patterns, which can bring multiple + // new idents into scope. For example, it's important that + // we catch (Blah foo foo) -> … as being an example of shadowing. + scope + .idents + .insert(new_ident.clone(), symbol_and_region.clone()); + shadowable_idents.insert(new_ident, symbol_and_region); + + Ok(symbol) + } + } + } + } +} + /// 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. fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern {