mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-01 05:24:15 +00:00
Merge pull request #7454 from roc-lang/ayaz/error-on-invalid-generalized-types
Restrict usages of type variables in non-generalized contexts
This commit is contained in:
commit
c8467b1fe0
23 changed files with 273 additions and 182 deletions
|
|
@ -532,7 +532,7 @@ pi = 3.14159265358979323846264338327950288419716939937510
|
|||
|
||||
## Circle constant (τ)
|
||||
tau : Frac *
|
||||
tau = 2 * pi
|
||||
tau = 6.2831853071795864769252867665590057683943387987502
|
||||
|
||||
# ------- Functions
|
||||
## Convert a number to a [Str].
|
||||
|
|
|
|||
|
|
@ -915,7 +915,7 @@ pub struct DefTypes {
|
|||
pub loc_symbols: Slice<(Symbol, Region)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Generalizable(pub bool);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -95,22 +95,6 @@ flags! {
|
|||
/// Prints all type variables entered for fixpoint-fixing.
|
||||
ROC_PRINT_FIXPOINT_FIXING
|
||||
|
||||
/// Verifies that after let-generalization of a def, any rigid variables in the type annotation
|
||||
/// of the def are indeed generalized.
|
||||
///
|
||||
/// Note that rigids need not always be generalized in a def. For example, they may be
|
||||
/// constrained by a type from a lower rank, as `b` is in the following def:
|
||||
///
|
||||
/// F a : { foo : a }
|
||||
/// foo = \arg ->
|
||||
/// x : F b
|
||||
/// x = arg
|
||||
/// x.foo
|
||||
///
|
||||
/// Instead, this flag is useful for checking that in general, introduction is correct, when
|
||||
/// chainging how defs are constrained.
|
||||
ROC_VERIFY_RIGID_LET_GENERALIZED
|
||||
|
||||
/// Verifies that an `occurs` check indeed only contains non-recursive types that need to be
|
||||
/// fixed-up with one new recursion variable.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1522,18 +1522,18 @@ mod test_reporting {
|
|||
from_annotation_if,
|
||||
indoc!(
|
||||
r"
|
||||
x : Num.Int *
|
||||
x : Num.Int _
|
||||
x = if Bool.true then 3.14 else 4
|
||||
|
||||
x
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
Something is off with the `then` branch of this `if` expression:
|
||||
|
||||
4│ x : Num.Int *
|
||||
4│ x : Num.Int _
|
||||
5│ x = if Bool.true then 3.14 else 4
|
||||
^^^^
|
||||
|
||||
|
|
@ -1547,14 +1547,14 @@ mod test_reporting {
|
|||
|
||||
Tip: You can convert between integers and fractions using functions
|
||||
like `Num.to_frac` and `Num.round`.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
from_annotation_when,
|
||||
indoc!(
|
||||
r"
|
||||
x : Num.Int *
|
||||
x : Num.Int _
|
||||
x =
|
||||
when True is
|
||||
_ -> 3.14
|
||||
|
|
@ -1562,12 +1562,12 @@ mod test_reporting {
|
|||
x
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
4│ x : Num.Int *
|
||||
4│ x : Num.Int _
|
||||
5│ x =
|
||||
6│> when True is
|
||||
7│> _ -> 3.14
|
||||
|
|
@ -1582,7 +1582,7 @@ mod test_reporting {
|
|||
|
||||
Tip: You can convert between integers and fractions using functions
|
||||
like `Num.to_frac` and `Num.round`.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
|
@ -1910,7 +1910,7 @@ mod test_reporting {
|
|||
from_annotation_complex_pattern,
|
||||
indoc!(
|
||||
r"
|
||||
{ x } : { x : Num.Int * }
|
||||
{ x } : { x : Num.Int _ }
|
||||
{ x } = { x: 4.0 }
|
||||
|
||||
x
|
||||
|
|
@ -1921,7 +1921,7 @@ mod test_reporting {
|
|||
|
||||
Something is off with the body of this definition:
|
||||
|
||||
4│ { x } : { x : Num.Int * }
|
||||
4│ { x } : { x : Num.Int _ }
|
||||
5│ { x } = { x: 4.0 }
|
||||
^^^^^^^^^^
|
||||
|
||||
|
|
@ -2047,18 +2047,18 @@ mod test_reporting {
|
|||
missing_fields,
|
||||
indoc!(
|
||||
r"
|
||||
x : { a : Num.Int *, b : Num.Frac *, c : Str }
|
||||
x : { a : Num.Int _, b : Num.Frac _, c : Str }
|
||||
x = { b: 4.0 }
|
||||
|
||||
x
|
||||
"
|
||||
),
|
||||
@r"
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
Something is off with the body of the `x` definition:
|
||||
|
||||
4│ x : { a : Num.Int *, b : Num.Frac *, c : Str }
|
||||
4│ x : { a : Num.Int _, b : Num.Frac _, c : Str }
|
||||
5│ x = { b: 4.0 }
|
||||
^^^^^^^^^^
|
||||
|
||||
|
|
@ -2075,7 +2075,7 @@ mod test_reporting {
|
|||
}
|
||||
|
||||
Tip: Looks like the c and a fields are missing.
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
// this previously reported the message below, not sure which is better
|
||||
|
|
@ -3448,7 +3448,7 @@ mod test_reporting {
|
|||
x : AList Num.I64 Num.I64
|
||||
x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||||
|
||||
y : BList a a
|
||||
y : BList _ _
|
||||
y = BNil
|
||||
|
||||
{ x, y }
|
||||
|
|
@ -4189,9 +4189,8 @@ mod test_reporting {
|
|||
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
|
||||
|
||||
# Create an empty dictionary.
|
||||
empty : RBTree k v
|
||||
empty =
|
||||
Empty
|
||||
empty : {} -> RBTree k v
|
||||
empty = \{} -> Empty
|
||||
|
||||
empty
|
||||
"
|
||||
|
|
@ -11218,10 +11217,10 @@ All branches in an `if` must have the same type!
|
|||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
my_decoder : Decoder (a -> a) fmt where fmt implements DecoderFormatting
|
||||
my_decoder = decoder
|
||||
my_decoder : Decoder (_ -> _) _
|
||||
my_decoder = decoder
|
||||
|
||||
main =
|
||||
my_decoder
|
||||
"#
|
||||
),
|
||||
|
|
@ -11230,12 +11229,12 @@ All branches in an `if` must have the same type!
|
|||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
7│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
6│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
||||
a -> a
|
||||
* -> *
|
||||
|
||||
Note: `Decoding` cannot be generated for functions.
|
||||
"
|
||||
|
|
@ -11251,10 +11250,10 @@ All branches in an `if` must have the same type!
|
|||
|
||||
A := {}
|
||||
|
||||
main =
|
||||
my_decoder : Decoder {x : A} fmt where fmt implements DecoderFormatting
|
||||
my_decoder = decoder
|
||||
my_decoder : Decoder {x : A} _
|
||||
my_decoder = decoder
|
||||
|
||||
main =
|
||||
my_decoder
|
||||
"#
|
||||
),
|
||||
|
|
@ -11263,8 +11262,8 @@ All branches in an `if` must have the same type!
|
|||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
9│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
8│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
||||
|
|
@ -11514,11 +11513,10 @@ All branches in an `if` must have the same type!
|
|||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
my_decoder : Decoder {x : Str, y ? Str} fmt where fmt implements DecoderFormatting
|
||||
my_decoder = decoder
|
||||
my_decoder : Decoder {x : Str, y ? Str} _
|
||||
my_decoder = decoder
|
||||
|
||||
my_decoder
|
||||
main = my_decoder
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
|
|
@ -11526,8 +11524,8 @@ All branches in an `if` must have the same type!
|
|||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
7│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
6│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
||||
|
|
@ -14111,11 +14109,10 @@ All branches in an `if` must have the same type!
|
|||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
my_decoder : Decoder (U32, Str) fmt where fmt implements DecoderFormatting
|
||||
my_decoder = decoder
|
||||
my_decoder : Decoder (U32, Str) _
|
||||
my_decoder = decoder
|
||||
|
||||
my_decoder
|
||||
main = my_decoder
|
||||
"#
|
||||
)
|
||||
);
|
||||
|
|
@ -14128,11 +14125,10 @@ All branches in an `if` must have the same type!
|
|||
|
||||
import Decode exposing [decoder]
|
||||
|
||||
main =
|
||||
my_decoder : Decoder (U32, {} -> {}) fmt where fmt implements DecoderFormatting
|
||||
my_decoder = decoder
|
||||
my_decoder : Decoder (U32, {} -> {}) _
|
||||
my_decoder = decoder
|
||||
|
||||
my_decoder
|
||||
main = my_decoder
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
|
|
@ -14140,8 +14136,8 @@ All branches in an `if` must have the same type!
|
|||
|
||||
This expression has a type that does not implement the abilities it's expected to:
|
||||
|
||||
7│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
6│ my_decoder = decoder
|
||||
^^^^^^^
|
||||
|
||||
I can't generate an implementation of the `Decoding` ability for
|
||||
|
||||
|
|
@ -15997,4 +15993,66 @@ All branches in an `if` must have the same type!
|
|||
Str -> {}
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
invalid_generic_literal,
|
||||
indoc!(
|
||||
r#"
|
||||
module [v]
|
||||
|
||||
v : *
|
||||
v = 1
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
Something is off with the body of the `v` definition:
|
||||
|
||||
3│ v : *
|
||||
4│ v = 1
|
||||
^
|
||||
|
||||
The body is a number of type:
|
||||
|
||||
Num *
|
||||
|
||||
But the type annotation on `v` says it should be:
|
||||
|
||||
*
|
||||
|
||||
Tip: The type annotation uses the type variable `*` to say that this
|
||||
definition can produce any type of value. But in the body I see that
|
||||
it will only produce a `Num` value of a single specific type. Maybe
|
||||
change the type annotation to be more specific? Maybe change the code
|
||||
to be more general?
|
||||
"###
|
||||
);
|
||||
|
||||
test_report!(
|
||||
invalid_generic_literal_list,
|
||||
indoc!(
|
||||
r#"
|
||||
module [v]
|
||||
|
||||
v : List *
|
||||
v = []
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
── TYPE VARIABLE IS NOT GENERIC in /code/proj/Main.roc ─────────────────────────
|
||||
|
||||
This type variable has a single type:
|
||||
|
||||
3│ v : List *
|
||||
^
|
||||
|
||||
Type variables tell me that they can be used with any type, but they
|
||||
can only be used with functions. All other values have exactly one
|
||||
type.
|
||||
|
||||
Hint: If you would like the type to be inferred for you, use an
|
||||
underscore _ instead.
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ succeed = \x -> Identity(x)
|
|||
|
||||
with_default = Res.with_default
|
||||
|
||||
yay : Res.Res {} err
|
||||
yay : Res.Res {} _
|
||||
yay =
|
||||
ok = Ok("foo")
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ succeed = \x -> Identity(x)
|
|||
|
||||
with_default = Res.with_default
|
||||
|
||||
yay : Res.Res {} err
|
||||
yay : Res.Res {} _
|
||||
yay =
|
||||
ok = Ok("foo")
|
||||
|
||||
|
|
|
|||
|
|
@ -271,11 +271,13 @@ fn load_fixture(
|
|||
);
|
||||
}
|
||||
|
||||
assert!(loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default()
|
||||
.is_empty());
|
||||
assert_eq!(
|
||||
loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
|
||||
let expected_name = loaded_module
|
||||
.interns
|
||||
|
|
@ -433,11 +435,13 @@ fn module_with_deps() {
|
|||
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
assert!(loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default()
|
||||
.is_empty(),);
|
||||
assert_eq!(
|
||||
loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
|
||||
let mut def_count = 0;
|
||||
let declarations = loaded_module.declarations_by_id.remove(&home).unwrap();
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ pub fn remove_module_param_arguments(
|
|||
| TypeError::ExpectedEffectful(_, _)
|
||||
| TypeError::UnsuffixedEffectfulFunction(_, _)
|
||||
| TypeError::SuffixedPureFunction(_, _)
|
||||
| TypeError::InvalidTryTarget(_, _, _) => {}
|
||||
| TypeError::InvalidTryTarget(_, _, _)
|
||||
| TypeError::TypeIsNotGeneralized(..) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,6 +214,7 @@ fn drop_last_argument(err_type: &mut ErrorType) {
|
|||
| ErrorType::Alias(_, _, _, _)
|
||||
| ErrorType::Range(_)
|
||||
| ErrorType::Error
|
||||
| ErrorType::EffectfulFunc => {}
|
||||
| ErrorType::EffectfulFunc
|
||||
| ErrorType::InferenceVar => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,12 @@ use bumpalo::Bump;
|
|||
use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo};
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::{
|
||||
Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, LetConstraint, OpportunisticResolve,
|
||||
TryTargetConstraint,
|
||||
Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, Generalizable, LetConstraint,
|
||||
OpportunisticResolve, TryTargetConstraint,
|
||||
};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::module::ModuleParams;
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::ident::IdentSuffix;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
|
|
@ -32,8 +29,8 @@ use roc_region::all::{Loc, Region};
|
|||
use roc_solve_problem::TypeError;
|
||||
use roc_solve_schema::UnificationMode;
|
||||
use roc_types::subs::{
|
||||
self, Content, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt, UlsOfVar,
|
||||
Variable,
|
||||
self, Content, ErrorTypeContext, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt,
|
||||
UlsOfVar, Variable,
|
||||
};
|
||||
use roc_types::types::{Category, Polarity, Reason, RecordField, Type, TypeExtension, Types, Uls};
|
||||
use roc_unify::unify::{
|
||||
|
|
@ -356,29 +353,13 @@ fn solve(
|
|||
generalize(env, young_mark, visit_mark, rank.next());
|
||||
debug_assert!(env.pools.get(rank.next()).is_empty(), "variables left over in let-binding scope, but they should all be in a lower scope or generalized now");
|
||||
|
||||
// check that things went well
|
||||
dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, {
|
||||
let rigid_vars = &env.constraints[let_con.rigid_vars];
|
||||
|
||||
// NOTE the `subs.redundant` check does not come from elm.
|
||||
// It's unclear whether this is a bug with our implementation
|
||||
// (something is redundant that shouldn't be)
|
||||
// or that it just never came up in elm.
|
||||
let mut it = rigid_vars
|
||||
.iter()
|
||||
.filter(|loc_var| {
|
||||
let var = loc_var.value;
|
||||
!env.subs.redundant(var) && env.subs.get_rank(var) != Rank::GENERALIZED
|
||||
})
|
||||
.peekable();
|
||||
|
||||
if it.peek().is_some() {
|
||||
let failing: Vec<_> = it.collect();
|
||||
println!("Rigids {:?}", &rigid_vars);
|
||||
println!("Failing {failing:?}");
|
||||
debug_assert!(false);
|
||||
}
|
||||
});
|
||||
let named_variables = &env.constraints[let_con.rigid_vars];
|
||||
check_named_variables_are_generalized(
|
||||
env,
|
||||
problems,
|
||||
named_variables,
|
||||
let_con.generalizable,
|
||||
);
|
||||
|
||||
let mut new_scope = scope.clone();
|
||||
for (symbol, loc_var) in local_def_vars.iter() {
|
||||
|
|
@ -1636,6 +1617,30 @@ fn solve(
|
|||
state
|
||||
}
|
||||
|
||||
fn check_named_variables_are_generalized(
|
||||
env: &mut InferenceEnv<'_>,
|
||||
problems: &mut Vec<TypeError>,
|
||||
named_variables: &[Loc<Variable>],
|
||||
generalizable: Generalizable,
|
||||
) {
|
||||
for loc_var in named_variables {
|
||||
let is_generalized = env.subs.get_rank(loc_var.value) == Rank::GENERALIZED;
|
||||
if !is_generalized {
|
||||
// TODO: should be OF_PATTERN if on the LHS of a function, otherwise OF_VALUE.
|
||||
let polarity = Polarity::OF_VALUE;
|
||||
let ctx = ErrorTypeContext::NON_GENERALIZED_AS_INFERRED;
|
||||
let error_type = env
|
||||
.subs
|
||||
.var_to_error_type_contextual(loc_var.value, ctx, polarity);
|
||||
problems.push(TypeError::TypeIsNotGeneralized(
|
||||
loc_var.region,
|
||||
error_type,
|
||||
generalizable,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_suffix_fx(
|
||||
env: &mut InferenceEnv<'_>,
|
||||
problems: &mut Vec<TypeError>,
|
||||
|
|
|
|||
|
|
@ -2510,13 +2510,15 @@ mod solve_expr {
|
|||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r"
|
||||
empty : [Cons a (ConsList a), Nil] as ConsList a
|
||||
empty = Nil
|
||||
ConsList a : [Cons a (ConsList a), Nil]
|
||||
|
||||
empty
|
||||
"
|
||||
empty : ConsList _
|
||||
empty = Nil
|
||||
|
||||
empty
|
||||
"
|
||||
),
|
||||
"ConsList a",
|
||||
"ConsList *",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -3742,7 +3744,7 @@ mod solve_expr {
|
|||
indoc!(
|
||||
r"
|
||||
\rec ->
|
||||
{ x, y } : { x : I64, y ? Bool }*
|
||||
{ x, y } : { x : I64, y ? Bool }_
|
||||
{ x, y ? Bool.false } = rec
|
||||
|
||||
{ x, y }
|
||||
|
|
@ -3909,26 +3911,6 @@ mod solve_expr {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_named_rigids() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
|
||||
main : List x
|
||||
main =
|
||||
empty : List x
|
||||
empty = []
|
||||
|
||||
empty
|
||||
"#
|
||||
),
|
||||
"List x",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_tag_application() {
|
||||
infer_eq_without_problem(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Provides types to describe problems that can occur during solving.
|
||||
use std::{path::PathBuf, str::Utf8Error};
|
||||
|
||||
use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind};
|
||||
use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind, Generalizable};
|
||||
use roc_can::expr::TryKind;
|
||||
use roc_can::{
|
||||
constraint::FxCallKind,
|
||||
|
|
@ -50,6 +50,7 @@ pub enum TypeError {
|
|||
UnsuffixedEffectfulFunction(Region, FxSuffixKind),
|
||||
SuffixedPureFunction(Region, FxSuffixKind),
|
||||
InvalidTryTarget(Region, ErrorType, TryKind),
|
||||
TypeIsNotGeneralized(Region, ErrorType, Generalizable),
|
||||
}
|
||||
|
||||
impl TypeError {
|
||||
|
|
@ -80,6 +81,7 @@ impl TypeError {
|
|||
TypeError::UnsuffixedEffectfulFunction(_, _) => Warning,
|
||||
TypeError::SuffixedPureFunction(_, _) => Warning,
|
||||
TypeError::InvalidTryTarget(_, _, _) => RuntimeError,
|
||||
TypeError::TypeIsNotGeneralized(..) => RuntimeError,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +103,8 @@ impl TypeError {
|
|||
| TypeError::ExpectedEffectful(region, _)
|
||||
| TypeError::UnsuffixedEffectfulFunction(region, _)
|
||||
| TypeError::SuffixedPureFunction(region, _)
|
||||
| TypeError::InvalidTryTarget(region, _, _) => Some(*region),
|
||||
| TypeError::InvalidTryTarget(region, _, _)
|
||||
| TypeError::TypeIsNotGeneralized(region, _, _) => Some(*region),
|
||||
TypeError::UnfulfilledAbility(ab, ..) => ab.region(),
|
||||
TypeError::Exhaustive(e) => Some(e.region()),
|
||||
TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region),
|
||||
|
|
|
|||
|
|
@ -662,7 +662,7 @@ fn linked_list_len_1() {
|
|||
|
||||
LinkedList a : [Nil, Cons a (LinkedList a)]
|
||||
|
||||
one : LinkedList (Int *)
|
||||
one : LinkedList (Int _)
|
||||
one = Cons 1 Nil
|
||||
|
||||
length : LinkedList a -> Int *
|
||||
|
|
@ -690,7 +690,7 @@ fn linked_list_len_twice_1() {
|
|||
|
||||
LinkedList a : [Nil, Cons a (LinkedList a)]
|
||||
|
||||
one : LinkedList (Int *)
|
||||
one : LinkedList (Int _)
|
||||
one = Cons 1 Nil
|
||||
|
||||
length : LinkedList a -> Int *
|
||||
|
|
@ -718,7 +718,7 @@ fn linked_list_len_3() {
|
|||
|
||||
LinkedList a : [Nil, Cons a (LinkedList a)]
|
||||
|
||||
three : LinkedList (Int *)
|
||||
three : LinkedList (Int _)
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
length : LinkedList a -> Int *
|
||||
|
|
@ -747,7 +747,7 @@ fn linked_list_sum_num_a() {
|
|||
|
||||
LinkedList a : [Nil, Cons a (LinkedList a)]
|
||||
|
||||
three : LinkedList (Int *)
|
||||
three : LinkedList (Int _)
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
|
||||
|
|
@ -776,7 +776,7 @@ fn linked_list_sum_int() {
|
|||
|
||||
LinkedList a : [Nil, Cons a (LinkedList a)]
|
||||
|
||||
zero : LinkedList (Int *)
|
||||
zero : LinkedList (Int _)
|
||||
zero = Nil
|
||||
|
||||
sum : LinkedList (Int a) -> Int a
|
||||
|
|
@ -804,7 +804,7 @@ fn linked_list_map() {
|
|||
|
||||
LinkedList a : [Nil, Cons a (LinkedList a)]
|
||||
|
||||
three : LinkedList (Int *)
|
||||
three : LinkedList (Int _)
|
||||
three = Cons 3 (Cons 2 (Cons 1 Nil))
|
||||
|
||||
sum : LinkedList (Num a) -> Num a
|
||||
|
|
@ -836,7 +836,7 @@ fn when_nested_maybe() {
|
|||
r"
|
||||
Maybe a : [Nothing, Just a]
|
||||
|
||||
x : Maybe (Maybe (Int a))
|
||||
x : Maybe (Maybe (Int _))
|
||||
x = Just (Just 41)
|
||||
|
||||
when x is
|
||||
|
|
@ -853,7 +853,7 @@ fn when_nested_maybe() {
|
|||
r"
|
||||
Maybe a : [Nothing, Just a]
|
||||
|
||||
x : Maybe (Maybe (Int *))
|
||||
x : Maybe (Maybe (Int _))
|
||||
x = Just Nothing
|
||||
|
||||
when x is
|
||||
|
|
@ -871,7 +871,7 @@ fn when_nested_maybe() {
|
|||
r"
|
||||
Maybe a : [Nothing, Just a]
|
||||
|
||||
x : Maybe (Maybe (Int *))
|
||||
x : Maybe (Maybe (Int _))
|
||||
x = Nothing
|
||||
|
||||
when x is
|
||||
|
|
@ -1402,7 +1402,7 @@ fn recursive_function_with_rigid() {
|
|||
else
|
||||
1 + foo { count: state.count - 1, x: state.x }
|
||||
|
||||
main : Int *
|
||||
main : Int _
|
||||
main =
|
||||
foo { count: 3, x: {} }
|
||||
"#
|
||||
|
|
@ -1517,7 +1517,7 @@ fn rbtree_balance_3() {
|
|||
balance = \key, left ->
|
||||
Node key left Empty
|
||||
|
||||
main : RedBlackTree (Int *)
|
||||
main : RedBlackTree (Int _)
|
||||
main =
|
||||
balance 0 Empty
|
||||
"#
|
||||
|
|
@ -1696,7 +1696,7 @@ fn nested_pattern_match_two_ways() {
|
|||
_ -> 3
|
||||
_ -> 3
|
||||
|
||||
main : Int *
|
||||
main : Int _
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
|
|
@ -1719,7 +1719,7 @@ fn nested_pattern_match_two_ways() {
|
|||
Cons 1 (Cons 1 _) -> 3
|
||||
_ -> 3
|
||||
|
||||
main : Int *
|
||||
main : Int _
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
|
|
@ -1751,7 +1751,7 @@ fn linked_list_guarded_double_pattern_match() {
|
|||
_ -> 3
|
||||
_ -> 3
|
||||
|
||||
main : Int *
|
||||
main : Int _
|
||||
main =
|
||||
when balance Nil is
|
||||
_ -> 3
|
||||
|
|
@ -1778,7 +1778,7 @@ fn linked_list_double_pattern_match() {
|
|||
Cons _ (Cons x _) -> x
|
||||
_ -> 0
|
||||
|
||||
main : Int *
|
||||
main : Int _
|
||||
main =
|
||||
foo (Cons 1 (Cons 32 Nil))
|
||||
"#
|
||||
|
|
@ -1886,7 +1886,7 @@ fn wildcard_rigid() {
|
|||
@Effect inner
|
||||
|
||||
|
||||
main : MyTask {} (Frac *)
|
||||
main : MyTask {} (Frac _)
|
||||
main = always {}
|
||||
"#
|
||||
),
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ fn err_type_var_annotation() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"
|
||||
ok : Result I64 *
|
||||
ok : Result I64 _
|
||||
ok = Ok 3
|
||||
|
||||
Result.map_ok ok (\x -> x + 1)
|
||||
|
|
|
|||
|
|
@ -1075,7 +1075,7 @@ fn applied_tag_function_result() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : List (Result Str *)
|
||||
x : List (Result Str _)
|
||||
x = List.map ["a", "b"] Ok
|
||||
|
||||
List.keep_oks x (\y -> y)
|
||||
|
|
|
|||
|
|
@ -22,3 +22,4 @@ bumpalo.workspace = true
|
|||
static_assertions.workspace = true
|
||||
|
||||
soa.workspace = true
|
||||
bitflags.workspace = true
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::types::{
|
|||
Polarity, RecordField, RecordFieldsError, TupleElemsError, TypeExt, Uls,
|
||||
};
|
||||
use crate::unification_table::{self, UnificationTable};
|
||||
use bitflags::bitflags;
|
||||
use roc_collections::all::{FnvMap, ImMap, ImSet, MutSet, SendMap};
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_error_macros::internal_error;
|
||||
|
|
@ -50,10 +51,23 @@ impl fmt::Debug for Mark {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ErrorTypeContext {
|
||||
None,
|
||||
ExpandRanges,
|
||||
bitflags! {
|
||||
pub struct ErrorTypeContext : u8 {
|
||||
/// List all number types that satisfy number range constraints.
|
||||
const EXPAND_RANGES = 1 << 0;
|
||||
/// Re-write non-generalized types like to inference variables.
|
||||
const NON_GENERALIZED_AS_INFERRED = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorTypeContext {
|
||||
fn expand_ranges(&self) -> bool {
|
||||
self.contains(Self::EXPAND_RANGES)
|
||||
}
|
||||
|
||||
fn non_generalized_as_inferred(&self) -> bool {
|
||||
self.contains(Self::NON_GENERALIZED_AS_INFERRED)
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorTypeState {
|
||||
|
|
@ -2055,7 +2069,7 @@ impl Subs {
|
|||
}
|
||||
|
||||
pub fn var_to_error_type(&mut self, var: Variable, observed_pol: Polarity) -> ErrorType {
|
||||
self.var_to_error_type_contextual(var, ErrorTypeContext::None, observed_pol)
|
||||
self.var_to_error_type_contextual(var, ErrorTypeContext::empty(), observed_pol)
|
||||
}
|
||||
|
||||
pub fn var_to_error_type_contextual(
|
||||
|
|
@ -4020,6 +4034,13 @@ fn content_to_err_type(
|
|||
match content {
|
||||
Structure(flat_type) => flat_type_to_err_type(subs, state, flat_type, pol),
|
||||
|
||||
RigidVar(..) | RigidAbleVar(..)
|
||||
if state.context.non_generalized_as_inferred()
|
||||
&& subs.get_rank(var) != Rank::GENERALIZED =>
|
||||
{
|
||||
ErrorType::InferenceVar
|
||||
}
|
||||
|
||||
FlexVar(opt_name) => {
|
||||
let name = match opt_name {
|
||||
Some(name_index) => subs.field_names[name_index.index()].clone(),
|
||||
|
|
@ -4123,7 +4144,7 @@ fn content_to_err_type(
|
|||
}
|
||||
|
||||
RangedNumber(range) => {
|
||||
if state.context == ErrorTypeContext::ExpandRanges {
|
||||
if state.context.expand_ranges() {
|
||||
let mut types = Vec::new();
|
||||
for var in range.variable_slice() {
|
||||
types.push(var_to_err_type(subs, state, *var, pol));
|
||||
|
|
|
|||
|
|
@ -3679,6 +3679,7 @@ pub enum ErrorType {
|
|||
/// If the name was auto-generated, it will start with a `#`.
|
||||
FlexVar(Lowercase),
|
||||
RigidVar(Lowercase),
|
||||
InferenceVar,
|
||||
EffectfulFunc,
|
||||
/// If the name was auto-generated, it will start with a `#`.
|
||||
FlexAbleVar(Lowercase, AbilitySet),
|
||||
|
|
@ -3733,6 +3734,7 @@ impl ErrorType {
|
|||
FlexVar(v) | RigidVar(v) | FlexAbleVar(v, _) | RigidAbleVar(v, _) => {
|
||||
taken.insert(v.clone());
|
||||
}
|
||||
InferenceVar => {}
|
||||
Record(fields, ext) => {
|
||||
fields
|
||||
.iter()
|
||||
|
|
@ -3912,13 +3914,14 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
|||
Infinite => buf.push('∞'),
|
||||
Error => buf.push('?'),
|
||||
FlexVar(name) | RigidVar(name) => buf.push_str(name.as_str()),
|
||||
FlexAbleVar(name, symbol) | RigidAbleVar(name, symbol) => {
|
||||
InferenceVar => buf.push('_'),
|
||||
FlexAbleVar(name, abilities) | RigidAbleVar(name, abilities) => {
|
||||
let write_parens = parens == Parens::InTypeParam;
|
||||
if write_parens {
|
||||
buf.push('(');
|
||||
}
|
||||
buf.push_str(name.as_str());
|
||||
write!(buf, "{} {:?}", roc_parse::keyword::IMPLEMENTS, symbol).unwrap();
|
||||
write!(buf, "{} {:?}", roc_parse::keyword::IMPLEMENTS, abilities).unwrap();
|
||||
if write_parens {
|
||||
buf.push(')');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
app "test" provides [foo] to "./platform"
|
||||
|
||||
F a : { foo : a }
|
||||
|
||||
foo = \arg ->
|
||||
#^^^{-1} F b -[[foo(0)]]-> b
|
||||
x : F b
|
||||
x = arg
|
||||
x.foo
|
||||
|
|
@ -356,9 +356,9 @@ fn unify_help<M: MetaCollector>(
|
|||
}
|
||||
} else {
|
||||
let error_context = if mismatches.contains(&Mismatch::TypeNotInRange) {
|
||||
ErrorTypeContext::ExpandRanges
|
||||
ErrorTypeContext::EXPAND_RANGES
|
||||
} else {
|
||||
ErrorTypeContext::None
|
||||
ErrorTypeContext::empty()
|
||||
};
|
||||
|
||||
let type1 = env.var_to_error_type_contextual(var1, error_context, observed_pol);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue