Improve error message when we try to unify two wildcards

Closes #1931
This commit is contained in:
ayazhafiz 2021-11-21 01:42:24 -05:00
parent 8f1878bc42
commit 817bb22f9e
3 changed files with 86 additions and 16 deletions

View file

@ -4,7 +4,7 @@ use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
static WILDCARD: &str = "*"; pub static WILDCARD: &str = "*";
static EMPTY_RECORD: &str = "{}"; static EMPTY_RECORD: &str = "{}";
static EMPTY_TAG_UNION: &str = "[]"; static EMPTY_TAG_UNION: &str = "[]";

View file

@ -5,7 +5,7 @@ use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_solve::solve; use roc_solve::solve;
use roc_types::pretty_print::Parens; use roc_types::pretty_print::{Parens, WILDCARD};
use roc_types::types::{Category, ErrorType, PatternCategory, Reason, RecordField, TypeExt}; use roc_types::types::{Category, ErrorType, PatternCategory, Reason, RecordField, TypeExt};
use std::path::PathBuf; use std::path::PathBuf;
@ -355,18 +355,44 @@ fn to_expr_report<'b>(
TypedBody { .. } => "The body is".into(), TypedBody { .. } => "The body is".into(),
}; };
let comparison = type_comparison( let i_am_seeing = add_category(alloc, alloc.text(it_is), &category);
// TODO: can comparisons between wildcards happen outside the context of an annotation?
let comparison = if is_wildcards_comparison(&found, &expected_type) {
alloc.stack(vec![
i_am_seeing,
alloc.type_block(to_doc(alloc, Parens::Unnecessary, found)),
alloc.concat(vec![
alloc.reflow("But the type annotation on "),
on_name_text,
alloc.reflow(" says it should be a "),
alloc.type_str(WILDCARD),
alloc.reflow(" too! This tells me that the type is connected in a way that doesn't require a wildcard."),
]),
alloc.concat(vec![
alloc.reflow("Since the type has to be the same in both places, the type can be more specific than "),
alloc.type_str(WILDCARD),
alloc.reflow(". You can change the "),
alloc.type_str(WILDCARD),
alloc.reflow(" to a named type variable like "),
alloc.type_variable("a".into()),
alloc.reflow(" to reflect the connection.")
]),
])
} else {
type_comparison(
alloc, alloc,
found, found,
expected_type, expected_type,
add_category(alloc, alloc.text(it_is), &category), i_am_seeing,
alloc.concat(vec![ alloc.concat(vec![
alloc.text("But the type annotation"), alloc.text("But the type annotation"),
on_name_text, on_name_text,
alloc.text(" says it should be:"), alloc.text(" says it should be:"),
]), ]),
None, None,
); )
};
Report { Report {
title: "TYPE MISMATCH".to_string(), title: "TYPE MISMATCH".to_string(),
@ -891,6 +917,14 @@ fn to_expr_report<'b>(
} }
} }
fn is_wildcards_comparison(type1: &ErrorType, type2: &ErrorType) -> bool {
use ErrorType::*;
match (type1, type2) {
(RigidVar(v1), RigidVar(v2)) if v1.as_str() == WILDCARD && v2.as_str() == WILDCARD => true,
_ => false,
}
}
fn count_arguments(tipe: &ErrorType) -> usize { fn count_arguments(tipe: &ErrorType) -> usize {
use ErrorType::*; use ErrorType::*;

View file

@ -6761,8 +6761,44 @@ I need all branches in an `if` to have the same type!
Tip: Your type annotation uses `a` and `b` as separate type variables. Tip: Your type annotation uses `a` and `b` as separate type variables.
Your code seems to be saying they are the same though. Maybe they Your code seems to be saying they are the same though. Maybe they
should be the same your type annotation? Maybe your code uses them in should be the same in your type annotation? Maybe your code uses them
a weird way? in a weird way?
"#
),
)
}
fn error_wildcards_are_related() {
report_problem_as(
indoc!(
r#"
f : * -> *
f = \x -> x
f
"#
),
indoc!(
r#"
TYPE MISMATCH
Something is off with the body of the `f` definition:
1 f : * -> *
2 f = \x -> x
^
This `x` value is a:
*
But the type annotation on on `f` says it should be a * too! This tells
me that the type is connected in a way that doesn't require a
wildcard.
Since the type has to be the same in both places, the type can be more
specific than *. You can change the * to a named type variable like `a`
to reflect the connection.
"# "#
), ),
) )