mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
make a Index data type
This commit is contained in:
parent
ab19529077
commit
2811f978a4
@ -93,3 +93,40 @@ where
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct Index(usize);
|
||||
|
||||
impl Index {
|
||||
pub const FIRST: Self = Index(0);
|
||||
|
||||
pub fn zero_based(i: usize) -> Self {
|
||||
Index(i)
|
||||
}
|
||||
|
||||
pub fn one_based(i: usize) -> Self {
|
||||
Index(i - 1)
|
||||
}
|
||||
|
||||
pub fn ordinal(self) -> std::string::String {
|
||||
int_to_ordinal(self.0 + 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn int_to_ordinal(number: usize) -> std::string::String {
|
||||
// NOTE: one-based
|
||||
let remainder10 = number % 10;
|
||||
let remainder100 = number % 100;
|
||||
|
||||
let ending = match remainder100 {
|
||||
11..=13 => "th",
|
||||
_ => match remainder10 {
|
||||
1 => "st",
|
||||
2 => "nd",
|
||||
3 => "rd",
|
||||
_ => "th",
|
||||
},
|
||||
};
|
||||
|
||||
format!("{}{}", number, ending)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use roc_can::expected::PExpected;
|
||||
use roc_can::expr::Expr::{self, *};
|
||||
use roc_can::expr::{Field, WhenBranch};
|
||||
use roc_can::pattern::Pattern;
|
||||
use roc_collections::all::{ImMap, SendMap};
|
||||
use roc_collections::all::{ImMap, Index, SendMap};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::{Located, Region};
|
||||
@ -156,7 +156,7 @@ pub fn constrain_expr(
|
||||
cons.push(con);
|
||||
}
|
||||
|
||||
let fields_type = Type::Record(fields.clone(), Box::new(Type::Variable(*ext_var)));
|
||||
let fields_type = Type::Record(fields, Box::new(Type::Variable(*ext_var)));
|
||||
let record_type = Type::Variable(*record_var);
|
||||
|
||||
// NOTE from elm compiler: fields_type is separate so that Error propagates better
|
||||
@ -211,7 +211,9 @@ pub fn constrain_expr(
|
||||
|
||||
for (index, loc_elem) in loc_elems.iter().enumerate() {
|
||||
let elem_expected = ForReason(
|
||||
Reason::ElemInList { index },
|
||||
Reason::ElemInList {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
list_elem_type.clone(),
|
||||
loc_elem.region,
|
||||
);
|
||||
@ -270,7 +272,7 @@ pub fn constrain_expr(
|
||||
|
||||
let reason = Reason::FnArg {
|
||||
name: opt_symbol,
|
||||
arg_index: index as u8,
|
||||
arg_index: Index::zero_based(index),
|
||||
};
|
||||
let expected_arg = ForReason(reason, arg_type.clone(), region);
|
||||
let arg_con = constrain_expr(env, loc_arg.region, &loc_arg.value, expected_arg);
|
||||
@ -401,7 +403,10 @@ pub fn constrain_expr(
|
||||
FromAnnotation(
|
||||
name.clone(),
|
||||
arity,
|
||||
AnnotationSource::TypedIfBranch(index + 1),
|
||||
AnnotationSource::TypedIfBranch {
|
||||
index: Index::zero_based(index),
|
||||
num_branches: branches.len(),
|
||||
},
|
||||
tipe.clone(),
|
||||
),
|
||||
);
|
||||
@ -416,7 +421,10 @@ pub fn constrain_expr(
|
||||
FromAnnotation(
|
||||
name,
|
||||
arity,
|
||||
AnnotationSource::TypedIfBranch(branches.len() + 1),
|
||||
AnnotationSource::TypedIfBranch {
|
||||
index: Index::zero_based(branches.len()),
|
||||
num_branches: branches.len(),
|
||||
},
|
||||
tipe.clone(),
|
||||
),
|
||||
);
|
||||
@ -448,7 +456,7 @@ pub fn constrain_expr(
|
||||
&loc_body.value,
|
||||
ForReason(
|
||||
Reason::IfBranch {
|
||||
index: index + 1,
|
||||
index: Index::zero_based(index),
|
||||
total_branches: branches.len(),
|
||||
},
|
||||
Type::Variable(*branch_var),
|
||||
@ -465,7 +473,7 @@ pub fn constrain_expr(
|
||||
&final_else.value,
|
||||
ForReason(
|
||||
Reason::IfBranch {
|
||||
index: branches.len() + 1,
|
||||
index: Index::zero_based(branches.len()),
|
||||
total_branches: branches.len() + 1,
|
||||
},
|
||||
Type::Variable(*branch_var),
|
||||
@ -521,14 +529,16 @@ pub fn constrain_expr(
|
||||
when_branch.value.region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch { index },
|
||||
PReason::WhenMatch {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
Region::across_all(when_branch.patterns.iter().map(|v| &v.region)),
|
||||
),
|
||||
FromAnnotation(
|
||||
name.clone(),
|
||||
*arity,
|
||||
TypedWhenBranch(index + 1),
|
||||
TypedWhenBranch(Index::zero_based(index)),
|
||||
typ.clone(),
|
||||
),
|
||||
);
|
||||
@ -547,12 +557,16 @@ pub fn constrain_expr(
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch { index },
|
||||
PReason::WhenMatch {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
Region::across_all(when_branch.patterns.iter().map(|v| &v.region)),
|
||||
),
|
||||
ForReason(
|
||||
Reason::WhenBranch { index },
|
||||
Reason::WhenBranch {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
branch_type.clone(),
|
||||
when_branch.value.region,
|
||||
),
|
||||
|
@ -3,7 +3,7 @@ use roc_can::constraint::Constraint;
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::pattern::Pattern::{self, *};
|
||||
use roc_can::pattern::RecordDestruct;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_collections::all::{Index, SendMap};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
@ -256,7 +256,7 @@ pub fn constrain_pattern(
|
||||
let expected = PExpected::ForReason(
|
||||
PReason::TagArg {
|
||||
tag_name: tag_name.clone(),
|
||||
index,
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
pattern_type,
|
||||
region,
|
||||
|
@ -6,7 +6,7 @@ use roc_can::def::{Declaration, Def};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::expr::{Expr, Field, WhenBranch};
|
||||
use roc_can::pattern::{Pattern, RecordDestruct};
|
||||
use roc_collections::all::{ImMap, ImSet, SendMap};
|
||||
use roc_collections::all::{ImMap, ImSet, Index, SendMap};
|
||||
use roc_module::ident::{Ident, Lowercase};
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_region::all::{Located, Region};
|
||||
@ -598,7 +598,9 @@ pub fn constrain_expr(
|
||||
|
||||
for (index, loc_elem) in loc_elems.iter().enumerate() {
|
||||
let elem_expected = Expected::ForReason(
|
||||
Reason::ElemInList { index },
|
||||
Reason::ElemInList {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
entry_type.clone(),
|
||||
region,
|
||||
);
|
||||
@ -755,7 +757,7 @@ pub fn constrain_expr(
|
||||
|
||||
let reason = Reason::FnArg {
|
||||
name: opt_symbol,
|
||||
arg_index: index as u8,
|
||||
arg_index: Index::zero_based(index),
|
||||
};
|
||||
|
||||
let expected_arg = Expected::ForReason(reason, arg_type.clone(), region);
|
||||
@ -936,7 +938,10 @@ pub fn constrain_expr(
|
||||
Expected::FromAnnotation(
|
||||
name.clone(),
|
||||
arity,
|
||||
AnnotationSource::TypedIfBranch(index + 1),
|
||||
AnnotationSource::TypedIfBranch {
|
||||
index: Index::zero_based(index),
|
||||
num_branches: branches.len(),
|
||||
},
|
||||
tipe.clone(),
|
||||
),
|
||||
);
|
||||
@ -954,7 +959,10 @@ pub fn constrain_expr(
|
||||
Expected::FromAnnotation(
|
||||
name,
|
||||
arity,
|
||||
AnnotationSource::TypedIfBranch(branches.len() + 1),
|
||||
AnnotationSource::TypedIfBranch {
|
||||
index: Index::zero_based(branches.len()),
|
||||
num_branches: branches.len(),
|
||||
},
|
||||
tipe.clone(),
|
||||
),
|
||||
);
|
||||
@ -1003,7 +1011,7 @@ pub fn constrain_expr(
|
||||
&loc_body.value,
|
||||
Expected::ForReason(
|
||||
Reason::IfBranch {
|
||||
index: index + 1,
|
||||
index: Index::zero_based(index),
|
||||
total_branches: branches.len(),
|
||||
},
|
||||
Type::Variable(*branch_var),
|
||||
@ -1023,7 +1031,7 @@ pub fn constrain_expr(
|
||||
&final_else.value,
|
||||
Expected::ForReason(
|
||||
Reason::IfBranch {
|
||||
index: branches.len() + 1,
|
||||
index: Index::zero_based(branches.len()),
|
||||
total_branches: branches.len(),
|
||||
},
|
||||
Type::Variable(*branch_var),
|
||||
@ -1085,14 +1093,16 @@ pub fn constrain_expr(
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch { index },
|
||||
PReason::WhenMatch {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
region,
|
||||
),
|
||||
Expected::FromAnnotation(
|
||||
name.clone(),
|
||||
*arity,
|
||||
TypedWhenBranch(index),
|
||||
TypedWhenBranch(Index::zero_based(index)),
|
||||
typ.clone(),
|
||||
),
|
||||
);
|
||||
@ -1118,12 +1128,16 @@ pub fn constrain_expr(
|
||||
region,
|
||||
when_branch,
|
||||
PExpected::ForReason(
|
||||
PReason::WhenMatch { index },
|
||||
PReason::WhenMatch {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
cond_type.clone(),
|
||||
region,
|
||||
),
|
||||
Expected::ForReason(
|
||||
Reason::WhenBranch { index },
|
||||
Reason::WhenBranch {
|
||||
index: Index::zero_based(index),
|
||||
},
|
||||
branch_type.clone(),
|
||||
region,
|
||||
),
|
||||
@ -1176,7 +1190,7 @@ pub fn constrain_expr(
|
||||
|
||||
let fields_type = attr_type(
|
||||
Bool::variable(uniq_var),
|
||||
Type::Record(fields.clone(), Box::new(Type::Variable(*ext_var))),
|
||||
Type::Record(fields, Box::new(Type::Variable(*ext_var))),
|
||||
);
|
||||
let record_type = Type::Variable(*record_var);
|
||||
|
||||
|
@ -612,6 +612,7 @@ define_builtins! {
|
||||
8 FLOAT_ADD: "#add"
|
||||
9 FLOAT_SUB: "#sub"
|
||||
10 FLOAT_EQ: "#eq"
|
||||
11 FLOAT_ROUND: "round"
|
||||
}
|
||||
4 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
@ -1,5 +1,5 @@
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_collections::all::{MutSet, SendMap};
|
||||
use roc_collections::all::{Index, MutSet, SendMap};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_solve::solve;
|
||||
@ -32,24 +32,6 @@ pub fn type_problem<'b>(
|
||||
}
|
||||
}
|
||||
|
||||
fn int_to_ordinal(number: usize) -> String {
|
||||
// NOTE: one-based
|
||||
let remainder10 = number % 10;
|
||||
let remainder100 = number % 100;
|
||||
|
||||
let ending = match remainder100 {
|
||||
11..=13 => "th",
|
||||
_ => match remainder10 {
|
||||
1 => "st",
|
||||
2 => "nd",
|
||||
3 => "rd",
|
||||
_ => "th",
|
||||
},
|
||||
};
|
||||
|
||||
format!("{}{}", number, ending)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn report_mismatch<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
@ -162,22 +144,34 @@ fn to_expr_report<'b>(
|
||||
|
||||
let (the_name_text, on_name_text) = match pattern_to_doc(alloc, &name.value) {
|
||||
Some(doc) => (
|
||||
alloc.concat(vec![alloc.text("the "), doc.clone()]),
|
||||
alloc.concat(vec![alloc.text(" on "), doc]),
|
||||
alloc.concat(vec![alloc.reflow("the "), doc.clone()]),
|
||||
alloc.concat(vec![alloc.reflow(" on "), doc]),
|
||||
),
|
||||
None => (alloc.text("this"), alloc.nil()),
|
||||
};
|
||||
|
||||
// TODO special-case 2-branch if
|
||||
let thing = match annotation_source {
|
||||
TypedIfBranch(index) => alloc.concat(vec![
|
||||
alloc.string(format!("{}", int_to_ordinal(index))),
|
||||
TypedIfBranch {
|
||||
index,
|
||||
num_branches,
|
||||
} if num_branches == 2 => alloc.concat(vec![
|
||||
alloc.keyword(if index == Index::FIRST {
|
||||
"then"
|
||||
} else {
|
||||
"else"
|
||||
}),
|
||||
alloc.reflow(" branch of this "),
|
||||
alloc.keyword("if"),
|
||||
alloc.text(" expression:"),
|
||||
]),
|
||||
TypedIfBranch { index, .. } => alloc.concat(vec![
|
||||
alloc.string(index.ordinal()),
|
||||
alloc.reflow(" branch of this "),
|
||||
alloc.keyword("if"),
|
||||
alloc.text(" expression:"),
|
||||
]),
|
||||
TypedWhenBranch(index) => alloc.concat(vec![
|
||||
alloc.string(format!("{}", int_to_ordinal(index))),
|
||||
alloc.string(index.ordinal()),
|
||||
alloc.reflow(" branch of this "),
|
||||
alloc.keyword("when"),
|
||||
alloc.text(" expression:"),
|
||||
@ -190,8 +184,8 @@ fn to_expr_report<'b>(
|
||||
};
|
||||
|
||||
let it_is = match annotation_source {
|
||||
TypedIfBranch(index) => format!("The {} branch is", int_to_ordinal(index)),
|
||||
TypedWhenBranch(index) => format!("The {} branch is", int_to_ordinal(index)),
|
||||
TypedIfBranch { index, .. } => format!("The {} branch is", index.ordinal()),
|
||||
TypedWhenBranch(index) => format!("The {} branch is", index.ordinal()),
|
||||
TypedBody => "The body is".into(),
|
||||
};
|
||||
|
||||
@ -325,39 +319,7 @@ fn to_expr_report<'b>(
|
||||
alloc.text(" to have the same type!"),
|
||||
])),
|
||||
),
|
||||
_ => {
|
||||
let ith = int_to_ordinal(index);
|
||||
|
||||
report_mismatch(
|
||||
alloc,
|
||||
filename,
|
||||
&category,
|
||||
found,
|
||||
expected_type,
|
||||
region,
|
||||
Some(expr_region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
alloc.string(format!("{}", ith)),
|
||||
alloc.reflow(" branch of this "),
|
||||
alloc.keyword("if"),
|
||||
alloc.reflow(" does not match all the previous branches:"),
|
||||
]),
|
||||
alloc.string(format!("The {} branch is", ith)),
|
||||
alloc.reflow("But all the previous branches have type:"),
|
||||
Some(alloc.concat(vec![
|
||||
alloc.reflow("I need all branches in an "),
|
||||
alloc.keyword("if"),
|
||||
alloc.reflow(" to have the same type!"),
|
||||
])),
|
||||
)
|
||||
}
|
||||
},
|
||||
Reason::WhenBranch { index } => {
|
||||
// NOTE: is 0-based
|
||||
let ith = int_to_ordinal(index + 1);
|
||||
|
||||
report_mismatch(
|
||||
_ => report_mismatch(
|
||||
alloc,
|
||||
filename,
|
||||
&category,
|
||||
@ -367,28 +329,49 @@ fn to_expr_report<'b>(
|
||||
Some(expr_region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
alloc.string(format!("{}", ith)),
|
||||
alloc.string(index.ordinal()),
|
||||
alloc.reflow(" branch of this "),
|
||||
alloc.keyword("when"),
|
||||
alloc.keyword("if"),
|
||||
alloc.reflow(" does not match all the previous branches:"),
|
||||
]),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
alloc.string(format!("{}", ith)),
|
||||
alloc.reflow(" branch is"),
|
||||
]),
|
||||
alloc.string(format!("The {} branch is", index.ordinal())),
|
||||
alloc.reflow("But all the previous branches have type:"),
|
||||
Some(alloc.concat(vec![
|
||||
alloc.reflow("I need all branches of a "),
|
||||
alloc.keyword("when"),
|
||||
alloc.reflow("I need all branches in an "),
|
||||
alloc.keyword("if"),
|
||||
alloc.reflow(" to have the same type!"),
|
||||
])),
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
Reason::WhenBranch { index } => report_mismatch(
|
||||
alloc,
|
||||
filename,
|
||||
&category,
|
||||
found,
|
||||
expected_type,
|
||||
region,
|
||||
Some(expr_region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
alloc.string(index.ordinal()),
|
||||
alloc.reflow(" branch of this "),
|
||||
alloc.keyword("when"),
|
||||
alloc.reflow(" does not match all the previous branches:"),
|
||||
]),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
alloc.string(index.ordinal()),
|
||||
alloc.reflow(" branch is"),
|
||||
]),
|
||||
alloc.reflow("But all the previous branches have type:"),
|
||||
Some(alloc.concat(vec![
|
||||
alloc.reflow("I need all branches of a "),
|
||||
alloc.keyword("when"),
|
||||
alloc.reflow(" to have the same type!"),
|
||||
])),
|
||||
),
|
||||
Reason::ElemInList { index } => {
|
||||
// NOTE: is 0-based
|
||||
|
||||
let ith = int_to_ordinal(index + 1);
|
||||
let ith = index.ordinal();
|
||||
|
||||
report_mismatch(
|
||||
alloc,
|
||||
@ -438,13 +421,9 @@ fn to_expr_report<'b>(
|
||||
let expected_set: MutSet<_> = expected_fields.keys().cloned().collect();
|
||||
let actual_set: MutSet<_> = actual_fields.keys().cloned().collect();
|
||||
|
||||
let diff = expected_set.difference(&actual_set);
|
||||
let mut diff = expected_set.difference(&actual_set);
|
||||
|
||||
match diff
|
||||
.into_iter()
|
||||
.next()
|
||||
.and_then(|k| Some((k, expected_fields.get(k)?)))
|
||||
{
|
||||
match diff.next().and_then(|k| Some((k, expected_fields.get(k)?))) {
|
||||
None => report_mismatch(
|
||||
alloc,
|
||||
filename,
|
||||
@ -464,7 +443,7 @@ fn to_expr_report<'b>(
|
||||
),
|
||||
Some((field, field_region)) => {
|
||||
let r_doc = alloc.symbol_unqualified(symbol);
|
||||
let f_doc = alloc.record_field(field.clone().clone());
|
||||
let f_doc = alloc.record_field(field.clone());
|
||||
|
||||
let header = alloc.concat(vec![
|
||||
alloc.reflow("The "),
|
||||
@ -632,7 +611,7 @@ fn to_expr_report<'b>(
|
||||
}
|
||||
},
|
||||
Reason::FnArg { name, arg_index } => {
|
||||
let ith = int_to_ordinal(arg_index as usize + 1);
|
||||
let ith = arg_index.ordinal();
|
||||
|
||||
let this_function = match name {
|
||||
None => alloc.text("this function"),
|
||||
@ -661,17 +640,12 @@ fn to_expr_report<'b>(
|
||||
None,
|
||||
)
|
||||
}
|
||||
other => {
|
||||
// NamedFnArg(String /* function name */, u8 /* arg index */),
|
||||
// AnonymousFnCall { arity: u8 },
|
||||
// NamedFnCall(String /* function name */, u8 /* arity */),
|
||||
// BinOpArg(BinOp, ArgSide),
|
||||
// BinOpRet(BinOp),
|
||||
// FloatLiteral,
|
||||
// IntLiteral,
|
||||
// NumLiteral,
|
||||
// InterpolatedStringVar,
|
||||
todo!("I don't have a message yet for reason {:?}", other)
|
||||
Reason::FloatLiteral | Reason::IntLiteral | Reason::NumLiteral => {
|
||||
unreachable!("I don't think these can be reached")
|
||||
}
|
||||
|
||||
Reason::InterpolatedStringVar => {
|
||||
unimplemented!("string interpolation is not implemented yet")
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -836,7 +810,7 @@ fn to_pattern_report<'b>(
|
||||
|
||||
PExpected::ForReason(reason, expected_type, region) => match reason {
|
||||
PReason::WhenMatch { index } => {
|
||||
if index == 0 {
|
||||
if index == Index::FIRST {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc
|
||||
.text("The 1st pattern in this ")
|
||||
@ -871,10 +845,7 @@ fn to_pattern_report<'b>(
|
||||
} else {
|
||||
let doc = alloc.stack(vec![
|
||||
alloc
|
||||
.string(format!(
|
||||
"The {} pattern in this ",
|
||||
int_to_ordinal(index + 1)
|
||||
))
|
||||
.string(format!("The {} pattern in this ", index.ordinal()))
|
||||
.append(alloc.keyword("when"))
|
||||
.append(alloc.text(" does not match the previous ones:")),
|
||||
alloc.region(region),
|
||||
@ -886,7 +857,7 @@ fn to_pattern_report<'b>(
|
||||
alloc,
|
||||
alloc.string(format!(
|
||||
"The {} pattern is trying to match",
|
||||
int_to_ordinal(index + 1)
|
||||
index.ordinal()
|
||||
)),
|
||||
&category,
|
||||
),
|
||||
@ -902,11 +873,8 @@ fn to_pattern_report<'b>(
|
||||
}
|
||||
}
|
||||
}
|
||||
PReason::TagArg { .. } => {
|
||||
panic!("I didn't think this could trigger. Please tell Folkert about it!")
|
||||
}
|
||||
PReason::PatternGuard => {
|
||||
todo!("Blocked on https://github.com/rtfeldman/roc/issues/304")
|
||||
PReason::TagArg { .. } | PReason::PatternGuard => {
|
||||
unreachable!("I didn't think this could trigger. Please tell Folkert about it!")
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -943,19 +911,19 @@ fn add_pattern_category<'b>(
|
||||
use PatternCategory::*;
|
||||
|
||||
let rest = match category {
|
||||
Record => alloc.text(" record values of type:"),
|
||||
EmptyRecord => alloc.text(" an empty record:"),
|
||||
PatternGuard => alloc.text(" a pattern guard of type:"),
|
||||
Set => alloc.text(" sets of type:"),
|
||||
Map => alloc.text(" maps of type:"),
|
||||
Record => alloc.reflow(" record values of type:"),
|
||||
EmptyRecord => alloc.reflow(" an empty record:"),
|
||||
PatternGuard => alloc.reflow(" a pattern guard of type:"),
|
||||
Set => alloc.reflow(" sets of type:"),
|
||||
Map => alloc.reflow(" maps of type:"),
|
||||
Ctor(tag_name) => alloc.concat(vec![
|
||||
alloc.tag_name(tag_name.clone()),
|
||||
alloc.text(" values of type:"),
|
||||
alloc.reflow(" values of type:"),
|
||||
]),
|
||||
Str => alloc.text(" strings:"),
|
||||
Num => alloc.text(" numbers:"),
|
||||
Int => alloc.text(" integers:"),
|
||||
Float => alloc.text(" floats"),
|
||||
Str => alloc.reflow(" strings:"),
|
||||
Num => alloc.reflow(" numbers:"),
|
||||
Int => alloc.reflow(" integers:"),
|
||||
Float => alloc.reflow(" floats"),
|
||||
};
|
||||
|
||||
alloc.concat(vec![i_am_trying_to_match, rest])
|
||||
@ -972,7 +940,6 @@ fn to_circular_report<'b>(
|
||||
title: "CIRCULAR TYPE".to_string(),
|
||||
filename,
|
||||
doc: {
|
||||
let line = r#"Here is my best effort at writing down the type. You will see ∞ for parts of the type that repeat something already printed out infinitely."#;
|
||||
alloc.stack(vec![
|
||||
alloc
|
||||
.reflow("I'm inferring a weird self-referential type for ")
|
||||
@ -980,7 +947,11 @@ fn to_circular_report<'b>(
|
||||
.append(alloc.text(":")),
|
||||
alloc.region(region),
|
||||
alloc.stack(vec![
|
||||
alloc.reflow(line),
|
||||
alloc.reflow(
|
||||
"Here is my best effort at writing down the type. \
|
||||
You will see ∞ for parts of the type that repeat \
|
||||
something already printed out infinitely.",
|
||||
),
|
||||
alloc.type_block(to_doc(alloc, Parens::Unnecessary, overall_type)),
|
||||
]),
|
||||
])
|
||||
@ -994,8 +965,6 @@ pub enum Problem {
|
||||
ArityMismatch(usize, usize),
|
||||
FieldTypo(Lowercase, Vec<Lowercase>),
|
||||
FieldsMissing(Vec<Lowercase>),
|
||||
|
||||
// TODO maybe these should include the arguments too?
|
||||
TagTypo(TagName, Vec<TagName>),
|
||||
TagsMissing(Vec<TagName>),
|
||||
BadRigidVar(Lowercase, ErrorType),
|
||||
@ -1375,8 +1344,34 @@ fn to_diff<'b>(
|
||||
let left = to_doc(alloc, Parens::Unnecessary, type1);
|
||||
let right = to_doc(alloc, Parens::Unnecessary, type2);
|
||||
|
||||
let is_int = |t: &ErrorType| match t {
|
||||
ErrorType::Type(Symbol::INT_INT, _) => true,
|
||||
ErrorType::Alias(Symbol::INT_INT, _, _) => true,
|
||||
|
||||
ErrorType::Type(Symbol::NUM_NUM, args) => match &args.get(0) {
|
||||
Some(ErrorType::Type(Symbol::INT_INTEGER, _)) => true,
|
||||
Some(ErrorType::Alias(Symbol::INT_INTEGER, _, _)) => true,
|
||||
_ => false,
|
||||
},
|
||||
ErrorType::Alias(Symbol::NUM_NUM, args, _) => match &args.get(0) {
|
||||
Some((_, ErrorType::Type(Symbol::INT_INTEGER, _))) => true,
|
||||
Some((_, ErrorType::Alias(Symbol::INT_INTEGER, _, _))) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
let is_float = |t: &ErrorType| match t {
|
||||
ErrorType::Type(Symbol::FLOAT_FLOAT, _) => true,
|
||||
ErrorType::Alias(Symbol::FLOAT_FLOAT, _, _) => true,
|
||||
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let problems = match pair {
|
||||
(RigidVar(x), other) | (other, RigidVar(x)) => vec![Problem::BadRigidVar(x, other)],
|
||||
(a, b) if (is_int(&a) && is_float(&b)) || (is_float(&a) && is_int(&b)) => {
|
||||
vec![Problem::IntFloat]
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
@ -2148,7 +2143,58 @@ fn type_problem_to_pretty<'b>(
|
||||
Boolean(_) => bad_rigid_var(x, alloc.reflow("a uniqueness attribute value")),
|
||||
}
|
||||
}
|
||||
IntFloat => alloc.hint().append(alloc.concat(vec![
|
||||
alloc.reflow("Convert between "),
|
||||
alloc.type_str("Int"),
|
||||
alloc.reflow(" and "),
|
||||
alloc.type_str("Float"),
|
||||
alloc.reflow(" with "),
|
||||
alloc.symbol_qualified(Symbol::NUM_TO_FLOAT),
|
||||
alloc.reflow(" and "),
|
||||
alloc.symbol_qualified(Symbol::FLOAT_ROUND),
|
||||
alloc.reflow("."),
|
||||
])),
|
||||
|
||||
_ => todo!(),
|
||||
TagsMissing(missing) => match missing.split_last() {
|
||||
None => alloc.nil(),
|
||||
Some((f1, [])) => {
|
||||
let hint1 = alloc
|
||||
.hint()
|
||||
.append(alloc.reflow("Looks like a closed tag union does not have the "))
|
||||
.append(alloc.tag_name(f1.clone()))
|
||||
.append(alloc.reflow(" tag."));
|
||||
|
||||
let hint2 = alloc.hint().append(alloc.reflow(
|
||||
"Closed tag unions can't grow, \
|
||||
because that might change the size in memory. \
|
||||
Can you use an open tag union?",
|
||||
));
|
||||
|
||||
alloc.stack(vec![hint1, hint2])
|
||||
}
|
||||
|
||||
Some((last, init)) => {
|
||||
let separator = alloc.reflow(", ");
|
||||
|
||||
let hint1 = alloc
|
||||
.hint()
|
||||
.append(alloc.reflow("Looks like a closed tag union does not have the "))
|
||||
.append(
|
||||
alloc
|
||||
.intersperse(init.iter().map(|v| alloc.tag_name(v.clone())), separator),
|
||||
)
|
||||
.append(alloc.reflow(" and "))
|
||||
.append(alloc.tag_name(last.clone()))
|
||||
.append(alloc.reflow(" tags."));
|
||||
|
||||
let hint2 = alloc.hint().append(alloc.reflow(
|
||||
"Closed tag unions can't grow, \
|
||||
because that might change the size in memory. \
|
||||
Can you use an open tag union?",
|
||||
));
|
||||
|
||||
alloc.stack(vec![hint1, hint2])
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1067,6 +1067,8 @@ mod test_reporting {
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
Int
|
||||
|
||||
Hint: Convert between Int and Float with `Num.toFloat` and `Float.round`.
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -1101,6 +1103,8 @@ mod test_reporting {
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
Int
|
||||
|
||||
Hint: Convert between Int and Float with `Num.toFloat` and `Float.round`.
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -1133,6 +1137,8 @@ mod test_reporting {
|
||||
But the type annotation on `x` says it should be:
|
||||
|
||||
Int -> Int
|
||||
|
||||
Hint: Convert between Int and Float with `Num.toFloat` and `Float.round`.
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -1461,6 +1467,8 @@ mod test_reporting {
|
||||
But the type annotation says it should be:
|
||||
|
||||
{ x : Int }
|
||||
|
||||
Hint: Convert between Int and Float with `Num.toFloat` and `Float.round`.
|
||||
"#
|
||||
),
|
||||
)
|
||||
@ -1883,4 +1891,155 @@ mod test_reporting {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plus_on_str() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
0x4 + "foo"
|
||||
"#
|
||||
),
|
||||
// TODO also suggest fields with the correct type
|
||||
indoc!(
|
||||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
The 2nd argument to `add` is not what I expect:
|
||||
|
||||
1 ┆ 0x4 + "foo"
|
||||
┆ ^^^^^
|
||||
|
||||
This argument is a string of type:
|
||||
|
||||
Str
|
||||
|
||||
But `add` needs the 2nd argument to be:
|
||||
|
||||
Num Integer
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_float() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
0x4 + 3.14
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
The 2nd argument to `add` is not what I expect:
|
||||
|
||||
1 ┆ 0x4 + 3.14
|
||||
┆ ^^^^
|
||||
|
||||
This argument is a float of type:
|
||||
|
||||
Float
|
||||
|
||||
But `add` needs the 2nd argument to be:
|
||||
|
||||
Num Integer
|
||||
|
||||
Hint: Convert between Int and Float with `Num.toFloat` and `Float.round`.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_missing() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : [ A ] -> [ A, B ]
|
||||
f = \a -> a
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
Something is off with the body of the `f` definition:
|
||||
|
||||
2 ┆ f = \a -> a
|
||||
┆ ^^^^^^^
|
||||
|
||||
The body is an anonymous function of type:
|
||||
|
||||
[ A ] -> [ A ]
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
|
||||
[ A ] -> [ A, B ]
|
||||
|
||||
Hint: Looks like a closed tag union does not have the `B` tag.
|
||||
|
||||
Hint: Closed tag unions can't grow, because that might change the size
|
||||
in memory. Can you use an open tag union?
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tags_missing() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : [ A ] -> [ A, B, C ]
|
||||
f = \a -> a
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
Something is off with the body of the `f` definition:
|
||||
|
||||
2 ┆ f = \a -> a
|
||||
┆ ^^^^^^^
|
||||
|
||||
The body is an anonymous function of type:
|
||||
|
||||
[ A ] -> [ A ]
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
|
||||
[ A ] -> [ A, B, C ]
|
||||
|
||||
Hint: Looks like a closed tag union does not have the `C` and `B` tags.
|
||||
|
||||
Hint: Closed tag unions can't grow, because that might change the size
|
||||
in memory. Can you use an open tag union?
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_tag_union_can_grow() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f : [ A ]* -> [ A, B, C ]
|
||||
f = \a -> a
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
// should not give errors
|
||||
indoc!(""),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,9 @@ use crate::boolean_algebra;
|
||||
use crate::pretty_print::Parens;
|
||||
use crate::subs::{Subs, VarStore, Variable};
|
||||
use inlinable_string::InlinableString;
|
||||
use roc_collections::all::{union, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
use roc_collections::all::{union, ImMap, ImSet, Index, MutMap, MutSet, SendMap};
|
||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_parse::operator::{ArgSide, BinOp};
|
||||
use roc_region::all::{Located, Region};
|
||||
use std::fmt;
|
||||
|
||||
@ -598,33 +597,46 @@ pub struct RecordStructure {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PReason {
|
||||
WhenMatch { index: usize },
|
||||
TagArg { tag_name: TagName, index: usize },
|
||||
WhenMatch { index: Index },
|
||||
TagArg { tag_name: TagName, index: Index },
|
||||
PatternGuard,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AnnotationSource {
|
||||
TypedIfBranch(usize /* index */),
|
||||
TypedWhenBranch(usize /* index */),
|
||||
TypedIfBranch { index: Index, num_branches: usize },
|
||||
TypedWhenBranch(Index),
|
||||
TypedBody,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Reason {
|
||||
FnArg { name: Option<Symbol>, arg_index: u8 },
|
||||
FnCall { name: Option<Symbol>, arity: u8 },
|
||||
BinOpArg(BinOp, ArgSide),
|
||||
BinOpRet(BinOp),
|
||||
FnArg {
|
||||
name: Option<Symbol>,
|
||||
arg_index: Index,
|
||||
},
|
||||
FnCall {
|
||||
name: Option<Symbol>,
|
||||
arity: u8,
|
||||
},
|
||||
// BinOpArg(BinOp, ArgSide),
|
||||
// BinOpRet(BinOp),
|
||||
FloatLiteral,
|
||||
IntLiteral,
|
||||
NumLiteral,
|
||||
InterpolatedStringVar,
|
||||
WhenBranch { index: usize },
|
||||
WhenBranch {
|
||||
index: Index,
|
||||
},
|
||||
WhenGuard,
|
||||
IfCondition,
|
||||
IfBranch { index: usize, total_branches: usize },
|
||||
ElemInList { index: usize },
|
||||
IfBranch {
|
||||
index: Index,
|
||||
total_branches: usize,
|
||||
},
|
||||
ElemInList {
|
||||
index: Index,
|
||||
},
|
||||
RecordUpdateValue(Lowercase),
|
||||
RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user