mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
don't add guarded record fields into scope
This commit is contained in:
parent
85e2cf4465
commit
fd7ca5bcc9
8 changed files with 196 additions and 79 deletions
|
@ -267,45 +267,26 @@ pub fn canonicalize_pattern<'a>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
RecordField(label, loc_guard) => {
|
RecordField(label, loc_guard) => {
|
||||||
match scope.introduce(
|
// a guard does not introduce the label into scope!
|
||||||
label.into(),
|
let symbol = scope.ignore(label.into(), &mut env.ident_ids);
|
||||||
&env.exposed_ident_ids,
|
let can_guard = canonicalize_pattern(
|
||||||
&mut env.ident_ids,
|
env,
|
||||||
region,
|
var_store,
|
||||||
) {
|
scope,
|
||||||
Ok(symbol) => {
|
pattern_type,
|
||||||
let can_guard = canonicalize_pattern(
|
&loc_guard.value,
|
||||||
env,
|
loc_guard.region,
|
||||||
var_store,
|
);
|
||||||
scope,
|
|
||||||
pattern_type,
|
|
||||||
&loc_guard.value,
|
|
||||||
loc_guard.region,
|
|
||||||
);
|
|
||||||
|
|
||||||
destructs.push(Located {
|
destructs.push(Located {
|
||||||
region: loc_pattern.region,
|
region: loc_pattern.region,
|
||||||
value: RecordDestruct {
|
value: RecordDestruct {
|
||||||
var: var_store.fresh(),
|
var: var_store.fresh(),
|
||||||
label: Lowercase::from(label),
|
label: Lowercase::from(label),
|
||||||
symbol,
|
symbol,
|
||||||
guard: Some((var_store.fresh(), can_guard)),
|
guard: Some((var_store.fresh(), can_guard)),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
Err((original_region, shadow)) => {
|
|
||||||
env.problem(Problem::RuntimeError(RuntimeError::Shadowing {
|
|
||||||
original_region,
|
|
||||||
shadow: shadow.clone(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 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(original_region, shadow));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
_ => panic!("invalid pattern in record"),
|
_ => panic!("invalid pattern in record"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,10 +57,13 @@ impl Scope {
|
||||||
pub fn lookup(&mut self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
|
pub fn lookup(&mut self, ident: &Ident, region: Region) -> Result<Symbol, RuntimeError> {
|
||||||
match self.idents.get(ident) {
|
match self.idents.get(ident) {
|
||||||
Some((symbol, _)) => Ok(*symbol),
|
Some((symbol, _)) => Ok(*symbol),
|
||||||
None => Err(RuntimeError::LookupNotInScope(Located {
|
None => Err(RuntimeError::LookupNotInScope(
|
||||||
region,
|
Located {
|
||||||
value: ident.clone().into(),
|
region,
|
||||||
})),
|
value: ident.clone().into(),
|
||||||
|
},
|
||||||
|
self.idents.keys().map(|v| v.as_ref().into()).collect(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +110,14 @@ impl Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ignore an identifier.
|
||||||
|
///
|
||||||
|
/// Used for record guards like { x: Just _ }
|
||||||
|
pub fn ignore(&mut self, ident: Ident, all_ident_ids: &mut IdentIds) -> Symbol {
|
||||||
|
let ident_id = all_ident_ids.add(ident.into());
|
||||||
|
Symbol::new(self.home, ident_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Import a Symbol from another module into this module's top-level scope.
|
/// Import a Symbol from another module into this module's top-level scope.
|
||||||
///
|
///
|
||||||
/// Returns Err if this would shadow an existing ident, including the
|
/// Returns Err if this would shadow an existing ident, including the
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
|
use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::Ident;
|
use roc_module::ident::Ident;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
use roc_parse::operator::BinOp;
|
use roc_parse::operator::BinOp;
|
||||||
|
@ -37,7 +38,7 @@ pub enum RuntimeError {
|
||||||
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
// Example: (5 = 1 + 2) is an unsupported pattern in an assignment; Int patterns aren't allowed in assignments!
|
||||||
UnsupportedPattern(Region),
|
UnsupportedPattern(Region),
|
||||||
UnrecognizedFunctionName(Located<InlinableString>),
|
UnrecognizedFunctionName(Located<InlinableString>),
|
||||||
LookupNotInScope(Located<InlinableString>),
|
LookupNotInScope(Located<InlinableString>, MutSet<Box<str>>),
|
||||||
ValueNotExposed {
|
ValueNotExposed {
|
||||||
module_name: InlinableString,
|
module_name: InlinableString,
|
||||||
ident: InlinableString,
|
ident: InlinableString,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::report::ReportText::{BinOp, Concat, Module, Region, Value};
|
use crate::report::ReportText::{BinOp, Concat, Module, Region, Value};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||||
|
@ -130,8 +131,16 @@ pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
|
||||||
shadowing_report(&mut texts, original_region, shadow);
|
shadowing_report(&mut texts, original_region, shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
RuntimeError::LookupNotInScope(loc_name, options) => {
|
||||||
panic!("TODO implement run time error reporting");
|
texts.push(not_found(
|
||||||
|
loc_name.region,
|
||||||
|
&loc_name.value,
|
||||||
|
"value",
|
||||||
|
options,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
todo!("TODO implement run time error reporting for {:?}", other);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -156,6 +165,53 @@ fn shadowing_report(
|
||||||
texts.push(plain_text("Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."));
|
texts.push(plain_text("Since these variables have the same name, it's easy to use the wrong one on accident. Give one of them a new name."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn not_found(
|
||||||
|
region: roc_region::all::Region,
|
||||||
|
name: &str,
|
||||||
|
thing: &str,
|
||||||
|
options: MutSet<Box<str>>,
|
||||||
|
) -> ReportText {
|
||||||
|
use crate::type_error::suggest;
|
||||||
|
|
||||||
|
let mut suggestions = suggest::sort(name, options.iter().map(|v| v.as_ref()).collect());
|
||||||
|
suggestions.truncate(4);
|
||||||
|
|
||||||
|
let to_details = |no_suggestion_details, yes_suggestion_details| {
|
||||||
|
if suggestions.is_empty() {
|
||||||
|
no_suggestion_details
|
||||||
|
} else {
|
||||||
|
ReportText::Stack(vec![
|
||||||
|
yes_suggestion_details,
|
||||||
|
ReportText::Indent(
|
||||||
|
4,
|
||||||
|
Box::new(ReportText::Stack(
|
||||||
|
suggestions
|
||||||
|
.into_iter()
|
||||||
|
.map(|v: &str| plain_text(v))
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let default_no = ReportText::Concat(vec![
|
||||||
|
plain_text("Is there an "),
|
||||||
|
keyword_text("import"),
|
||||||
|
plain_text(" or "),
|
||||||
|
keyword_text("exposing"),
|
||||||
|
plain_text(" missing up-top?"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let default_yes = plain_text("these names seem close though:");
|
||||||
|
|
||||||
|
ReportText::Stack(vec![
|
||||||
|
plain_text(&format!("I cannot find a `{}` {}", name, thing)),
|
||||||
|
ReportText::Region(region),
|
||||||
|
to_details(default_no, default_yes),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ReportText {
|
pub enum ReportText {
|
||||||
/// A value. Render it qualified unless it was defined in the current module.
|
/// A value. Render it qualified unless it was defined in the current module.
|
||||||
|
|
|
@ -882,6 +882,12 @@ pub mod suggest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToStr for &str {
|
||||||
|
fn to_str(&self) -> &str {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sort<'a, T>(typo: &'a str, mut options: Vec<T>) -> Vec<T>
|
pub fn sort<'a, T>(typo: &'a str, mut options: Vec<T>) -> Vec<T>
|
||||||
where
|
where
|
||||||
T: ToStr,
|
T: ToStr,
|
||||||
|
|
|
@ -1500,36 +1500,98 @@ mod test_reporting {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently hits a bug where `x` is marked as unused
|
#[test]
|
||||||
// https://github.com/rtfeldman/roc/issues/304
|
fn pattern_guard_mismatch() {
|
||||||
// #[test]
|
report_problem_as(
|
||||||
// fn pattern_guard_mismatch() {
|
indoc!(
|
||||||
// report_problem_as(
|
r#"
|
||||||
// indoc!(
|
when { foo: 1 } is
|
||||||
// r#"
|
{ foo: True } -> 42
|
||||||
// when { foo: 1 } is
|
"#
|
||||||
// { x: True } -> 42
|
),
|
||||||
// "#
|
indoc!(
|
||||||
// ),
|
r#"
|
||||||
// indoc!(
|
The 1st pattern in this `when` is causing a mismatch:
|
||||||
// r#"
|
|
||||||
// The 2nd pattern in this `when` does not match the previous ones:
|
2 ┆ { foo: True } -> 42
|
||||||
//
|
┆ ^^^^^^^^^^^^^
|
||||||
// 3 ┆ {} -> 42
|
|
||||||
// ┆ ^^
|
The first pattern is trying to match record values of type:
|
||||||
//
|
|
||||||
// The 2nd pattern is trying to match record values of type:
|
{ foo : [ True ]a }
|
||||||
//
|
|
||||||
// {}a
|
But the expression between `when` and `is` has the type:
|
||||||
//
|
|
||||||
// But all the previous branches match:
|
{ foo : Num a }
|
||||||
//
|
|
||||||
// Num a
|
"#
|
||||||
//
|
),
|
||||||
// "#
|
)
|
||||||
// ),
|
}
|
||||||
// )
|
|
||||||
// }
|
#[test]
|
||||||
|
fn pattern_guard_does_not_bind_label() {
|
||||||
|
// needs some improvement, but the principle works
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when { foo: 1 } is
|
||||||
|
{ foo: 2 } -> foo
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
I cannot find a `foo` value
|
||||||
|
|
||||||
|
|
||||||
|
2 ┆ { foo: 2 } -> foo
|
||||||
|
┆ ^^^
|
||||||
|
|
||||||
|
|
||||||
|
these names seem close though:
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Num
|
||||||
|
Map
|
||||||
|
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pattern_guard_can_be_shadowed_above() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
foo = 3
|
||||||
|
|
||||||
|
when { foo: 1 } is
|
||||||
|
{ foo: 2 } -> foo
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
// should give no error
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pattern_guard_can_be_shadowed_below() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when { foo: 1 } is
|
||||||
|
{ foo: 2 } ->
|
||||||
|
foo = 3
|
||||||
|
|
||||||
|
foo
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
// should give no error
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pattern_or_pattern_mismatch() {
|
fn pattern_or_pattern_mismatch() {
|
||||||
|
|
|
@ -1004,7 +1004,7 @@ mod test_solve {
|
||||||
xEmpty = if thunk {} == 42 then { x: {} } else { x: {} }
|
xEmpty = if thunk {} == 42 then { x: {} } else { x: {} }
|
||||||
|
|
||||||
when xEmpty is
|
when xEmpty is
|
||||||
{ x: {} } -> x
|
{ x: {} } -> {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"{}",
|
"{}",
|
||||||
|
@ -1138,7 +1138,7 @@ mod test_solve {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when { x: 5 } is
|
when { x: 5 } is
|
||||||
{ x: 4 } -> x
|
{ x: 4 } -> 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Num *",
|
"Num *",
|
||||||
|
|
|
@ -908,7 +908,7 @@ mod test_uniq_solve {
|
||||||
xEmpty = if thunk {} == 42 then { x: {} } else { x: {} }
|
xEmpty = if thunk {} == 42 then { x: {} } else { x: {} }
|
||||||
|
|
||||||
when xEmpty is
|
when xEmpty is
|
||||||
{ x: {} } -> x
|
{ x: {} } -> {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * {}",
|
"Attr * {}",
|
||||||
|
@ -1046,7 +1046,7 @@ mod test_uniq_solve {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when { x: 5 } is
|
when { x: 5 } is
|
||||||
{ x: 4 } -> x
|
{ x: 4 } -> 4
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
"Attr * (Num (Attr * *))",
|
"Attr * (Num (Attr * *))",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue