mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Allow ignored defs with an effectful RHS
This commit is contained in:
parent
175a2b5683
commit
c9f001b041
13 changed files with 136 additions and 126 deletions
8
crates/cli/tests/effectful/ignore_result.roc
Normal file
8
crates/cli/tests/effectful/ignore_result.roc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
app [main!] { pf: platform "../../../../examples/cli/effects-platform/main.roc" }
|
||||||
|
|
||||||
|
import pf.Effect
|
||||||
|
|
||||||
|
main! : {} => {}
|
||||||
|
main! = \{} ->
|
||||||
|
_ = Effect.getLine! {}
|
||||||
|
Effect.putLine! "I asked for input and I ignored it. Deal with it! 😎"
|
|
@ -663,7 +663,7 @@ impl Constraints {
|
||||||
| Constraint::Store(..)
|
| Constraint::Store(..)
|
||||||
| Constraint::Lookup(..)
|
| Constraint::Lookup(..)
|
||||||
| Constraint::Pattern(..)
|
| Constraint::Pattern(..)
|
||||||
| Constraint::EffectfulStmt(..)
|
| Constraint::ExpectEffectful(..)
|
||||||
| Constraint::FxCall(_)
|
| Constraint::FxCall(_)
|
||||||
| Constraint::FxSuffix(_)
|
| Constraint::FxSuffix(_)
|
||||||
| Constraint::FlexToPure(_)
|
| Constraint::FlexToPure(_)
|
||||||
|
@ -845,8 +845,8 @@ pub enum Constraint {
|
||||||
FxSuffix(Index<FxSuffixConstraint>),
|
FxSuffix(Index<FxSuffixConstraint>),
|
||||||
/// Set an fx var as pure if flex (no effectful functions were called)
|
/// Set an fx var as pure if flex (no effectful functions were called)
|
||||||
FlexToPure(Variable),
|
FlexToPure(Variable),
|
||||||
/// Expect statement to be effectful
|
/// Expect statement or ignored def to be effectful
|
||||||
EffectfulStmt(Variable, Region),
|
ExpectEffectful(Variable, ExpectEffectfulReason, Region),
|
||||||
/// Used for things that always unify, e.g. blanks and runtime errors
|
/// Used for things that always unify, e.g. blanks and runtime errors
|
||||||
True,
|
True,
|
||||||
SaveTheEnvironment,
|
SaveTheEnvironment,
|
||||||
|
@ -937,6 +937,7 @@ pub struct FxExpectation {
|
||||||
pub enum FxCallKind {
|
pub enum FxCallKind {
|
||||||
Call(Option<Symbol>),
|
Call(Option<Symbol>),
|
||||||
Stmt,
|
Stmt,
|
||||||
|
Ignored,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -962,6 +963,12 @@ impl FxSuffixKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ExpectEffectfulReason {
|
||||||
|
Stmt,
|
||||||
|
Ignored,
|
||||||
|
}
|
||||||
|
|
||||||
/// Custom impl to limit vertical space used by the debug output
|
/// Custom impl to limit vertical space used by the debug output
|
||||||
impl std::fmt::Debug for Constraint {
|
impl std::fmt::Debug for Constraint {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -984,8 +991,8 @@ impl std::fmt::Debug for Constraint {
|
||||||
Self::FxSuffix(arg0) => {
|
Self::FxSuffix(arg0) => {
|
||||||
write!(f, "FxSuffix({arg0:?})")
|
write!(f, "FxSuffix({arg0:?})")
|
||||||
}
|
}
|
||||||
Self::EffectfulStmt(arg0, arg1) => {
|
Self::ExpectEffectful(arg0, arg1, arg2) => {
|
||||||
write!(f, "EffectfulStmt({arg0:?}, {arg1:?})")
|
write!(f, "EffectfulStmt({arg0:?}, {arg1:?}, {arg2:?})")
|
||||||
}
|
}
|
||||||
Self::FlexToPure(arg0) => {
|
Self::FlexToPure(arg0) => {
|
||||||
write!(f, "FlexToPure({arg0:?})")
|
write!(f, "FlexToPure({arg0:?})")
|
||||||
|
|
|
@ -390,6 +390,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
||||||
kind: match kind {
|
kind: match kind {
|
||||||
DefKind::Let => DefKind::Let,
|
DefKind::Let => DefKind::Let,
|
||||||
DefKind::Stmt(v) => DefKind::Stmt(sub!(*v)),
|
DefKind::Stmt(v) => DefKind::Stmt(sub!(*v)),
|
||||||
|
DefKind::Ignored(v) => DefKind::Ignored(sub!(*v)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -112,6 +112,7 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> {
|
||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
DefKind::Let => def_help(c, f, &loc_pattern.value, &loc_expr.value),
|
DefKind::Let => def_help(c, f, &loc_pattern.value, &loc_expr.value),
|
||||||
|
DefKind::Ignored(_) => def_help(c, f, &loc_pattern.value, &loc_expr.value),
|
||||||
DefKind::Stmt(_) => expr(c, EPrec::Free, f, &loc_expr.value),
|
DefKind::Stmt(_) => expr(c, EPrec::Free, f, &loc_expr.value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,6 +97,8 @@ pub enum DefKind {
|
||||||
Let,
|
Let,
|
||||||
/// A standalone statement with an fx variable
|
/// A standalone statement with an fx variable
|
||||||
Stmt(Variable),
|
Stmt(Variable),
|
||||||
|
/// Ignored result, must be effectful
|
||||||
|
Ignored(Variable),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefKind {
|
impl DefKind {
|
||||||
|
@ -104,6 +106,19 @@ impl DefKind {
|
||||||
match self {
|
match self {
|
||||||
DefKind::Let => DefKind::Let,
|
DefKind::Let => DefKind::Let,
|
||||||
DefKind::Stmt(v) => DefKind::Stmt(f(v)),
|
DefKind::Stmt(v) => DefKind::Stmt(f(v)),
|
||||||
|
DefKind::Ignored(v) => DefKind::Ignored(f(v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_pattern(var_store: &mut VarStore, pattern: &Loc<Pattern>) -> Self {
|
||||||
|
if BindingsFromPattern::new(pattern)
|
||||||
|
.peekable()
|
||||||
|
.peek()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
DefKind::Ignored(var_store.fresh())
|
||||||
|
} else {
|
||||||
|
DefKind::Let
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1212,11 +1227,7 @@ fn canonicalize_value_defs<'a>(
|
||||||
|
|
||||||
for (def_index, pending_def) in pending_value_defs.iter().enumerate() {
|
for (def_index, pending_def) in pending_value_defs.iter().enumerate() {
|
||||||
if let Some(loc_pattern) = pending_def.loc_pattern() {
|
if let Some(loc_pattern) = pending_def.loc_pattern() {
|
||||||
let mut new_bindings = BindingsFromPattern::new(loc_pattern).peekable();
|
let new_bindings = BindingsFromPattern::new(loc_pattern).peekable();
|
||||||
|
|
||||||
if new_bindings.peek().is_none() {
|
|
||||||
env.problem(Problem::NoIdentifiersIntroduced(loc_pattern.region));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (s, r) in new_bindings {
|
for (s, r) in new_bindings {
|
||||||
// store the top-level defs, used to ensure that closures won't capture them
|
// store the top-level defs, used to ensure that closures won't capture them
|
||||||
|
@ -2439,6 +2450,8 @@ fn canonicalize_pending_value_def<'a>(
|
||||||
}
|
}
|
||||||
Body(loc_can_pattern, loc_expr) => {
|
Body(loc_can_pattern, loc_expr) => {
|
||||||
//
|
//
|
||||||
|
let def_kind = DefKind::from_pattern(var_store, &loc_can_pattern);
|
||||||
|
|
||||||
canonicalize_pending_body(
|
canonicalize_pending_body(
|
||||||
env,
|
env,
|
||||||
output,
|
output,
|
||||||
|
@ -2447,7 +2460,7 @@ fn canonicalize_pending_value_def<'a>(
|
||||||
loc_can_pattern,
|
loc_can_pattern,
|
||||||
loc_expr,
|
loc_expr,
|
||||||
None,
|
None,
|
||||||
DefKind::Let,
|
def_kind,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Stmt(loc_expr) => {
|
Stmt(loc_expr) => {
|
||||||
|
|
|
@ -8,8 +8,8 @@ use crate::builtins::{
|
||||||
use crate::pattern::{constrain_pattern, PatternState};
|
use crate::pattern::{constrain_pattern, PatternState};
|
||||||
use roc_can::annotation::IntroducedVariables;
|
use roc_can::annotation::IntroducedVariables;
|
||||||
use roc_can::constraint::{
|
use roc_can::constraint::{
|
||||||
Constraint, Constraints, ExpectedTypeIndex, FxCallKind, FxExpectation, Generalizable,
|
Constraint, Constraints, ExpectEffectfulReason, ExpectedTypeIndex, FxCallKind, FxExpectation,
|
||||||
OpportunisticResolve, TypeOrVar,
|
Generalizable, OpportunisticResolve, TypeOrVar,
|
||||||
};
|
};
|
||||||
use roc_can::def::{Def, DefKind};
|
use roc_can::def::{Def, DefKind};
|
||||||
use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext};
|
use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext};
|
||||||
|
@ -1458,10 +1458,13 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
while let Some(def) = stack.pop() {
|
while let Some(def) = stack.pop() {
|
||||||
body_con = match def.kind {
|
body_con = match def.kind {
|
||||||
DefKind::Let => constrain_let_def(types, constraints, env, def, body_con),
|
DefKind::Let => constrain_let_def(types, constraints, env, def, body_con, None),
|
||||||
DefKind::Stmt(fx_var) => {
|
DefKind::Stmt(fx_var) => {
|
||||||
constrain_stmt_def(types, constraints, env, def, body_con, fx_var)
|
constrain_stmt_def(types, constraints, env, def, body_con, fx_var)
|
||||||
}
|
}
|
||||||
|
DefKind::Ignored(fx_var) => {
|
||||||
|
constrain_let_def(types, constraints, env, def, body_con, Some(fx_var))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3434,6 +3437,7 @@ fn constrain_let_def(
|
||||||
env: &mut Env,
|
env: &mut Env,
|
||||||
def: &Def,
|
def: &Def,
|
||||||
body_con: Constraint,
|
body_con: Constraint,
|
||||||
|
ignored_fx_var: Option<Variable>,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
match &def.annotation {
|
match &def.annotation {
|
||||||
Some(annotation) => constrain_typed_def(types, constraints, env, def, body_con, annotation),
|
Some(annotation) => constrain_typed_def(types, constraints, env, def, body_con, annotation),
|
||||||
|
@ -3448,14 +3452,49 @@ fn constrain_let_def(
|
||||||
// no annotation, so no extra work with rigids
|
// no annotation, so no extra work with rigids
|
||||||
|
|
||||||
let expected = constraints.push_expected_type(NoExpectation(expr_type_index));
|
let expected = constraints.push_expected_type(NoExpectation(expr_type_index));
|
||||||
let expr_con = constrain_expr(
|
|
||||||
|
let expr_con = match ignored_fx_var {
|
||||||
|
None => constrain_expr(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
def.loc_expr.region,
|
def.loc_expr.region,
|
||||||
&def.loc_expr.value,
|
&def.loc_expr.value,
|
||||||
expected,
|
expected,
|
||||||
|
),
|
||||||
|
Some(fx_var) => {
|
||||||
|
let expr_con = env.with_fx_expectation(fx_var, None, |env| {
|
||||||
|
constrain_expr(
|
||||||
|
types,
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
def.loc_expr.region,
|
||||||
|
&def.loc_expr.value,
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ignored def must be effectful, otherwise it's dead code
|
||||||
|
let effectful_constraint = Constraint::ExpectEffectful(
|
||||||
|
fx_var,
|
||||||
|
ExpectEffectfulReason::Ignored,
|
||||||
|
def.loc_pattern.region,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let enclosing_fx_constraint = constraints.fx_call(
|
||||||
|
fx_var,
|
||||||
|
FxCallKind::Ignored,
|
||||||
|
def.loc_pattern.region,
|
||||||
|
env.fx_expectation,
|
||||||
|
);
|
||||||
|
|
||||||
|
constraints.and_constraint([
|
||||||
|
expr_con,
|
||||||
|
enclosing_fx_constraint,
|
||||||
|
effectful_constraint,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
};
|
||||||
let expr_con = attach_resolution_constraints(constraints, env, expr_con);
|
let expr_con = attach_resolution_constraints(constraints, env, expr_con);
|
||||||
|
|
||||||
let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value));
|
let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value));
|
||||||
|
@ -3528,7 +3567,8 @@ fn constrain_stmt_def(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stmt expr must be effectful, otherwise it's dead code
|
// Stmt expr must be effectful, otherwise it's dead code
|
||||||
let effectful_constraint = Constraint::EffectfulStmt(fx_var, region);
|
let effectful_constraint =
|
||||||
|
Constraint::ExpectEffectful(fx_var, ExpectEffectfulReason::Stmt, region);
|
||||||
|
|
||||||
let fx_call_kind = match fn_name {
|
let fx_call_kind = match fn_name {
|
||||||
None => FxCallKind::Stmt,
|
None => FxCallKind::Stmt,
|
||||||
|
|
|
@ -10170,7 +10170,7 @@ All branches in an `if` must have the same type!
|
||||||
main = \n -> n + 2
|
main = \n -> n + 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r"
|
@r###"
|
||||||
── DUPLICATE NAME in /code/proj/Main.roc ───────────────────────────────────────
|
── DUPLICATE NAME in /code/proj/Main.roc ───────────────────────────────────────
|
||||||
|
|
||||||
The `main` name is first defined here:
|
The `main` name is first defined here:
|
||||||
|
@ -10185,19 +10185,7 @@ All branches in an `if` must have the same type!
|
||||||
|
|
||||||
Since these variables have the same name, it's easy to use the wrong
|
Since these variables have the same name, it's easy to use the wrong
|
||||||
one by accident. Give one of them a new name.
|
one by accident. Give one of them a new name.
|
||||||
|
"###
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
|
||||||
|
|
||||||
5│ main = \n -> n + 2
|
|
||||||
^^^^
|
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
|
||||||
program's behavior!
|
|
||||||
"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
test_report!(
|
test_report!(
|
||||||
|
@ -10843,51 +10831,47 @@ All branches in an `if` must have the same type!
|
||||||
@r###"
|
@r###"
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
This assignment doesn't introduce any new variables:
|
||||||
|
|
||||||
4│ Pair _ _ = Pair 0 1
|
4│ Pair _ _ = Pair 0 1
|
||||||
^^^^^^^^
|
^^^^^^^^
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
Since it doesn't call any effectful functions, this assignment cannot
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
affect the program's behavior. If you don't need to use the value on
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
the right-hand-side, consider removing the assignment.
|
||||||
program's behavior!
|
|
||||||
|
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
This assignment doesn't introduce any new variables:
|
||||||
|
|
||||||
6│ _ = Pair 0 1
|
6│ _ = Pair 0 1
|
||||||
^
|
^
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
Since it doesn't call any effectful functions, this assignment cannot
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
affect the program's behavior. If you don't need to use the value on
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
the right-hand-side, consider removing the assignment.
|
||||||
program's behavior!
|
|
||||||
|
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
This assignment doesn't introduce any new variables:
|
||||||
|
|
||||||
8│ {} = {}
|
8│ {} = {}
|
||||||
^^
|
^^
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
Since it doesn't call any effectful functions, this assignment cannot
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
affect the program's behavior. If you don't need to use the value on
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
the right-hand-side, consider removing the assignment.
|
||||||
program's behavior!
|
|
||||||
|
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
This assignment doesn't introduce any new variables:
|
||||||
|
|
||||||
10│ Foo = Foo
|
10│ Foo = Foo
|
||||||
^^^
|
^^^
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
Since it doesn't call any effectful functions, this assignment cannot
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
affect the program's behavior. If you don't need to use the value on
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
the right-hand-side, consider removing the assignment.
|
||||||
program's behavior!
|
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10906,55 +10890,7 @@ All branches in an `if` must have the same type!
|
||||||
Foo = Foo
|
Foo = Foo
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
@r"
|
@""
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
|
||||||
|
|
||||||
3│ Pair _ _ = Pair 0 1
|
|
||||||
^^^^^^^^
|
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
|
||||||
program's behavior!
|
|
||||||
|
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
|
||||||
|
|
||||||
5│ _ = Pair 0 1
|
|
||||||
^
|
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
|
||||||
program's behavior!
|
|
||||||
|
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
|
||||||
|
|
||||||
7│ {} = {}
|
|
||||||
^^
|
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
|
||||||
program's behavior!
|
|
||||||
|
|
||||||
── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
|
|
||||||
|
|
||||||
This destructure assignment doesn't introduce any new variables:
|
|
||||||
|
|
||||||
9│ Foo = Foo
|
|
||||||
^^^
|
|
||||||
|
|
||||||
If you don't need to use the value on the right-hand-side of this
|
|
||||||
assignment, consider removing the assignment. Since Roc is purely
|
|
||||||
functional, assignments that don't introduce variables cannot affect a
|
|
||||||
program's behavior!
|
|
||||||
"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
test_report!(
|
test_report!(
|
||||||
|
|
|
@ -102,7 +102,7 @@ pub fn remove_module_param_arguments(
|
||||||
| TypeError::ModuleParamsMismatch(_, _, _, _)
|
| TypeError::ModuleParamsMismatch(_, _, _, _)
|
||||||
| TypeError::FxInPureFunction(_, _, _)
|
| TypeError::FxInPureFunction(_, _, _)
|
||||||
| TypeError::FxInTopLevel(_, _)
|
| TypeError::FxInTopLevel(_, _)
|
||||||
| TypeError::PureStmt(_)
|
| TypeError::ExpectedEffectful(_, _)
|
||||||
| TypeError::UnsuffixedEffectfulFunction(_, _)
|
| TypeError::UnsuffixedEffectfulFunction(_, _)
|
||||||
| TypeError::SuffixedPureFunction(_, _) => {}
|
| TypeError::SuffixedPureFunction(_, _) => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,6 @@ pub enum Problem {
|
||||||
unbound_symbol: Symbol,
|
unbound_symbol: Symbol,
|
||||||
region: Region,
|
region: Region,
|
||||||
},
|
},
|
||||||
NoIdentifiersIntroduced(Region),
|
|
||||||
OverloadedSpecialization {
|
OverloadedSpecialization {
|
||||||
overload: Region,
|
overload: Region,
|
||||||
original_opaque: Symbol,
|
original_opaque: Symbol,
|
||||||
|
@ -318,7 +317,6 @@ impl Problem {
|
||||||
Problem::ImplementsNonRequired { .. } => Warning,
|
Problem::ImplementsNonRequired { .. } => Warning,
|
||||||
Problem::DoesNotImplementAbility { .. } => RuntimeError,
|
Problem::DoesNotImplementAbility { .. } => RuntimeError,
|
||||||
Problem::NotBoundInAllPatterns { .. } => RuntimeError,
|
Problem::NotBoundInAllPatterns { .. } => RuntimeError,
|
||||||
Problem::NoIdentifiersIntroduced(_) => Warning,
|
|
||||||
Problem::OverloadedSpecialization { .. } => Warning, // Ideally, will compile
|
Problem::OverloadedSpecialization { .. } => Warning, // Ideally, will compile
|
||||||
Problem::UnnecessaryOutputWildcard { .. } => Warning,
|
Problem::UnnecessaryOutputWildcard { .. } => Warning,
|
||||||
// TODO: sometimes this can just be a warning, e.g. if you have [1, .., .., 2] but we
|
// TODO: sometimes this can just be a warning, e.g. if you have [1, .., .., 2] but we
|
||||||
|
@ -483,7 +481,6 @@ impl Problem {
|
||||||
| Problem::NotAnAbility(region)
|
| Problem::NotAnAbility(region)
|
||||||
| Problem::ImplementsNonRequired { region, .. }
|
| Problem::ImplementsNonRequired { region, .. }
|
||||||
| Problem::DoesNotImplementAbility { region, .. }
|
| Problem::DoesNotImplementAbility { region, .. }
|
||||||
| Problem::NoIdentifiersIntroduced(region)
|
|
||||||
| Problem::OverloadedSpecialization {
|
| Problem::OverloadedSpecialization {
|
||||||
overload: region, ..
|
overload: region, ..
|
||||||
}
|
}
|
||||||
|
|
|
@ -856,12 +856,12 @@ fn solve(
|
||||||
solve_suffix_fx(env, problems, *kind, actual, region);
|
solve_suffix_fx(env, problems, *kind, actual, region);
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
EffectfulStmt(variable, region) => {
|
ExpectEffectful(variable, reason, region) => {
|
||||||
let content = env.subs.get_content_without_compacting(*variable);
|
let content = env.subs.get_content_without_compacting(*variable);
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
Content::Pure | Content::FlexVar(_) => {
|
Content::Pure | Content::FlexVar(_) => {
|
||||||
let problem = TypeError::PureStmt(*region);
|
let problem = TypeError::ExpectedEffectful(*region, *reason);
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
|
|
||||||
state
|
state
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Provides types to describe problems that can occur during solving.
|
//! Provides types to describe problems that can occur during solving.
|
||||||
use std::{path::PathBuf, str::Utf8Error};
|
use std::{path::PathBuf, str::Utf8Error};
|
||||||
|
|
||||||
use roc_can::constraint::FxSuffixKind;
|
use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind};
|
||||||
use roc_can::{
|
use roc_can::{
|
||||||
constraint::FxCallKind,
|
constraint::FxCallKind,
|
||||||
expected::{Expected, PExpected},
|
expected::{Expected, PExpected},
|
||||||
|
@ -45,7 +45,7 @@ pub enum TypeError {
|
||||||
ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType),
|
ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType),
|
||||||
FxInPureFunction(Region, FxCallKind, Option<Region>),
|
FxInPureFunction(Region, FxCallKind, Option<Region>),
|
||||||
FxInTopLevel(Region, FxCallKind),
|
FxInTopLevel(Region, FxCallKind),
|
||||||
PureStmt(Region),
|
ExpectedEffectful(Region, ExpectEffectfulReason),
|
||||||
UnsuffixedEffectfulFunction(Region, FxSuffixKind),
|
UnsuffixedEffectfulFunction(Region, FxSuffixKind),
|
||||||
SuffixedPureFunction(Region, FxSuffixKind),
|
SuffixedPureFunction(Region, FxSuffixKind),
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ impl TypeError {
|
||||||
TypeError::ModuleParamsMismatch(..) => RuntimeError,
|
TypeError::ModuleParamsMismatch(..) => RuntimeError,
|
||||||
TypeError::IngestedFileBadUtf8(..) => Fatal,
|
TypeError::IngestedFileBadUtf8(..) => Fatal,
|
||||||
TypeError::IngestedFileUnsupportedType(..) => Fatal,
|
TypeError::IngestedFileUnsupportedType(..) => Fatal,
|
||||||
TypeError::PureStmt(..) => Warning,
|
TypeError::ExpectedEffectful(..) => Warning,
|
||||||
TypeError::FxInPureFunction(_, _, _) => Warning,
|
TypeError::FxInPureFunction(_, _, _) => Warning,
|
||||||
TypeError::FxInTopLevel(_, _) => Warning,
|
TypeError::FxInTopLevel(_, _) => Warning,
|
||||||
TypeError::UnsuffixedEffectfulFunction(_, _) => Warning,
|
TypeError::UnsuffixedEffectfulFunction(_, _) => Warning,
|
||||||
|
@ -95,7 +95,7 @@ impl TypeError {
|
||||||
| TypeError::ModuleParamsMismatch(region, ..)
|
| TypeError::ModuleParamsMismatch(region, ..)
|
||||||
| TypeError::FxInPureFunction(region, _, _)
|
| TypeError::FxInPureFunction(region, _, _)
|
||||||
| TypeError::FxInTopLevel(region, _)
|
| TypeError::FxInTopLevel(region, _)
|
||||||
| TypeError::PureStmt(region)
|
| TypeError::ExpectedEffectful(region, _)
|
||||||
| TypeError::UnsuffixedEffectfulFunction(region, _)
|
| TypeError::UnsuffixedEffectfulFunction(region, _)
|
||||||
| TypeError::SuffixedPureFunction(region, _) => Some(*region),
|
| TypeError::SuffixedPureFunction(region, _) => Some(*region),
|
||||||
TypeError::UnfulfilledAbility(ab, ..) => ab.region(),
|
TypeError::UnfulfilledAbility(ab, ..) => ab.region(),
|
||||||
|
|
|
@ -1189,14 +1189,6 @@ pub fn can_problem<'b>(
|
||||||
]);
|
]);
|
||||||
title = "NAME NOT BOUND IN ALL PATTERNS".to_string();
|
title = "NAME NOT BOUND IN ALL PATTERNS".to_string();
|
||||||
}
|
}
|
||||||
Problem::NoIdentifiersIntroduced(region) => {
|
|
||||||
doc = alloc.stack([
|
|
||||||
alloc.reflow("This destructure assignment doesn't introduce any new variables:"),
|
|
||||||
alloc.region(lines.convert_region(region), severity),
|
|
||||||
alloc.reflow("If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior!"),
|
|
||||||
]);
|
|
||||||
title = "UNNECESSARY DEFINITION".to_string();
|
|
||||||
}
|
|
||||||
Problem::OverloadedSpecialization {
|
Problem::OverloadedSpecialization {
|
||||||
ability_member,
|
ability_member,
|
||||||
overload,
|
overload,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::error::canonicalize::{to_circular_def_doc, CIRCULAR_DEF};
|
||||||
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder};
|
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder};
|
||||||
use itertools::EitherOrBoth;
|
use itertools::EitherOrBoth;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use roc_can::constraint::{FxCallKind, FxSuffixKind};
|
use roc_can::constraint::{ExpectEffectfulReason, FxCallKind, FxSuffixKind};
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{HumanIndex, MutSet, SendMap};
|
use roc_collections::all::{HumanIndex, MutSet, SendMap};
|
||||||
use roc_collections::VecMap;
|
use roc_collections::VecMap;
|
||||||
|
@ -368,7 +368,7 @@ pub fn type_problem<'b>(
|
||||||
severity,
|
severity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
PureStmt(region) => {
|
ExpectedEffectful(region, ExpectEffectfulReason::Stmt) => {
|
||||||
let stack = [
|
let stack = [
|
||||||
alloc.reflow("This statement does not produce any effects:"),
|
alloc.reflow("This statement does not produce any effects:"),
|
||||||
alloc.region(lines.convert_region(region), severity),
|
alloc.region(lines.convert_region(region), severity),
|
||||||
|
@ -384,6 +384,20 @@ pub fn type_problem<'b>(
|
||||||
severity,
|
severity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
ExpectedEffectful(region, ExpectEffectfulReason::Ignored) => {
|
||||||
|
let stack = [
|
||||||
|
alloc.reflow("This assignment doesn't introduce any new variables:"),
|
||||||
|
alloc.region(lines.convert_region(region), severity),
|
||||||
|
alloc.reflow("Since it doesn't call any effectful functions, this assignment cannot affect the program's behavior. If you don't need to use the value on the right-hand-side, consider removing the assignment.")
|
||||||
|
];
|
||||||
|
|
||||||
|
Some(Report {
|
||||||
|
title: "UNNECESSARY DEFINITION".to_string(),
|
||||||
|
filename,
|
||||||
|
doc: alloc.stack(stack),
|
||||||
|
severity,
|
||||||
|
})
|
||||||
|
}
|
||||||
UnsuffixedEffectfulFunction(
|
UnsuffixedEffectfulFunction(
|
||||||
region,
|
region,
|
||||||
kind @ (FxSuffixKind::Let(symbol) | FxSuffixKind::Pattern(symbol)),
|
kind @ (FxSuffixKind::Let(symbol) | FxSuffixKind::Pattern(symbol)),
|
||||||
|
@ -5522,5 +5536,6 @@ fn describe_fx_call_kind<'b>(
|
||||||
]),
|
]),
|
||||||
FxCallKind::Call(None) => alloc.reflow("This expression calls an effectful function:"),
|
FxCallKind::Call(None) => alloc.reflow("This expression calls an effectful function:"),
|
||||||
FxCallKind::Stmt => alloc.reflow("This statement calls an effectful function:"),
|
FxCallKind::Stmt => alloc.reflow("This statement calls an effectful function:"),
|
||||||
|
FxCallKind::Ignored => alloc.reflow("This ignored def calls an effectful function:"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue