mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-01 10:52:18 +00:00
Treat untyped unsuffixed functions as pure
This commit is contained in:
parent
70fa4d036c
commit
af6fc6306f
5 changed files with 186 additions and 24 deletions
|
@ -635,6 +635,10 @@ impl Constraints {
|
||||||
Constraint::FxSuffix(constraint_index)
|
Constraint::FxSuffix(constraint_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flex_to_pure(&mut self, fx_var: Variable) -> Constraint {
|
||||||
|
Constraint::FlexToPure(fx_var)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
|
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
|
||||||
match constraint {
|
match constraint {
|
||||||
Constraint::SaveTheEnvironment => true,
|
Constraint::SaveTheEnvironment => true,
|
||||||
|
|
|
@ -234,6 +234,7 @@ fn constrain_untyped_closure(
|
||||||
ret_constraint,
|
ret_constraint,
|
||||||
Generalizable(true),
|
Generalizable(true),
|
||||||
),
|
),
|
||||||
|
constraints.and_constraint(pattern_state.delayed_fx_suffix_constraints),
|
||||||
constraints.equal_types_with_storage(
|
constraints.equal_types_with_storage(
|
||||||
function_type,
|
function_type,
|
||||||
expected,
|
expected,
|
||||||
|
@ -243,7 +244,7 @@ fn constrain_untyped_closure(
|
||||||
),
|
),
|
||||||
early_returns_constraint,
|
early_returns_constraint,
|
||||||
closure_constraint,
|
closure_constraint,
|
||||||
Constraint::FlexToPure(fx_var),
|
constraints.flex_to_pure(fx_var),
|
||||||
];
|
];
|
||||||
|
|
||||||
constraints.exists_many(vars, cons)
|
constraints.exists_many(vars, cons)
|
||||||
|
@ -2029,6 +2030,7 @@ fn constrain_function_def(
|
||||||
vars: Vec::with_capacity(function_def.arguments.len()),
|
vars: Vec::with_capacity(function_def.arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
delayed_is_open_constraints: vec![],
|
delayed_is_open_constraints: vec![],
|
||||||
|
delayed_fx_suffix_constraints: Vec::with_capacity(function_def.arguments.len()),
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||||
let closure_var = function_def.closure_type;
|
let closure_var = function_def.closure_type;
|
||||||
|
@ -2171,9 +2173,12 @@ fn constrain_function_def(
|
||||||
Category::Lambda,
|
Category::Lambda,
|
||||||
region,
|
region,
|
||||||
),
|
),
|
||||||
|
// Check argument suffixes against usage
|
||||||
|
constraints.and_constraint(argument_pattern_state.delayed_fx_suffix_constraints),
|
||||||
// Finally put the solved closure type into the dedicated def expr variable.
|
// Finally put the solved closure type into the dedicated def expr variable.
|
||||||
constraints.store(signature_index, expr_var, std::file!(), std::line!()),
|
constraints.store(signature_index, expr_var, std::file!(), std::line!()),
|
||||||
closure_constraint,
|
closure_constraint,
|
||||||
|
constraints.flex_to_pure(function_def.fx_type),
|
||||||
];
|
];
|
||||||
|
|
||||||
let expr_con = constraints.exists_many(vars, cons);
|
let expr_con = constraints.exists_many(vars, cons);
|
||||||
|
@ -2488,6 +2493,7 @@ fn constrain_when_branch_help(
|
||||||
vars: Vec::with_capacity(2),
|
vars: Vec::with_capacity(2),
|
||||||
constraints: Vec::with_capacity(2),
|
constraints: Vec::with_capacity(2),
|
||||||
delayed_is_open_constraints: Vec::new(),
|
delayed_is_open_constraints: Vec::new(),
|
||||||
|
delayed_fx_suffix_constraints: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, loc_pattern) in when_branch.patterns.iter().enumerate() {
|
for (i, loc_pattern) in when_branch.patterns.iter().enumerate() {
|
||||||
|
@ -2512,6 +2518,9 @@ fn constrain_when_branch_help(
|
||||||
state
|
state
|
||||||
.delayed_is_open_constraints
|
.delayed_is_open_constraints
|
||||||
.extend(partial_state.delayed_is_open_constraints);
|
.extend(partial_state.delayed_is_open_constraints);
|
||||||
|
state
|
||||||
|
.delayed_fx_suffix_constraints
|
||||||
|
.extend(partial_state.delayed_fx_suffix_constraints);
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
state.headers.extend(partial_state.headers);
|
state.headers.extend(partial_state.headers);
|
||||||
|
@ -2840,6 +2849,7 @@ pub(crate) fn constrain_def_pattern(
|
||||||
vars: Vec::with_capacity(1),
|
vars: Vec::with_capacity(1),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
delayed_is_open_constraints: vec![],
|
delayed_is_open_constraints: vec![],
|
||||||
|
delayed_fx_suffix_constraints: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern(
|
||||||
|
@ -2949,6 +2959,7 @@ fn constrain_typed_def(
|
||||||
vars: Vec::with_capacity(arguments.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
delayed_is_open_constraints: vec![],
|
delayed_is_open_constraints: vec![],
|
||||||
|
delayed_fx_suffix_constraints: Vec::with_capacity(arguments.len()),
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||||
let ret_var = *ret_var;
|
let ret_var = *ret_var;
|
||||||
|
@ -3032,6 +3043,8 @@ fn constrain_typed_def(
|
||||||
// This is a syntactic function, it can be generalized
|
// This is a syntactic function, it can be generalized
|
||||||
Generalizable(true),
|
Generalizable(true),
|
||||||
),
|
),
|
||||||
|
// Check argument suffixes against usage
|
||||||
|
constraints.and_constraint(argument_pattern_state.delayed_fx_suffix_constraints),
|
||||||
// Store the inferred ret var into the function type now, so that
|
// Store the inferred ret var into the function type now, so that
|
||||||
// when we check that the solved function type matches the annotation, we can
|
// when we check that the solved function type matches the annotation, we can
|
||||||
// display the fully inferred return variable.
|
// display the fully inferred return variable.
|
||||||
|
@ -3047,7 +3060,7 @@ fn constrain_typed_def(
|
||||||
constraints.store(signature_index, *fn_var, std::file!(), std::line!()),
|
constraints.store(signature_index, *fn_var, std::file!(), std::line!()),
|
||||||
constraints.store(signature_index, expr_var, std::file!(), std::line!()),
|
constraints.store(signature_index, expr_var, std::file!(), std::line!()),
|
||||||
closure_constraint,
|
closure_constraint,
|
||||||
Constraint::FlexToPure(fx_var),
|
constraints.flex_to_pure(fx_var),
|
||||||
];
|
];
|
||||||
|
|
||||||
let expr_con = constraints.exists_many(vars, cons);
|
let expr_con = constraints.exists_many(vars, cons);
|
||||||
|
@ -3933,6 +3946,7 @@ fn constraint_recursive_function(
|
||||||
vars: Vec::with_capacity(function_def.arguments.len()),
|
vars: Vec::with_capacity(function_def.arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
delayed_is_open_constraints: vec![],
|
delayed_is_open_constraints: vec![],
|
||||||
|
delayed_fx_suffix_constraints: Vec::with_capacity(function_def.arguments.len()),
|
||||||
};
|
};
|
||||||
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
let mut vars = Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||||
let ret_var = function_def.return_type;
|
let ret_var = function_def.return_type;
|
||||||
|
@ -4022,13 +4036,15 @@ fn constraint_recursive_function(
|
||||||
// Syntactic function can be generalized
|
// Syntactic function can be generalized
|
||||||
Generalizable(true),
|
Generalizable(true),
|
||||||
),
|
),
|
||||||
|
// Check argument suffixes against usage
|
||||||
|
constraints.and_constraint(argument_pattern_state.delayed_fx_suffix_constraints),
|
||||||
constraints.equal_types(fn_type, annotation_expected, Category::Lambda, region),
|
constraints.equal_types(fn_type, annotation_expected, Category::Lambda, region),
|
||||||
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
||||||
// Store type into AST vars. We use Store so errors aren't reported twice
|
// Store type into AST vars. We use Store so errors aren't reported twice
|
||||||
constraints.store(signature_index, expr_var, std::file!(), std::line!()),
|
constraints.store(signature_index, expr_var, std::file!(), std::line!()),
|
||||||
constraints.store(ret_type_index, ret_var, std::file!(), std::line!()),
|
constraints.store(ret_type_index, ret_var, std::file!(), std::line!()),
|
||||||
closure_constraint,
|
closure_constraint,
|
||||||
Constraint::FlexToPure(fx_var),
|
constraints.flex_to_pure(fx_var),
|
||||||
];
|
];
|
||||||
|
|
||||||
let and_constraint = constraints.and_constraint(cons);
|
let and_constraint = constraints.and_constraint(cons);
|
||||||
|
@ -4502,6 +4518,7 @@ fn rec_defs_help(
|
||||||
vars: Vec::with_capacity(arguments.len()),
|
vars: Vec::with_capacity(arguments.len()),
|
||||||
constraints: Vec::with_capacity(1),
|
constraints: Vec::with_capacity(1),
|
||||||
delayed_is_open_constraints: vec![],
|
delayed_is_open_constraints: vec![],
|
||||||
|
delayed_fx_suffix_constraints: Vec::with_capacity(arguments.len()),
|
||||||
};
|
};
|
||||||
let mut vars =
|
let mut vars =
|
||||||
Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
Vec::with_capacity(argument_pattern_state.vars.capacity() + 1);
|
||||||
|
@ -4578,6 +4595,10 @@ fn rec_defs_help(
|
||||||
expr_con,
|
expr_con,
|
||||||
generalizable,
|
generalizable,
|
||||||
),
|
),
|
||||||
|
// Check argument suffixes against usage
|
||||||
|
constraints.and_constraint(
|
||||||
|
argument_pattern_state.delayed_fx_suffix_constraints,
|
||||||
|
),
|
||||||
constraints.equal_types(
|
constraints.equal_types(
|
||||||
fn_type_index,
|
fn_type_index,
|
||||||
expected_index,
|
expected_index,
|
||||||
|
@ -4595,6 +4616,7 @@ fn rec_defs_help(
|
||||||
),
|
),
|
||||||
constraints.store(ret_type_index, ret_var, std::file!(), std::line!()),
|
constraints.store(ret_type_index, ret_var, std::file!(), std::line!()),
|
||||||
closure_constraint,
|
closure_constraint,
|
||||||
|
constraints.flex_to_pure(fx_var),
|
||||||
];
|
];
|
||||||
|
|
||||||
let and_constraint = constraints.and_constraint(cons);
|
let and_constraint = constraints.and_constraint(cons);
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub struct PatternState {
|
||||||
pub vars: Vec<Variable>,
|
pub vars: Vec<Variable>,
|
||||||
pub constraints: Vec<Constraint>,
|
pub constraints: Vec<Constraint>,
|
||||||
pub delayed_is_open_constraints: Vec<Constraint>,
|
pub delayed_is_open_constraints: Vec<Constraint>,
|
||||||
|
pub delayed_fx_suffix_constraints: Vec<Constraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If there is a type annotation, the pattern state headers can be optimized by putting the
|
/// If there is a type annotation, the pattern state headers can be optimized by putting the
|
||||||
|
@ -247,6 +248,28 @@ pub fn constrain_pattern(
|
||||||
region: Region,
|
region: Region,
|
||||||
expected: PExpectedTypeIndex,
|
expected: PExpectedTypeIndex,
|
||||||
state: &mut PatternState,
|
state: &mut PatternState,
|
||||||
|
) {
|
||||||
|
constrain_pattern_help(
|
||||||
|
types,
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
pattern,
|
||||||
|
region,
|
||||||
|
expected,
|
||||||
|
state,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn constrain_pattern_help(
|
||||||
|
types: &mut Types,
|
||||||
|
constraints: &mut Constraints,
|
||||||
|
env: &mut Env,
|
||||||
|
pattern: &Pattern,
|
||||||
|
region: Region,
|
||||||
|
expected: PExpectedTypeIndex,
|
||||||
|
state: &mut PatternState,
|
||||||
|
is_shallow: bool,
|
||||||
) {
|
) {
|
||||||
match pattern {
|
match pattern {
|
||||||
Underscore => {
|
Underscore => {
|
||||||
|
@ -276,9 +299,13 @@ pub fn constrain_pattern(
|
||||||
.push(constraints.is_open_type(type_index));
|
.push(constraints.is_open_type(type_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
if is_shallow {
|
||||||
.constraints
|
// Identifiers introduced in nested patterns get let constraints
|
||||||
.push(constraints.fx_pattern_suffix(*symbol, type_index, region));
|
// and therefore don't need fx_pattern_suffix constraints.
|
||||||
|
state
|
||||||
|
.delayed_fx_suffix_constraints
|
||||||
|
.push(constraints.fx_pattern_suffix(*symbol, type_index, region));
|
||||||
|
}
|
||||||
|
|
||||||
state.headers.insert(
|
state.headers.insert(
|
||||||
*symbol,
|
*symbol,
|
||||||
|
@ -301,7 +328,7 @@ pub fn constrain_pattern(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern_help(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -309,6 +336,7 @@ pub fn constrain_pattern(
|
||||||
subpattern.region,
|
subpattern.region,
|
||||||
expected,
|
expected,
|
||||||
state,
|
state,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,7 +562,7 @@ pub fn constrain_pattern(
|
||||||
));
|
));
|
||||||
state.vars.push(*guard_var);
|
state.vars.push(*guard_var);
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern_help(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -542,6 +570,7 @@ pub fn constrain_pattern(
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
expected,
|
expected,
|
||||||
state,
|
state,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
pat_type
|
pat_type
|
||||||
|
@ -632,7 +661,7 @@ pub fn constrain_pattern(
|
||||||
));
|
));
|
||||||
state.vars.push(*guard_var);
|
state.vars.push(*guard_var);
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern_help(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -640,6 +669,7 @@ pub fn constrain_pattern(
|
||||||
loc_guard.region,
|
loc_guard.region,
|
||||||
expected,
|
expected,
|
||||||
state,
|
state,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
RecordField::Demanded(pat_type)
|
RecordField::Demanded(pat_type)
|
||||||
|
@ -755,7 +785,7 @@ pub fn constrain_pattern(
|
||||||
loc_pat.region,
|
loc_pat.region,
|
||||||
));
|
));
|
||||||
|
|
||||||
constrain_pattern(
|
constrain_pattern_help(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -763,6 +793,7 @@ pub fn constrain_pattern(
|
||||||
loc_pat.region,
|
loc_pat.region,
|
||||||
expected,
|
expected,
|
||||||
state,
|
state,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,7 +842,7 @@ pub fn constrain_pattern(
|
||||||
pattern_type,
|
pattern_type,
|
||||||
region,
|
region,
|
||||||
));
|
));
|
||||||
constrain_pattern(
|
constrain_pattern_help(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -819,6 +850,7 @@ pub fn constrain_pattern(
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
expected,
|
expected,
|
||||||
state,
|
state,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +908,7 @@ pub fn constrain_pattern(
|
||||||
// First, add a constraint for the argument "who"
|
// First, add a constraint for the argument "who"
|
||||||
let arg_pattern_expected = constraints
|
let arg_pattern_expected = constraints
|
||||||
.push_pat_expected_type(PExpected::NoExpectation(arg_pattern_type_index));
|
.push_pat_expected_type(PExpected::NoExpectation(arg_pattern_type_index));
|
||||||
constrain_pattern(
|
constrain_pattern_help(
|
||||||
types,
|
types,
|
||||||
constraints,
|
constraints,
|
||||||
env,
|
env,
|
||||||
|
@ -884,6 +916,7 @@ pub fn constrain_pattern(
|
||||||
loc_arg_pattern.region,
|
loc_arg_pattern.region,
|
||||||
arg_pattern_expected,
|
arg_pattern_expected,
|
||||||
state,
|
state,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Next, link `whole_var` to the opaque type of "@Id who"
|
// Next, link `whole_var` to the opaque type of "@Id who"
|
||||||
|
|
|
@ -15275,4 +15275,91 @@ All branches in an `if` must have the same type!
|
||||||
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
fx_passed_to_untyped_pure_hof,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||||
|
|
||||||
|
import pf.Effect
|
||||||
|
|
||||||
|
main! = \{} ->
|
||||||
|
pureHigherOrder Effect.putLine! "hi"
|
||||||
|
|
||||||
|
pureHigherOrder = \f, x -> f x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||||
|
|
||||||
|
This 1st argument to `pureHigherOrder` has an unexpected type:
|
||||||
|
|
||||||
|
6│ pureHigherOrder Effect.putLine! "hi"
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This `Effect.putLine!` value is a:
|
||||||
|
|
||||||
|
Str => {}
|
||||||
|
|
||||||
|
But `pureHigherOrder` needs its 1st argument to be:
|
||||||
|
|
||||||
|
Str -> {}
|
||||||
|
|
||||||
|
── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
|
||||||
|
|
||||||
|
This function is pure, but its name suggests otherwise:
|
||||||
|
|
||||||
|
5│ main! = \{} ->
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
The exclamation mark at the end is reserved for effectful functions.
|
||||||
|
|
||||||
|
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
fx_passed_to_partially_inferred_pure_hof,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app [main!] { pf: platform "../../../../../examples/cli/effects-platform/main.roc" }
|
||||||
|
|
||||||
|
import pf.Effect
|
||||||
|
|
||||||
|
main! = \{} ->
|
||||||
|
pureHigherOrder Effect.putLine! "hi"
|
||||||
|
|
||||||
|
pureHigherOrder : _, _ -> _
|
||||||
|
pureHigherOrder = \f, x -> f x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||||
|
|
||||||
|
This 1st argument to `pureHigherOrder` has an unexpected type:
|
||||||
|
|
||||||
|
6│ pureHigherOrder Effect.putLine! "hi"
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This `Effect.putLine!` value is a:
|
||||||
|
|
||||||
|
Str => {}
|
||||||
|
|
||||||
|
But `pureHigherOrder` needs its 1st argument to be:
|
||||||
|
|
||||||
|
Str -> {}
|
||||||
|
|
||||||
|
── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
|
||||||
|
|
||||||
|
This function is pure, but its name suggests otherwise:
|
||||||
|
|
||||||
|
5│ main! = \{} ->
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
The exclamation mark at the end is reserved for effectful functions.
|
||||||
|
|
||||||
|
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
||||||
|
"###
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,7 +452,7 @@ fn solve(
|
||||||
|
|
||||||
new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value);
|
new_scope.insert_symbol_var_if_vacant(*symbol, loc_var.value);
|
||||||
|
|
||||||
check_ident_suffix(
|
solve_suffix_fx(
|
||||||
env,
|
env,
|
||||||
problems,
|
problems,
|
||||||
FxSuffixKind::Let(*symbol),
|
FxSuffixKind::Let(*symbol),
|
||||||
|
@ -853,7 +853,7 @@ fn solve(
|
||||||
*type_index,
|
*type_index,
|
||||||
);
|
);
|
||||||
|
|
||||||
check_ident_suffix(env, problems, *kind, actual, region);
|
solve_suffix_fx(env, problems, *kind, actual, region);
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
EffectfulStmt(variable, region) => {
|
EffectfulStmt(variable, region) => {
|
||||||
|
@ -1622,7 +1622,7 @@ fn solve(
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_ident_suffix(
|
fn solve_suffix_fx(
|
||||||
env: &mut InferenceEnv<'_>,
|
env: &mut InferenceEnv<'_>,
|
||||||
problems: &mut Vec<TypeError>,
|
problems: &mut Vec<TypeError>,
|
||||||
kind: FxSuffixKind,
|
kind: FxSuffixKind,
|
||||||
|
@ -1634,20 +1634,36 @@ fn check_ident_suffix(
|
||||||
if let Content::Structure(FlatType::Func(_, _, _, fx)) =
|
if let Content::Structure(FlatType::Func(_, _, _, fx)) =
|
||||||
env.subs.get_content_without_compacting(variable)
|
env.subs.get_content_without_compacting(variable)
|
||||||
{
|
{
|
||||||
if let Content::Effectful = env.subs.get_content_without_compacting(*fx) {
|
let fx = *fx;
|
||||||
problems.push(TypeError::UnsuffixedEffectfulFunction(*region, kind));
|
match env.subs.get_content_without_compacting(fx) {
|
||||||
|
Content::Effectful => {
|
||||||
|
problems.push(TypeError::UnsuffixedEffectfulFunction(*region, kind));
|
||||||
|
}
|
||||||
|
Content::FlexVar(_) => {
|
||||||
|
env.subs.set_content(fx, Content::Pure);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IdentSuffix::Bang => {
|
IdentSuffix::Bang => match env.subs.get_content_without_compacting(variable) {
|
||||||
if let Content::Structure(FlatType::Func(_, _, _, fx)) =
|
Content::Structure(FlatType::Func(_, _, _, fx)) => {
|
||||||
env.subs.get_content_without_compacting(variable)
|
let fx = *fx;
|
||||||
{
|
match env.subs.get_content_without_compacting(fx) {
|
||||||
if let Content::Pure = env.subs.get_content_without_compacting(*fx) {
|
Content::Pure => {
|
||||||
problems.push(TypeError::SuffixedPureFunction(*region, kind));
|
problems.push(TypeError::SuffixedPureFunction(*region, kind));
|
||||||
|
}
|
||||||
|
Content::FlexVar(_) => {
|
||||||
|
env.subs.set_content(fx, Content::Effectful);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Content::FlexVar(_) => {
|
||||||
|
// [purity-inference] TODO: Require effectful fn
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue