mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
even more wip
This commit is contained in:
parent
b8fd6992a2
commit
01a7fe77d4
10 changed files with 2710 additions and 64 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3416,6 +3416,7 @@ dependencies = [
|
|||
name = "roc_constrain"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
|
|
|
@ -360,8 +360,7 @@ fn can_annotation_help(
|
|||
As(
|
||||
loc_inner,
|
||||
_spaces,
|
||||
alias_header
|
||||
@ TypeHeader {
|
||||
alias_header @ TypeHeader {
|
||||
name,
|
||||
vars: loc_vars,
|
||||
},
|
||||
|
|
|
@ -133,7 +133,7 @@ impl Constraints {
|
|||
Constraint::Let(let_index)
|
||||
}
|
||||
|
||||
pub fn let_contraint<I1, I2, I3>(
|
||||
pub fn let_constraint<I1, I2, I3>(
|
||||
&mut self,
|
||||
rigid_vars: I1,
|
||||
flex_vars: I2,
|
||||
|
@ -164,7 +164,7 @@ impl Constraints {
|
|||
Constraint::Let(let_index)
|
||||
}
|
||||
|
||||
pub fn and_contraint<I>(&mut self, constraints: I) -> Constraint
|
||||
pub fn and_constraint<I>(&mut self, constraints: I) -> Constraint
|
||||
where
|
||||
I: IntoIterator<Item = Constraint>,
|
||||
{
|
||||
|
@ -178,6 +178,36 @@ impl Constraints {
|
|||
|
||||
Constraint::And(slice)
|
||||
}
|
||||
|
||||
pub fn lookup(
|
||||
&mut self,
|
||||
symbol: Symbol,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
Constraint::Lookup(
|
||||
symbol,
|
||||
Index::push_new(&mut self.expectations, expected),
|
||||
region,
|
||||
)
|
||||
}
|
||||
pub fn contains_save_the_environment(&self, constraint: Constraint) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn store(
|
||||
&mut self,
|
||||
typ: Type,
|
||||
variable: Variable,
|
||||
filename: &'static str,
|
||||
line_number: u32,
|
||||
) -> Constraint {
|
||||
let type_index = Index::new(self.types.len() as _);
|
||||
|
||||
self.types.push(typ);
|
||||
|
||||
Constraint::Store(type_index, variable, filename, line_number)
|
||||
}
|
||||
}
|
||||
|
||||
static_assertions::assert_eq_size!([u8; 4 * 8], Constraint);
|
||||
|
|
|
@ -14,3 +14,4 @@ roc_parse = { path = "../parse" }
|
|||
roc_types = { path = "../types" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_builtins = { path = "../builtins" }
|
||||
arrayvec = "0.7.2"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use arrayvec::ArrayVec;
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
use roc_can::constraint::LetConstraint;
|
||||
use roc_can::constraint_soa;
|
||||
use roc_can::constraint_soa::Constraints;
|
||||
use roc_can::expected::Expected::{self, *};
|
||||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
||||
|
@ -40,6 +42,35 @@ pub fn add_numeric_bound_constr(
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn add_numeric_bound_constr_soa(
|
||||
constraints: &mut Constraints,
|
||||
num_constraints: &mut impl Extend<constraint_soa::Constraint>,
|
||||
num_type: Type,
|
||||
bound: impl TypedNumericBound,
|
||||
region: Region,
|
||||
category: Category,
|
||||
) -> Type {
|
||||
let range = bound.bounded_range();
|
||||
|
||||
let total_num_type = num_type;
|
||||
|
||||
match range.len() {
|
||||
0 => total_num_type,
|
||||
1 => {
|
||||
let actual_type = Variable(range[0]);
|
||||
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||
let because_suffix = constraints.equal_types(actual_type, expected, category, region);
|
||||
|
||||
num_constraints.extend([because_suffix]);
|
||||
|
||||
total_num_type
|
||||
}
|
||||
_ => RangedNumber(Box::new(total_num_type), range),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal(
|
||||
num_var: Variable,
|
||||
|
@ -83,15 +114,6 @@ pub fn float_literal(
|
|||
) -> Constraint {
|
||||
let reason = Reason::FloatLiteral;
|
||||
|
||||
let value_is_float_literal = Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
);
|
||||
|
||||
let expected_float = Eq(num_type, expected, Category::Float, region);
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
let num_type = {
|
||||
let constrs: &mut Vec<Constraint> = &mut constrs;
|
||||
|
@ -121,38 +143,6 @@ pub fn float_literal(
|
|||
exists(vec![num_var, precision_var], And(constrs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_literal_soa(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: FloatBound,
|
||||
) -> Constraint {
|
||||
let reason = Reason::FloatLiteral;
|
||||
|
||||
let mut constrs = Vec::with_capacity(3);
|
||||
let num_type = add_numeric_bound_constr(
|
||||
&mut constrs,
|
||||
Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
);
|
||||
constrs.extend(vec![
|
||||
Eq(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
Eq(num_type, expected, Category::Float, region),
|
||||
]);
|
||||
|
||||
exists(vec![num_var, precision_var], And(constrs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_literal(
|
||||
num_var: Variable,
|
||||
|
@ -170,6 +160,104 @@ pub fn num_literal(
|
|||
exists(vec![num_var], And(constrs))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn int_literal_soa(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: IntBound,
|
||||
) -> constraint_soa::Constraint {
|
||||
let reason = Reason::IntLiteral;
|
||||
|
||||
// Always add the bound first; this improves the resolved type quality in case it's an alias like "U8".
|
||||
let mut constrs = ArrayVec::<_, 3>::new();
|
||||
let num_type = add_numeric_bound_constr_soa(
|
||||
constraints,
|
||||
&mut constrs,
|
||||
Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Num,
|
||||
);
|
||||
|
||||
constrs.extend([
|
||||
constraints.equal_types(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_int(Type::Variable(precision_var)), region),
|
||||
Category::Int,
|
||||
region,
|
||||
),
|
||||
constraints.equal_types(num_type, expected, Category::Int, region),
|
||||
]);
|
||||
|
||||
// TODO the precision_var is not part of the exists here; for float it is. Which is correct?
|
||||
let and_constraint = constraints.and_constraint(constrs);
|
||||
constraints.exists([num_var], and_constraint)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn float_literal_soa(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
precision_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: FloatBound,
|
||||
) -> constraint_soa::Constraint {
|
||||
let reason = Reason::FloatLiteral;
|
||||
|
||||
let mut constrs = ArrayVec::<_, 3>::new();
|
||||
let num_type = add_numeric_bound_constr_soa(
|
||||
constraints,
|
||||
&mut constrs,
|
||||
Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
);
|
||||
|
||||
constrs.extend([
|
||||
constraints.equal_types(
|
||||
num_type.clone(),
|
||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||
Category::Float,
|
||||
region,
|
||||
),
|
||||
constraints.equal_types(num_type, expected, Category::Float, region),
|
||||
]);
|
||||
|
||||
let and_constraint = constraints.and_constraint(constrs);
|
||||
constraints.exists([num_var, precision_var], and_constraint)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn num_literal_soa(
|
||||
constraints: &mut Constraints,
|
||||
num_var: Variable,
|
||||
expected: Expected<Type>,
|
||||
region: Region,
|
||||
bound: NumericBound,
|
||||
) -> constraint_soa::Constraint {
|
||||
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
||||
|
||||
let mut constrs = ArrayVec::<_, 2>::new();
|
||||
let num_type = add_numeric_bound_constr_soa(
|
||||
constraints,
|
||||
&mut constrs,
|
||||
open_number_type,
|
||||
bound,
|
||||
region,
|
||||
Category::Num,
|
||||
);
|
||||
|
||||
constrs.extend([constraints.equal_types(num_type, expected, Category::Num, region)]);
|
||||
|
||||
let and_constraint = constraints.and_constraint(constrs);
|
||||
constraints.exists([num_var], and_constraint)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn exists(flex_vars: Vec<Variable>, constraint: Constraint) -> Constraint {
|
||||
Let(Box::new(LetConstraint {
|
||||
|
|
|
@ -6,3 +6,4 @@ pub mod expr;
|
|||
pub mod module;
|
||||
pub mod pattern;
|
||||
pub mod soa_expr;
|
||||
pub mod soa_pattern;
|
||||
|
|
2017
compiler/constrain/src/soa_expr.rs
Normal file
2017
compiler/constrain/src/soa_expr.rs
Normal file
File diff suppressed because it is too large
Load diff
521
compiler/constrain/src/soa_pattern.rs
Normal file
521
compiler/constrain/src/soa_pattern.rs
Normal file
|
@ -0,0 +1,521 @@
|
|||
use crate::builtins;
|
||||
use crate::soa_expr::{constrain_expr, Env};
|
||||
use roc_can::constraint_soa::{Constraint, Constraints, PresenceConstraint};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::pattern::Pattern::{self, *};
|
||||
use roc_can::pattern::{DestructType, RecordDestruct};
|
||||
use roc_collections::all::{HumanIndex, SendMap};
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::subs::Variable;
|
||||
use roc_types::types::{AliasKind, Category, PReason, PatternCategory, Reason, RecordField, Type};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PatternState {
|
||||
pub headers: SendMap<Symbol, Loc<Type>>,
|
||||
pub vars: Vec<Variable>,
|
||||
pub constraints: Vec<Constraint>,
|
||||
}
|
||||
|
||||
/// If there is a type annotation, the pattern state headers can be optimized by putting the
|
||||
/// annotation in the headers. Normally
|
||||
///
|
||||
/// x = 4
|
||||
///
|
||||
/// Would add `x => <42>` to the headers (i.e., symbol points to a type variable). If the
|
||||
/// definition has an annotation, we instead now add `x => Int`.
|
||||
pub fn headers_from_annotation(
|
||||
pattern: &Pattern,
|
||||
annotation: &Loc<Type>,
|
||||
) -> Option<SendMap<Symbol, Loc<Type>>> {
|
||||
let mut headers = SendMap::default();
|
||||
// Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int`
|
||||
// in such incorrect cases we don't put the full annotation in headers, just a variable, and let
|
||||
// inference generate a proper error.
|
||||
let is_structurally_valid = headers_from_annotation_help(pattern, annotation, &mut headers);
|
||||
|
||||
if is_structurally_valid {
|
||||
Some(headers)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn headers_from_annotation_help(
|
||||
pattern: &Pattern,
|
||||
annotation: &Loc<Type>,
|
||||
headers: &mut SendMap<Symbol, Loc<Type>>,
|
||||
) -> bool {
|
||||
match pattern {
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
headers.insert(*symbol, annotation.clone());
|
||||
true
|
||||
}
|
||||
Underscore
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| OpaqueNotInScope(..)
|
||||
| NumLiteral(..)
|
||||
| IntLiteral(..)
|
||||
| FloatLiteral(..)
|
||||
| SingleQuote(_)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
|
||||
Type::Record(fields, _) => {
|
||||
for loc_destruct in destructs {
|
||||
let destruct = &loc_destruct.value;
|
||||
|
||||
// NOTE: We ignore both Guard and optionality when
|
||||
// determining the type of the assigned def (which is what
|
||||
// gets added to the header here).
|
||||
//
|
||||
// For example, no matter whether it's `{ x } = rec` or
|
||||
// `{ x ? 0 } = rec` or `{ x: 5 } -> ...` in all cases
|
||||
// the type of `x` within the binding itself is the same.
|
||||
if let Some(field_type) = fields.get(&destruct.label) {
|
||||
headers.insert(
|
||||
destruct.symbol,
|
||||
Loc::at(annotation.region, field_type.clone().into_inner()),
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
Type::EmptyRec => destructs.is_empty(),
|
||||
_ => false,
|
||||
},
|
||||
|
||||
AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
..
|
||||
} => match annotation.value.shallow_dealias() {
|
||||
Type::TagUnion(tags, _) => {
|
||||
if let Some((_, arg_types)) = tags.iter().find(|(name, _)| name == tag_name) {
|
||||
if !arguments.len() == arg_types.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
arguments
|
||||
.iter()
|
||||
.zip(arg_types.iter())
|
||||
.all(|(arg_pattern, arg_type)| {
|
||||
headers_from_annotation_help(
|
||||
&arg_pattern.1.value,
|
||||
&Loc::at(annotation.region, arg_type.clone()),
|
||||
headers,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
|
||||
UnwrappedOpaque {
|
||||
whole_var: _,
|
||||
opaque,
|
||||
argument,
|
||||
specialized_def_type: _,
|
||||
type_arguments: pat_type_arguments,
|
||||
lambda_set_variables: pat_lambda_set_variables,
|
||||
} => match &annotation.value {
|
||||
Type::Alias {
|
||||
symbol,
|
||||
kind: AliasKind::Opaque,
|
||||
actual,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
} if symbol == opaque
|
||||
&& type_arguments.len() == pat_type_arguments.len()
|
||||
&& lambda_set_variables.len() == pat_lambda_set_variables.len() =>
|
||||
{
|
||||
headers.insert(*opaque, annotation.clone());
|
||||
|
||||
let (_, argument_pat) = &**argument;
|
||||
headers_from_annotation_help(
|
||||
&argument_pat.value,
|
||||
&Loc::at(annotation.region, (**actual).clone()),
|
||||
headers,
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// This accepts PatternState (rather than returning it) so that the caller can
|
||||
/// initialize the Vecs in PatternState using with_capacity
|
||||
/// based on its knowledge of their lengths.
|
||||
pub fn constrain_pattern(
|
||||
constraints: &mut Constraints,
|
||||
env: &Env,
|
||||
pattern: &Pattern,
|
||||
region: Region,
|
||||
expected: PExpected<Type>,
|
||||
state: &mut PatternState,
|
||||
) {
|
||||
match pattern {
|
||||
Underscore => {
|
||||
// This is an underscore in a position where we destruct a variable,
|
||||
// like a when expression:
|
||||
// when x is
|
||||
// A -> ""
|
||||
// _ -> ""
|
||||
// so, we know that "x" (in this case, a tag union) must be open.
|
||||
state.constraints.push(Constraint::Present(
|
||||
expected.get_type(),
|
||||
PresenceConstraint::IsOpen,
|
||||
));
|
||||
}
|
||||
UnsupportedPattern(_) | MalformedPattern(_, _) | OpaqueNotInScope(..) => {
|
||||
// Erroneous patterns don't add any constraints.
|
||||
}
|
||||
|
||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||
state.constraints.push(Constraint::Present(
|
||||
expected.get_type_ref().clone(),
|
||||
PresenceConstraint::IsOpen,
|
||||
));
|
||||
state.headers.insert(
|
||||
*symbol,
|
||||
Loc {
|
||||
region,
|
||||
value: expected.get_type(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
&NumLiteral(var, _, _, bound) => {
|
||||
state.vars.push(var);
|
||||
|
||||
let num_type = builtins::num_num(Type::Variable(var));
|
||||
|
||||
let num_type = builtins::add_numeric_bound_constr_soa(
|
||||
constraints,
|
||||
&mut state.constraints,
|
||||
num_type,
|
||||
bound,
|
||||
region,
|
||||
Category::Num,
|
||||
);
|
||||
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Num,
|
||||
num_type,
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
&IntLiteral(num_var, precision_var, _, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
let num_type = builtins::add_numeric_bound_constr(
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Int,
|
||||
);
|
||||
|
||||
// Link the free num var with the int var and our expectation.
|
||||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(constraints.equal_types(
|
||||
num_type, // TODO check me if something breaks!
|
||||
Expected::NoExpectation(int_type),
|
||||
Category::Int,
|
||||
region,
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(constraints.equal_pattern_types(
|
||||
Type::Variable(num_var),
|
||||
expected,
|
||||
PatternCategory::Int,
|
||||
region,
|
||||
));
|
||||
}
|
||||
|
||||
&FloatLiteral(num_var, precision_var, _, _, bound) => {
|
||||
// First constraint on the free num var; this improves the resolved type quality in
|
||||
// case the bound is an alias.
|
||||
let num_type = builtins::add_numeric_bound_constr_soa(
|
||||
constraints,
|
||||
&mut state.constraints,
|
||||
Type::Variable(num_var),
|
||||
bound,
|
||||
region,
|
||||
Category::Float,
|
||||
);
|
||||
|
||||
// Link the free num var with the float var and our expectation.
|
||||
let float_type = builtins::num_float(Type::Variable(precision_var));
|
||||
|
||||
state.constraints.push(constraints.equal_types(
|
||||
num_type.clone(), // TODO check me if something breaks!
|
||||
Expected::NoExpectation(float_type),
|
||||
Category::Float,
|
||||
region,
|
||||
));
|
||||
|
||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
num_type, // TODO check me if something breaks!
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
StrLiteral(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Str,
|
||||
builtins::str_type(),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
SingleQuote(_) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Character,
|
||||
builtins::num_u32(),
|
||||
expected,
|
||||
));
|
||||
}
|
||||
|
||||
RecordDestructure {
|
||||
whole_var,
|
||||
ext_var,
|
||||
destructs,
|
||||
} => {
|
||||
state.vars.push(*whole_var);
|
||||
state.vars.push(*ext_var);
|
||||
let ext_type = Type::Variable(*ext_var);
|
||||
|
||||
let mut field_types: SendMap<Lowercase, RecordField<Type>> = SendMap::default();
|
||||
|
||||
for Loc {
|
||||
value:
|
||||
RecordDestruct {
|
||||
var,
|
||||
label,
|
||||
symbol,
|
||||
typ,
|
||||
},
|
||||
..
|
||||
} in destructs
|
||||
{
|
||||
let pat_type = Type::Variable(*var);
|
||||
let expected = PExpected::NoExpectation(pat_type.clone());
|
||||
|
||||
if !state.headers.contains_key(symbol) {
|
||||
state
|
||||
.headers
|
||||
.insert(*symbol, Loc::at(region, pat_type.clone()));
|
||||
}
|
||||
|
||||
let field_type = match typ {
|
||||
DestructType::Guard(guard_var, loc_guard) => {
|
||||
state.constraints.push(Constraint::Present(
|
||||
Type::Variable(*guard_var),
|
||||
PresenceConstraint::Pattern(
|
||||
region,
|
||||
PatternCategory::PatternGuard,
|
||||
PExpected::ForReason(
|
||||
PReason::PatternGuard,
|
||||
pat_type.clone(),
|
||||
loc_guard.region,
|
||||
),
|
||||
),
|
||||
));
|
||||
state.vars.push(*guard_var);
|
||||
|
||||
constrain_pattern(env, &loc_guard.value, loc_guard.region, expected, state);
|
||||
|
||||
RecordField::Demanded(pat_type)
|
||||
}
|
||||
DestructType::Optional(expr_var, loc_expr) => {
|
||||
state.constraints.push(Constraint::Present(
|
||||
Type::Variable(*expr_var),
|
||||
PresenceConstraint::Pattern(
|
||||
region,
|
||||
PatternCategory::PatternDefault,
|
||||
PExpected::ForReason(
|
||||
PReason::OptionalField,
|
||||
pat_type.clone(),
|
||||
loc_expr.region,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
state.vars.push(*expr_var);
|
||||
|
||||
let expr_expected = Expected::ForReason(
|
||||
Reason::RecordDefaultField(label.clone()),
|
||||
pat_type.clone(),
|
||||
loc_expr.region,
|
||||
);
|
||||
|
||||
let expr_con =
|
||||
constrain_expr(env, loc_expr.region, &loc_expr.value, expr_expected);
|
||||
state.constraints.push(expr_con);
|
||||
|
||||
RecordField::Optional(pat_type)
|
||||
}
|
||||
DestructType::Required => {
|
||||
// No extra constraints necessary.
|
||||
RecordField::Demanded(pat_type)
|
||||
}
|
||||
};
|
||||
|
||||
field_types.insert(label.clone(), field_type);
|
||||
|
||||
state.vars.push(*var);
|
||||
}
|
||||
|
||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
||||
|
||||
let whole_con = constraints.equal_types(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(record_type),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
let record_con = Constraint::Present(
|
||||
Type::Variable(*whole_var),
|
||||
PresenceConstraint::Pattern(region, PatternCategory::Record, expected),
|
||||
);
|
||||
|
||||
state.constraints.push(whole_con);
|
||||
state.constraints.push(record_con);
|
||||
}
|
||||
AppliedTag {
|
||||
whole_var,
|
||||
ext_var,
|
||||
tag_name,
|
||||
arguments,
|
||||
} => {
|
||||
let mut argument_types = Vec::with_capacity(arguments.len());
|
||||
for (index, (pattern_var, loc_pattern)) in arguments.iter().enumerate() {
|
||||
state.vars.push(*pattern_var);
|
||||
|
||||
let pattern_type = Type::Variable(*pattern_var);
|
||||
argument_types.push(pattern_type.clone());
|
||||
|
||||
let expected = PExpected::ForReason(
|
||||
PReason::TagArg {
|
||||
tag_name: tag_name.clone(),
|
||||
index: HumanIndex::zero_based(index),
|
||||
},
|
||||
pattern_type,
|
||||
region,
|
||||
);
|
||||
constrain_pattern(env, &loc_pattern.value, loc_pattern.region, expected, state);
|
||||
}
|
||||
|
||||
let pat_category = PatternCategory::Ctor(tag_name.clone());
|
||||
|
||||
let whole_con = Constraint::Present(
|
||||
expected.clone().get_type(),
|
||||
PresenceConstraint::IncludesTag(
|
||||
tag_name.clone(),
|
||||
argument_types.clone(),
|
||||
region,
|
||||
pat_category.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
let tag_con = Constraint::Present(
|
||||
Type::Variable(*whole_var),
|
||||
PresenceConstraint::Pattern(region, pat_category, expected),
|
||||
);
|
||||
|
||||
state.vars.push(*whole_var);
|
||||
state.vars.push(*ext_var);
|
||||
state.constraints.push(whole_con);
|
||||
state.constraints.push(tag_con);
|
||||
}
|
||||
|
||||
UnwrappedOpaque {
|
||||
whole_var,
|
||||
opaque,
|
||||
argument,
|
||||
specialized_def_type,
|
||||
type_arguments,
|
||||
lambda_set_variables,
|
||||
} => {
|
||||
// Suppose we are constraining the pattern \@Id who, where Id n := [ Id U64 n ]
|
||||
let (arg_pattern_var, loc_arg_pattern) = &**argument;
|
||||
let arg_pattern_type = Type::Variable(*arg_pattern_var);
|
||||
|
||||
let opaque_type = Type::Alias {
|
||||
symbol: *opaque,
|
||||
type_arguments: type_arguments.clone(),
|
||||
lambda_set_variables: lambda_set_variables.clone(),
|
||||
actual: Box::new(arg_pattern_type.clone()),
|
||||
kind: AliasKind::Opaque,
|
||||
};
|
||||
|
||||
// First, add a constraint for the argument "who"
|
||||
let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone());
|
||||
constrain_pattern(
|
||||
env,
|
||||
&loc_arg_pattern.value,
|
||||
loc_arg_pattern.region,
|
||||
arg_pattern_expected,
|
||||
state,
|
||||
);
|
||||
|
||||
// Next, link `whole_var` to the opaque type of "@Id who"
|
||||
let whole_con = constraints.equal_types(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(opaque_type),
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
// Link the entire wrapped opaque type (with the now-constrained argument) to the type
|
||||
// variables of the opaque type
|
||||
// TODO: better expectation here
|
||||
let link_type_variables_con = constraints.equal_types(
|
||||
(**specialized_def_type).clone(),
|
||||
Expected::NoExpectation(arg_pattern_type),
|
||||
Category::OpaqueWrap(*opaque),
|
||||
loc_arg_pattern.region,
|
||||
);
|
||||
|
||||
// Next, link `whole_var` (the type of "@Id who") to the expected type
|
||||
let opaque_pattern_con = Constraint::Present(
|
||||
Type::Variable(*whole_var),
|
||||
PresenceConstraint::Pattern(region, PatternCategory::Opaque(*opaque), expected),
|
||||
);
|
||||
|
||||
state
|
||||
.vars
|
||||
.extend_from_slice(&[*arg_pattern_var, *whole_var]);
|
||||
// Also add the fresh variables we created for the type argument and lambda sets
|
||||
state.vars.extend(type_arguments.iter().map(|(_, t)| {
|
||||
t.expect_variable("all type arguments should be fresh variables here")
|
||||
}));
|
||||
state.vars.extend(lambda_set_variables.iter().map(|v| {
|
||||
v.0.expect_variable("all lambda sets should be fresh variables here")
|
||||
}));
|
||||
|
||||
state.constraints.extend_from_slice(&[
|
||||
whole_con,
|
||||
link_type_variables_con,
|
||||
opaque_pattern_con,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -367,9 +367,7 @@ fn preprocess_impl(
|
|||
Some(section) => {
|
||||
let file_offset = match section.compressed_file_range() {
|
||||
Ok(
|
||||
range
|
||||
@
|
||||
CompressedFileRange {
|
||||
range @ CompressedFileRange {
|
||||
format: CompressionFormat::None,
|
||||
..
|
||||
},
|
||||
|
@ -494,9 +492,7 @@ fn preprocess_impl(
|
|||
for sec in text_sections {
|
||||
let (file_offset, compressed) = match sec.compressed_file_range() {
|
||||
Ok(
|
||||
range
|
||||
@
|
||||
CompressedFileRange {
|
||||
range @ CompressedFileRange {
|
||||
format: CompressionFormat::None,
|
||||
..
|
||||
},
|
||||
|
@ -626,9 +622,7 @@ fn preprocess_impl(
|
|||
};
|
||||
let dyn_offset = match dyn_sec.compressed_file_range() {
|
||||
Ok(
|
||||
range
|
||||
@
|
||||
CompressedFileRange {
|
||||
range @ CompressedFileRange {
|
||||
format: CompressionFormat::None,
|
||||
..
|
||||
},
|
||||
|
@ -714,9 +708,7 @@ fn preprocess_impl(
|
|||
};
|
||||
let symtab_offset = match symtab_sec.compressed_file_range() {
|
||||
Ok(
|
||||
range
|
||||
@
|
||||
CompressedFileRange {
|
||||
range @ CompressedFileRange {
|
||||
format: CompressionFormat::None,
|
||||
..
|
||||
},
|
||||
|
@ -738,9 +730,7 @@ fn preprocess_impl(
|
|||
};
|
||||
let dynsym_offset = match dynsym_sec.compressed_file_range() {
|
||||
Ok(
|
||||
range
|
||||
@
|
||||
CompressedFileRange {
|
||||
range @ CompressedFileRange {
|
||||
format: CompressionFormat::None,
|
||||
..
|
||||
},
|
||||
|
@ -759,9 +749,7 @@ fn preprocess_impl(
|
|||
{
|
||||
match sec.compressed_file_range() {
|
||||
Ok(
|
||||
range
|
||||
@
|
||||
CompressedFileRange {
|
||||
range @ CompressedFileRange {
|
||||
format: CompressionFormat::None,
|
||||
..
|
||||
},
|
||||
|
|
|
@ -1384,7 +1384,7 @@ fn to_pattern_report<'b>(
|
|||
found,
|
||||
expected_type,
|
||||
add_pattern_category(
|
||||
HumanIndexlloc,
|
||||
HumanIndexlloc,
|
||||
alloc.text("The first pattern is trying to match"),
|
||||
&category,
|
||||
),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue