Add a bit to mark syntactically-generalizable types

This commit is contained in:
Ayaz Hafiz 2023-01-04 17:02:20 -06:00
parent 3b0e2429e6
commit bcfb258db8
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
3 changed files with 130 additions and 6 deletions

View file

@ -390,6 +390,8 @@ impl Constraints {
}
}
/// A constraint that some type variables exist and should be introduced into the environment
/// at a given rank.
#[inline(always)]
pub fn exists<I>(&mut self, flex_vars: I, defs_constraint: Constraint) -> Constraint
where
@ -405,6 +407,10 @@ impl Constraints {
flex_vars: self.variable_slice(flex_vars),
def_types: DefTypes::default(),
defs_and_ret_constraint,
// We're just marking that the variables exist, not that they should be generalized
// (in fact there is no return constraint, so nothing to generalize at this level)
generalizable: Generalizable(false),
};
let let_index = Index::new(self.let_constraints.len() as _);
@ -431,6 +437,7 @@ impl Constraints {
flex_vars: self.variable_slice(flex_vars),
def_types: DefTypes::default(),
defs_and_ret_constraint,
generalizable: Generalizable(false),
};
let let_index = Index::new(self.let_constraints.len() as _);
@ -447,6 +454,7 @@ impl Constraints {
def_types: I3,
defs_constraint: Constraint,
ret_constraint: Constraint,
generalizable: Generalizable,
) -> Constraint
where
I1: IntoIterator<Item = Variable>,
@ -465,6 +473,7 @@ impl Constraints {
flex_vars: self.variable_slice(flex_vars),
def_types: self.def_types_slice(def_types),
defs_and_ret_constraint,
generalizable,
};
let let_index = Index::new(self.let_constraints.len() as _);
@ -513,6 +522,9 @@ impl Constraints {
flex_vars: self.variable_slice(flex_vars),
def_types: self.def_types_slice(def_types),
defs_and_ret_constraint,
// If the module these variables were solved in solved them as generalized,
// they should be generalized here too.
generalizable: Generalizable(true),
};
let let_index = Index::new(self.let_constraints.len() as _);
@ -769,12 +781,22 @@ pub struct DefTypes {
pub loc_symbols: Slice<(Symbol, Region)>,
}
#[derive(Debug, Clone, Copy)]
pub struct Generalizable(pub bool);
#[derive(Debug, Clone)]
pub struct LetConstraint {
pub rigid_vars: Slice<Variable>,
pub flex_vars: Slice<Variable>,
pub def_types: DefTypes,
pub defs_and_ret_constraint: Index<(Constraint, Constraint)>,
/// Whether the defs introduces in the let-binding can be generalized.
/// Only
/// - syntactic functions
/// - syntactic numbers
/// may be eligible for generalization, though there are other restrictions too.
pub generalizable: Generalizable,
}
#[derive(Debug, Clone)]

View file

@ -8,7 +8,7 @@ use crate::builtins::{
use crate::pattern::{constrain_pattern, PatternState};
use roc_can::annotation::IntroducedVariables;
use roc_can::constraint::{
Constraint, Constraints, ExpectedTypeIndex, OpportunisticResolve, TypeOrVar,
Constraint, Constraints, ExpectedTypeIndex, Generalizable, OpportunisticResolve, TypeOrVar,
};
use roc_can::def::Def;
use roc_can::exhaustive::{sketch_pattern_to_rows, sketch_when_branches, ExhaustiveContext};
@ -176,6 +176,7 @@ fn constrain_untyped_closure(
pattern_state.headers,
pattern_state_constraints,
ret_constraint,
Generalizable(true),
),
constraints.equal_types_with_storage(
function_type,
@ -1054,6 +1055,7 @@ pub fn constrain_expr(
pattern_headers,
pattern_constraints,
body_constraints,
Generalizable(false),
);
let result_con =
@ -1874,6 +1876,8 @@ fn constrain_function_def(
argument_pattern_state.headers,
defs_constraint,
ret_constraint,
// This is a syntactic function, it can be generalized
Generalizable(true),
),
constraints.equal_types_var(
closure_var,
@ -1927,6 +1931,7 @@ fn constrain_function_def(
loc_symbol,
expr_var,
expr_type,
Generalizable(true), // this is a syntactic function
)
}
}
@ -2057,6 +2062,8 @@ fn constrain_value_def(
let expr_var = declarations.variables[index];
let opt_annotation = &declarations.annotations[index];
let generalizable = Generalizable(is_generalizable_expr(&loc_expr.value));
match opt_annotation {
Some(annotation) => {
let arity = 1;
@ -2122,6 +2129,7 @@ fn constrain_value_def(
loc_symbol,
expr_var,
signature_index,
generalizable,
)
}
None => {
@ -2148,6 +2156,7 @@ fn constrain_value_def(
loc_symbol,
expr_var,
expr_type,
generalizable,
)
}
}
@ -2267,7 +2276,14 @@ fn constrain_when_branch_help(
// must introduce the headers from the pattern before constraining the guard
let delayed_is_open_constraints = state.delayed_is_open_constraints;
let state_constraints = constraints.and_constraint(state.constraints);
let inner = constraints.let_constraint([], [], [], guard_constraint, ret_constraint);
let inner = constraints.let_constraint(
[],
[],
[],
guard_constraint,
ret_constraint,
Generalizable(false),
);
(state_constraints, delayed_is_open_constraints, inner)
} else {
@ -2377,7 +2393,14 @@ pub fn constrain_decls(
expected,
);
constraint = constraints.let_constraint([], [], [], expect_constraint, constraint)
constraint = constraints.let_constraint(
[],
[],
[],
expect_constraint,
constraint,
Generalizable(false),
)
}
ExpectationFx => {
let loc_expr = &declarations.expressions[index];
@ -2398,7 +2421,14 @@ pub fn constrain_decls(
expected,
);
constraint = constraints.let_constraint([], [], [], expect_constraint, constraint)
constraint = constraints.let_constraint(
[],
[],
[],
expect_constraint,
constraint,
Generalizable(false),
)
}
Function(function_def_index) => {
constraint = constrain_function_def(
@ -2653,6 +2683,8 @@ fn constrain_typed_def(
argument_pattern_state.headers,
defs_constraint,
ret_constraint,
// This is a syntactic function, it can be generalized
Generalizable(true),
),
constraints.equal_types_var(
closure_var,
@ -2675,6 +2707,7 @@ fn constrain_typed_def(
expr_con,
body_con,
def_pattern_state,
Generalizable(true),
)
}
@ -2698,6 +2731,8 @@ fn constrain_typed_def(
);
let expr_con = attach_resolution_constraints(constraints, env, ret_constraint);
let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value));
constrain_def_make_constraint(
constraints,
new_rigid_variables.into_iter(),
@ -2705,6 +2740,7 @@ fn constrain_typed_def(
expr_con,
body_con,
def_pattern_state,
generalizable,
)
}
}
@ -3011,6 +3047,8 @@ fn constrain_def(
);
let expr_con = attach_resolution_constraints(constraints, env, expr_con);
let generalizable = Generalizable(is_generalizable_expr(&def.loc_expr.value));
constrain_def_make_constraint(
constraints,
std::iter::empty(),
@ -3018,6 +3056,7 @@ fn constrain_def(
expr_con,
body_con,
def_pattern_state,
generalizable,
)
}
}
@ -3032,6 +3071,7 @@ pub(crate) fn constrain_def_make_constraint(
def_expr_con: Constraint,
after_def_con: Constraint,
def_pattern_state: PatternState,
generalizable: Generalizable,
) -> Constraint {
let all_flex_variables = (def_pattern_state.vars.into_iter()).chain(annotation_infer_variables);
@ -3044,6 +3084,7 @@ pub(crate) fn constrain_def_make_constraint(
def_pattern_state.headers,
def_pattern_and_body_con,
after_def_con,
generalizable,
)
}
@ -3056,6 +3097,7 @@ fn constrain_value_def_make_constraint(
symbol: Loc<Symbol>,
expr_var: Variable,
expr_type: TypeOrVar,
generalizable: Generalizable,
) -> Constraint {
let def_con = constraints.let_constraint(
[],
@ -3063,11 +3105,19 @@ fn constrain_value_def_make_constraint(
[], // empty, because our functions have no arguments!
Constraint::True,
expr_con,
generalizable,
);
let headers = [(symbol.value, Loc::at(symbol.region, expr_type))];
constraints.let_constraint(new_rigid_variables, [expr_var], headers, def_con, body_con)
constraints.let_constraint(
new_rigid_variables,
[expr_var],
headers,
def_con,
body_con,
generalizable,
)
}
fn constrain_function_def_make_constraint(
@ -3086,6 +3136,7 @@ fn constrain_function_def_make_constraint(
[], // empty, because our functions have no arguments!
and_constraint,
expr_con,
Generalizable(true),
);
constraints.let_constraint(
@ -3094,6 +3145,7 @@ fn constrain_function_def_make_constraint(
def_pattern_state.headers,
def_con,
body_con,
Generalizable(true),
)
}
@ -3469,6 +3521,8 @@ fn constraint_recursive_function(
argument_pattern_state.headers,
state_constraints,
expr_con,
// Syntactic function can be generalized
Generalizable(true),
),
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
@ -3512,6 +3566,7 @@ fn constraint_recursive_function(
def_pattern_state.headers.clone(),
def_con,
Constraint::True,
Generalizable(true),
)
});
rigid_info.def_types.extend(def_pattern_state.headers);
@ -3540,6 +3595,18 @@ pub fn rec_defs_help_simple(
let mut loc_symbols = Vec::with_capacity(length);
let mut expr_regions = Vec::with_capacity(length);
// Rec defs are generalizable only if everything in the cycle is generalizable.
let generalizable = {
let generalizable = range.clone().all(|i| match declarations.declarations[i] {
DeclarationTag::Value => {
let loc_expr = &declarations.expressions[i];
is_generalizable_expr(&loc_expr.value)
}
_ => true, // this must be a function
});
Generalizable(generalizable)
};
for index in range {
// Clear the rigids from the previous iteration.
// rigids are not shared between top-level definitions
@ -3654,6 +3721,7 @@ pub fn rec_defs_help_simple(
[], // no headers introduced (at this level)
def_con,
Constraint::True,
generalizable,
));
rigid_info.def_types.insert(loc_symbol.value, loc_type);
}
@ -3712,6 +3780,7 @@ pub fn rec_defs_help_simple(
hybrid_and_flex_info.def_types.clone(),
Constraint::True,
untyped_body_constraints,
generalizable,
);
// an extra constraint that propagates information to the solver to check for invalid recursion
@ -3730,6 +3799,7 @@ pub fn rec_defs_help_simple(
hybrid_and_flex_info.def_types,
untyped_def_symbols_constr,
typed_body_and_final_constr,
generalizable,
);
// 1. Let-generalize annotations we know.
@ -3739,9 +3809,24 @@ pub fn rec_defs_help_simple(
rigid_info.def_types,
Constraint::True,
inner,
generalizable,
)
}
/// A let-bound expression is generalizable if it is
/// - a syntactic function under an opaque wrapper
/// - a number literal under an opaque wrapper
fn is_generalizable_expr(mut expr: &Expr) -> bool {
loop {
match expr {
Num(..) | Int(..) | Float(..) => return true,
Closure(_) => return true,
OpaqueRef { argument, .. } => expr = &argument.1.value,
_ => return false,
}
}
}
fn constrain_recursive_defs(
types: &mut Types,
constraints: &mut Constraints,
@ -3768,6 +3853,14 @@ fn rec_defs_help(
let mut rigid_info = Info::with_capacity(defs.len());
let mut hybrid_and_flex_info = Info::with_capacity(defs.len());
// Rec defs are generalizable only if everything in the cycle is generalizable.
let generalizable = {
let generalizable = defs
.iter()
.all(|d| is_generalizable_expr(&d.loc_expr.value));
Generalizable(generalizable)
};
for def in defs {
let expr_var = def.expr_var;
let expr_type_index = constraints.push_variable(expr_var);
@ -3927,6 +4020,7 @@ fn rec_defs_help(
state.headers,
state_constraints,
expr_con,
generalizable,
),
constraints.equal_types(
fn_type_index,
@ -3968,6 +4062,7 @@ fn rec_defs_help(
[], // no headers introduced (at this level)
def_con,
Constraint::True,
generalizable,
));
rigid_info.def_types.extend(def_pattern_state.headers);
}
@ -4013,6 +4108,7 @@ fn rec_defs_help(
[], // no headers introduced (at this level)
def_con,
Constraint::True,
generalizable,
));
rigid_info.def_types.extend(def_pattern_state.headers);
}
@ -4056,6 +4152,7 @@ fn rec_defs_help(
hybrid_and_flex_info.def_types.clone(),
Constraint::True,
untyped_body_constraints,
generalizable,
);
// an extra constraint that propagates information to the solver to check for invalid recursion
@ -4082,6 +4179,7 @@ fn rec_defs_help(
untyped_def_symbols_constr,
// 4 + 5. Solve the typed body defs, and the rest of the program.
typed_body_and_final_constr,
generalizable,
);
// 1. Let-generalize annotations we know.
@ -4091,6 +4189,7 @@ fn rec_defs_help(
rigid_info.def_types,
Constraint::True,
inner,
generalizable,
)
}

View file

@ -1,6 +1,6 @@
use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env};
use roc_can::abilities::{PendingAbilitiesStore, PendingMemberType};
use roc_can::constraint::{Constraint, Constraints};
use roc_can::constraint::{Constraint, Constraints, Generalizable};
use roc_can::expected::Expected;
use roc_can::expr::Declarations;
use roc_can::pattern::Pattern;
@ -76,6 +76,7 @@ fn constrain_symbols_from_requires(
Constraint::True,
constraint,
def_pattern_state,
Generalizable(true),
)
} else {
// Otherwise, this symbol comes from an app module - we want to check that the type
@ -158,6 +159,8 @@ pub fn frontload_ability_constraints(
Constraint::True,
constraint,
def_pattern_state,
// Ability signatures can always be generalized since they're prototypical
Generalizable(true),
);
}
}