mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge remote-tracking branch 'origin/trunk' into type-checking-storage-subs
This commit is contained in:
commit
ed247c9da3
12 changed files with 250 additions and 104 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -564,6 +564,16 @@ dependencies = [
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_error_panic_hook"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.22"
|
version = "0.2.22"
|
||||||
|
@ -3773,6 +3783,7 @@ name = "roc_repl_wasm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"console_error_panic_hook",
|
||||||
"futures",
|
"futures",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::expected::{Expected, PExpected};
|
use crate::expected::{Expected, PExpected};
|
||||||
use roc_collections::soa::{Index, Slice};
|
use roc_collections::soa::{EitherIndex, Index, Slice};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
@ -120,14 +120,27 @@ impl Constraints {
|
||||||
pub const PCATEGORY_CHARACTER: Index<PatternCategory> = Index::new(10);
|
pub const PCATEGORY_CHARACTER: Index<PatternCategory> = Index::new(10);
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_type(&mut self, typ: Type) -> Index<Type> {
|
pub fn push_type(&mut self, typ: Type) -> EitherIndex<Type, Variable> {
|
||||||
match typ {
|
match typ {
|
||||||
Type::EmptyRec => Self::EMPTY_RECORD,
|
Type::EmptyRec => EitherIndex::from_left(Self::EMPTY_RECORD),
|
||||||
Type::EmptyTagUnion => Self::EMPTY_TAG_UNION,
|
Type::EmptyTagUnion => EitherIndex::from_left(Self::EMPTY_TAG_UNION),
|
||||||
other => Index::push_new(&mut self.types, other),
|
Type::Variable(var) => Self::push_type_variable(var),
|
||||||
|
other => {
|
||||||
|
let index: Index<Type> = Index::push_new(&mut self.types, other);
|
||||||
|
EitherIndex::from_left(index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
const fn push_type_variable(var: Variable) -> EitherIndex<Type, Variable> {
|
||||||
|
// that's right, we use the variable's integer value as the index
|
||||||
|
// that way, we don't need to push anything onto a vector
|
||||||
|
let index: Index<Variable> = Index::new(var.index());
|
||||||
|
|
||||||
|
EitherIndex::from_right(index)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_expected_type(&mut self, expected: Expected<Type>) -> Index<Expected<Type>> {
|
pub fn push_expected_type(&mut self, expected: Expected<Type>) -> Index<Expected<Type>> {
|
||||||
Index::push_new(&mut self.expectations, expected)
|
Index::push_new(&mut self.expectations, expected)
|
||||||
|
@ -180,7 +193,22 @@ impl Constraints {
|
||||||
category: Category,
|
category: Category,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let type_index = Index::push_new(&mut self.types, typ);
|
let type_index = self.push_type(typ);
|
||||||
|
let expected_index = Index::push_new(&mut self.expectations, expected);
|
||||||
|
let category_index = Self::push_category(self, category);
|
||||||
|
|
||||||
|
Constraint::Eq(type_index, expected_index, category_index, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn equal_types_var(
|
||||||
|
&mut self,
|
||||||
|
var: Variable,
|
||||||
|
expected: Expected<Type>,
|
||||||
|
category: Category,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint {
|
||||||
|
let type_index = Self::push_type_variable(var);
|
||||||
let expected_index = Index::push_new(&mut self.expectations, expected);
|
let expected_index = Index::push_new(&mut self.expectations, expected);
|
||||||
let category_index = Self::push_category(self, category);
|
let category_index = Self::push_category(self, category);
|
||||||
|
|
||||||
|
@ -194,7 +222,7 @@ impl Constraints {
|
||||||
category: PatternCategory,
|
category: PatternCategory,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let type_index = Index::push_new(&mut self.types, typ);
|
let type_index = self.push_type(typ);
|
||||||
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
||||||
let category_index = Self::push_pattern_category(self, category);
|
let category_index = Self::push_pattern_category(self, category);
|
||||||
|
|
||||||
|
@ -208,7 +236,7 @@ impl Constraints {
|
||||||
category: PatternCategory,
|
category: PatternCategory,
|
||||||
region: Region,
|
region: Region,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let type_index = Index::push_new(&mut self.types, typ);
|
let type_index = self.push_type(typ);
|
||||||
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
||||||
let category_index = Index::push_new(&mut self.pattern_categories, category);
|
let category_index = Index::push_new(&mut self.pattern_categories, category);
|
||||||
|
|
||||||
|
@ -216,7 +244,7 @@ impl Constraints {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_open_type(&mut self, typ: Type) -> Constraint {
|
pub fn is_open_type(&mut self, typ: Type) -> Constraint {
|
||||||
let type_index = Index::push_new(&mut self.types, typ);
|
let type_index = self.push_type(typ);
|
||||||
|
|
||||||
Constraint::IsOpenType(type_index)
|
Constraint::IsOpenType(type_index)
|
||||||
}
|
}
|
||||||
|
@ -495,7 +523,7 @@ impl Constraints {
|
||||||
filename: &'static str,
|
filename: &'static str,
|
||||||
line_number: u32,
|
line_number: u32,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let type_index = Index::push_new(&mut self.types, typ);
|
let type_index = self.push_type(typ);
|
||||||
let string_index = Index::push_new(&mut self.strings, filename);
|
let string_index = Index::push_new(&mut self.strings, filename);
|
||||||
|
|
||||||
Constraint::Store(type_index, variable, string_index, line_number)
|
Constraint::Store(type_index, variable, string_index, line_number)
|
||||||
|
@ -506,11 +534,21 @@ roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8);
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Constraint {
|
pub enum Constraint {
|
||||||
Eq(Index<Type>, Index<Expected<Type>>, Index<Category>, Region),
|
Eq(
|
||||||
Store(Index<Type>, Variable, Index<&'static str>, u32),
|
EitherIndex<Type, Variable>,
|
||||||
|
Index<Expected<Type>>,
|
||||||
|
Index<Category>,
|
||||||
|
Region,
|
||||||
|
),
|
||||||
|
Store(
|
||||||
|
EitherIndex<Type, Variable>,
|
||||||
|
Variable,
|
||||||
|
Index<&'static str>,
|
||||||
|
u32,
|
||||||
|
),
|
||||||
Lookup(Symbol, Index<Expected<Type>>, Region),
|
Lookup(Symbol, Index<Expected<Type>>, Region),
|
||||||
Pattern(
|
Pattern(
|
||||||
Index<Type>,
|
EitherIndex<Type, Variable>,
|
||||||
Index<PExpected<Type>>,
|
Index<PExpected<Type>>,
|
||||||
Index<PatternCategory>,
|
Index<PatternCategory>,
|
||||||
Region,
|
Region,
|
||||||
|
@ -526,10 +564,10 @@ pub enum Constraint {
|
||||||
Let(Index<LetConstraint>, Slice<Variable>),
|
Let(Index<LetConstraint>, Slice<Variable>),
|
||||||
And(Slice<Constraint>),
|
And(Slice<Constraint>),
|
||||||
/// Presence constraints
|
/// Presence constraints
|
||||||
IsOpenType(Index<Type>), // Theory; always applied to a variable? if yes the use that
|
IsOpenType(EitherIndex<Type, Variable>), // Theory; always applied to a variable? if yes the use that
|
||||||
IncludesTag(Index<IncludesTag>),
|
IncludesTag(Index<IncludesTag>),
|
||||||
PatternPresence(
|
PatternPresence(
|
||||||
Index<Type>,
|
EitherIndex<Type, Variable>,
|
||||||
Index<PExpected<Type>>,
|
Index<PExpected<Type>>,
|
||||||
Index<PatternCategory>,
|
Index<PatternCategory>,
|
||||||
Region,
|
Region,
|
||||||
|
|
|
@ -117,3 +117,56 @@ impl<T> Slice<T> {
|
||||||
self.indices().map(|i| Index::new(i as _))
|
self.indices().map(|i| Index::new(i as _))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub struct EitherIndex<T, U> {
|
||||||
|
index: u32,
|
||||||
|
_marker: std::marker::PhantomData<(T, U)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> Clone for EitherIndex<T, U> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
index: self.index,
|
||||||
|
_marker: self._marker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> Copy for EitherIndex<T, U> {}
|
||||||
|
|
||||||
|
impl<T, U> std::fmt::Debug for EitherIndex<T, U> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Index({})", self.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> EitherIndex<T, U> {
|
||||||
|
const MASK: u32 = 1 << 31;
|
||||||
|
|
||||||
|
pub const fn from_left(input: Index<T>) -> Self {
|
||||||
|
assert!(input.index & Self::MASK == 0);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
index: input.index,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn from_right(input: Index<U>) -> Self {
|
||||||
|
assert!(input.index & Self::MASK == 0);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
index: input.index | Self::MASK,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn split(self) -> Result<Index<T>, Index<U>> {
|
||||||
|
if self.index & Self::MASK == 0 {
|
||||||
|
Ok(Index::new(self.index))
|
||||||
|
} else {
|
||||||
|
Err(Index::new(self.index ^ Self::MASK))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -136,8 +136,8 @@ pub fn constrain_expr(
|
||||||
rec_constraints.push(record_con);
|
rec_constraints.push(record_con);
|
||||||
|
|
||||||
// variable to store in the AST
|
// variable to store in the AST
|
||||||
let stored_con = constraints.equal_types(
|
let stored_con = constraints.equal_types_var(
|
||||||
Type::Variable(*record_var),
|
*record_var,
|
||||||
expected,
|
expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -177,14 +177,14 @@ pub fn constrain_expr(
|
||||||
let record_type = Type::Variable(*record_var);
|
let record_type = Type::Variable(*record_var);
|
||||||
|
|
||||||
// NOTE from elm compiler: fields_type is separate so that Error propagates better
|
// NOTE from elm compiler: fields_type is separate so that Error propagates better
|
||||||
let fields_con = constraints.equal_types(
|
let fields_con = constraints.equal_types_var(
|
||||||
record_type.clone(),
|
*record_var,
|
||||||
NoExpectation(fields_type),
|
NoExpectation(fields_type),
|
||||||
Category::Record,
|
Category::Record,
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
let record_con =
|
let record_con =
|
||||||
constraints.equal_types(record_type.clone(), expected, Category::Record, region);
|
constraints.equal_types_var(*record_var, expected, Category::Record, region);
|
||||||
|
|
||||||
vars.push(*record_var);
|
vars.push(*record_var);
|
||||||
vars.push(*ext_var);
|
vars.push(*ext_var);
|
||||||
|
@ -273,7 +273,7 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let fn_type = Variable(*fn_var);
|
let fn_type = Variable(*fn_var);
|
||||||
let fn_region = loc_fn.region;
|
let fn_region = loc_fn.region;
|
||||||
let fn_expected = NoExpectation(fn_type.clone());
|
let fn_expected = NoExpectation(fn_type);
|
||||||
|
|
||||||
let fn_reason = Reason::FnCall {
|
let fn_reason = Reason::FnCall {
|
||||||
name: opt_symbol,
|
name: opt_symbol,
|
||||||
|
@ -323,11 +323,7 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let expected_fn_type = ForReason(
|
let expected_fn_type = ForReason(
|
||||||
fn_reason,
|
fn_reason,
|
||||||
Function(
|
Function(arg_types, Box::new(closure_type), Box::new(ret_type)),
|
||||||
arg_types,
|
|
||||||
Box::new(closure_type),
|
|
||||||
Box::new(ret_type.clone()),
|
|
||||||
),
|
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -335,9 +331,9 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let and_cons = [
|
let and_cons = [
|
||||||
fn_con,
|
fn_con,
|
||||||
constraints.equal_types(fn_type, expected_fn_type, category.clone(), fn_region),
|
constraints.equal_types_var(*fn_var, expected_fn_type, category.clone(), fn_region),
|
||||||
constraints.and_constraint(arg_cons),
|
constraints.and_constraint(arg_cons),
|
||||||
constraints.equal_types(ret_type, expected, category, region),
|
constraints.equal_types_var(*ret_var, expected, category, region),
|
||||||
];
|
];
|
||||||
|
|
||||||
let and_constraint = constraints.and_constraint(and_cons);
|
let and_constraint = constraints.and_constraint(and_cons);
|
||||||
|
@ -418,8 +414,8 @@ pub fn constrain_expr(
|
||||||
// "the closure's type is equal to expected type"
|
// "the closure's type is equal to expected type"
|
||||||
constraints.equal_types(function_type.clone(), expected, Category::Lambda, region),
|
constraints.equal_types(function_type.clone(), 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
|
||||||
constraints.equal_types(
|
constraints.equal_types_var(
|
||||||
Type::Variable(*fn_var),
|
*fn_var,
|
||||||
NoExpectation(function_type),
|
NoExpectation(function_type),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -469,8 +465,8 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
// TODO why does this cond var exist? is it for error messages?
|
// TODO why does this cond var exist? is it for error messages?
|
||||||
let first_cond_region = branches[0].0.region;
|
let first_cond_region = branches[0].0.region;
|
||||||
let cond_var_is_bool_con = constraints.equal_types(
|
let cond_var_is_bool_con = constraints.equal_types_var(
|
||||||
Type::Variable(*cond_var),
|
*cond_var,
|
||||||
expect_bool(first_cond_region),
|
expect_bool(first_cond_region),
|
||||||
Category::If,
|
Category::If,
|
||||||
first_cond_region,
|
first_cond_region,
|
||||||
|
@ -528,8 +524,8 @@ pub fn constrain_expr(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast_con = constraints.equal_types(
|
let ast_con = constraints.equal_types_var(
|
||||||
Type::Variable(*branch_var),
|
*branch_var,
|
||||||
NoExpectation(tipe),
|
NoExpectation(tipe),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -583,8 +579,8 @@ pub fn constrain_expr(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
branch_cons.push(constraints.equal_types(
|
branch_cons.push(constraints.equal_types_var(
|
||||||
Type::Variable(*branch_var),
|
*branch_var,
|
||||||
expected,
|
expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -654,8 +650,8 @@ pub fn constrain_expr(
|
||||||
branch_constraints.push(branch_con);
|
branch_constraints.push(branch_con);
|
||||||
}
|
}
|
||||||
|
|
||||||
branch_constraints.push(constraints.equal_types(
|
branch_constraints.push(constraints.equal_types_var(
|
||||||
typ,
|
*expr_var,
|
||||||
expected,
|
expected,
|
||||||
Category::When,
|
Category::When,
|
||||||
region,
|
region,
|
||||||
|
@ -665,7 +661,8 @@ pub fn constrain_expr(
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let branch_type = Variable(*expr_var);
|
let branch_var = *expr_var;
|
||||||
|
let branch_type = Variable(branch_var);
|
||||||
let mut branch_cons = Vec::with_capacity(branches.len());
|
let mut branch_cons = Vec::with_capacity(branches.len());
|
||||||
|
|
||||||
for (index, when_branch) in branches.iter().enumerate() {
|
for (index, when_branch) in branches.iter().enumerate() {
|
||||||
|
@ -703,8 +700,8 @@ pub fn constrain_expr(
|
||||||
//
|
//
|
||||||
// The return type of each branch must equal the return type of
|
// The return type of each branch must equal the return type of
|
||||||
// the entire when-expression.
|
// the entire when-expression.
|
||||||
branch_cons.push(constraints.equal_types(
|
branch_cons.push(constraints.equal_types_var(
|
||||||
branch_type,
|
branch_var,
|
||||||
expected,
|
expected,
|
||||||
Category::When,
|
Category::When,
|
||||||
region,
|
region,
|
||||||
|
@ -731,15 +728,15 @@ pub fn constrain_expr(
|
||||||
let mut rec_field_types = SendMap::default();
|
let mut rec_field_types = SendMap::default();
|
||||||
|
|
||||||
let label = field.clone();
|
let label = field.clone();
|
||||||
rec_field_types.insert(label, RecordField::Demanded(field_type.clone()));
|
rec_field_types.insert(label, RecordField::Demanded(field_type));
|
||||||
|
|
||||||
let record_type = Type::Record(rec_field_types, Box::new(ext_type));
|
let record_type = Type::Record(rec_field_types, Box::new(ext_type));
|
||||||
let record_expected = Expected::NoExpectation(record_type);
|
let record_expected = Expected::NoExpectation(record_type);
|
||||||
|
|
||||||
let category = Category::Access(field.clone());
|
let category = Category::Access(field.clone());
|
||||||
|
|
||||||
let record_con = constraints.equal_types(
|
let record_con = constraints.equal_types_var(
|
||||||
Type::Variable(*record_var),
|
*record_var,
|
||||||
record_expected.clone(),
|
record_expected.clone(),
|
||||||
category.clone(),
|
category.clone(),
|
||||||
region,
|
region,
|
||||||
|
@ -756,7 +753,7 @@ pub fn constrain_expr(
|
||||||
record_expected,
|
record_expected,
|
||||||
);
|
);
|
||||||
|
|
||||||
let eq = constraints.equal_types(field_type, expected, category, region);
|
let eq = constraints.equal_types_var(field_var, expected, category, region);
|
||||||
constraints.exists_many(
|
constraints.exists_many(
|
||||||
[*record_var, field_var, ext_var],
|
[*record_var, field_var, ext_var],
|
||||||
[constraint, eq, record_con],
|
[constraint, eq, record_con],
|
||||||
|
@ -785,12 +782,8 @@ pub fn constrain_expr(
|
||||||
let category = Category::Accessor(field.clone());
|
let category = Category::Accessor(field.clone());
|
||||||
|
|
||||||
let record_expected = Expected::NoExpectation(record_type.clone());
|
let record_expected = Expected::NoExpectation(record_type.clone());
|
||||||
let record_con = constraints.equal_types(
|
let record_con =
|
||||||
Type::Variable(*record_var),
|
constraints.equal_types_var(*record_var, record_expected, category.clone(), region);
|
||||||
record_expected,
|
|
||||||
category.clone(),
|
|
||||||
region,
|
|
||||||
);
|
|
||||||
|
|
||||||
let lambda_set = Type::ClosureTag {
|
let lambda_set = Type::ClosureTag {
|
||||||
name: *closure_name,
|
name: *closure_name,
|
||||||
|
@ -801,13 +794,13 @@ pub fn constrain_expr(
|
||||||
|
|
||||||
let function_type = Type::Function(
|
let function_type = Type::Function(
|
||||||
vec![record_type],
|
vec![record_type],
|
||||||
Box::new(closure_type.clone()),
|
Box::new(closure_type),
|
||||||
Box::new(field_type),
|
Box::new(field_type),
|
||||||
);
|
);
|
||||||
|
|
||||||
let cons = [
|
let cons = [
|
||||||
constraints.equal_types(
|
constraints.equal_types_var(
|
||||||
closure_type,
|
*closure_var,
|
||||||
NoExpectation(lambda_set),
|
NoExpectation(lambda_set),
|
||||||
category.clone(),
|
category.clone(),
|
||||||
region,
|
region,
|
||||||
|
@ -847,8 +840,8 @@ pub fn constrain_expr(
|
||||||
constrain_recursive_defs(constraints, env, defs, body_con),
|
constrain_recursive_defs(constraints, env, defs, body_con),
|
||||||
// Record the type of tne entire def-expression in the variable.
|
// Record the type of tne entire def-expression in the variable.
|
||||||
// Code gen will need that later!
|
// Code gen will need that later!
|
||||||
constraints.equal_types(
|
constraints.equal_types_var(
|
||||||
Type::Variable(*var),
|
*var,
|
||||||
expected,
|
expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
loc_ret.region,
|
loc_ret.region,
|
||||||
|
@ -882,8 +875,8 @@ pub fn constrain_expr(
|
||||||
constrain_def(constraints, env, def, body_con),
|
constrain_def(constraints, env, def, body_con),
|
||||||
// Record the type of the entire def-expression in the variable.
|
// Record the type of the entire def-expression in the variable.
|
||||||
// Code gen will need that later!
|
// Code gen will need that later!
|
||||||
constraints.equal_types(
|
constraints.equal_types_var(
|
||||||
Type::Variable(*var),
|
*var,
|
||||||
expected.clone(),
|
expected.clone(),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
ret_region,
|
ret_region,
|
||||||
|
@ -931,8 +924,8 @@ pub fn constrain_expr(
|
||||||
},
|
},
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
let ast_con = constraints.equal_types(
|
let ast_con = constraints.equal_types_var(
|
||||||
Type::Variable(*variant_var),
|
*variant_var,
|
||||||
expected,
|
expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -983,8 +976,8 @@ pub fn constrain_expr(
|
||||||
},
|
},
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
let ast_con = constraints.equal_types(
|
let ast_con = constraints.equal_types_var(
|
||||||
Type::Variable(*variant_var),
|
*variant_var,
|
||||||
expected,
|
expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -1046,8 +1039,8 @@ pub fn constrain_expr(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Store the entire wrapped opaque type in `opaque_var`
|
// Store the entire wrapped opaque type in `opaque_var`
|
||||||
let storage_con = constraints.equal_types(
|
let storage_con = constraints.equal_types_var(
|
||||||
Type::Variable(*opaque_var),
|
*opaque_var,
|
||||||
expected,
|
expected,
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
|
@ -1071,9 +1064,6 @@ pub fn constrain_expr(
|
||||||
RunLowLevel { args, ret_var, op } => {
|
RunLowLevel { args, ret_var, op } => {
|
||||||
// This is a modified version of what we do for function calls.
|
// This is a modified version of what we do for function calls.
|
||||||
|
|
||||||
// The operation's return type
|
|
||||||
let ret_type = Variable(*ret_var);
|
|
||||||
|
|
||||||
// This will be used in the occurs check
|
// This will be used in the occurs check
|
||||||
let mut vars = Vec::with_capacity(1 + args.len());
|
let mut vars = Vec::with_capacity(1 + args.len());
|
||||||
|
|
||||||
|
@ -1103,7 +1093,7 @@ pub fn constrain_expr(
|
||||||
let category = Category::LowLevelOpResult(*op);
|
let category = Category::LowLevelOpResult(*op);
|
||||||
|
|
||||||
// Deviation: elm uses an additional And here
|
// Deviation: elm uses an additional And here
|
||||||
let eq = constraints.equal_types(ret_type, expected, category, region);
|
let eq = constraints.equal_types_var(*ret_var, expected, category, region);
|
||||||
arg_cons.push(eq);
|
arg_cons.push(eq);
|
||||||
constraints.exists_many(vars, arg_cons)
|
constraints.exists_many(vars, arg_cons)
|
||||||
}
|
}
|
||||||
|
@ -1114,9 +1104,6 @@ pub fn constrain_expr(
|
||||||
} => {
|
} => {
|
||||||
// This is a modified version of what we do for function calls.
|
// This is a modified version of what we do for function calls.
|
||||||
|
|
||||||
// The operation's return type
|
|
||||||
let ret_type = Variable(*ret_var);
|
|
||||||
|
|
||||||
// This will be used in the occurs check
|
// This will be used in the occurs check
|
||||||
let mut vars = Vec::with_capacity(1 + args.len());
|
let mut vars = Vec::with_capacity(1 + args.len());
|
||||||
|
|
||||||
|
@ -1146,7 +1133,7 @@ pub fn constrain_expr(
|
||||||
let category = Category::ForeignCall;
|
let category = Category::ForeignCall;
|
||||||
|
|
||||||
// Deviation: elm uses an additional And here
|
// Deviation: elm uses an additional And here
|
||||||
let eq = constraints.equal_types(ret_type, expected, category, region);
|
let eq = constraints.equal_types_var(*ret_var, expected, category, region);
|
||||||
arg_cons.push(eq);
|
arg_cons.push(eq);
|
||||||
constraints.exists_many(vars, arg_cons)
|
constraints.exists_many(vars, arg_cons)
|
||||||
}
|
}
|
||||||
|
@ -1248,14 +1235,7 @@ fn constrain_empty_record(
|
||||||
region: Region,
|
region: Region,
|
||||||
expected: Expected<Type>,
|
expected: Expected<Type>,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let expected_index = constraints.push_expected_type(expected);
|
constraints.equal_types(Type::EmptyRec, expected, Category::Record, region)
|
||||||
|
|
||||||
Constraint::Eq(
|
|
||||||
Constraints::EMPTY_RECORD,
|
|
||||||
expected_index,
|
|
||||||
Constraints::CATEGORY_RECORD,
|
|
||||||
region,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constrain top-level module declarations
|
/// Constrain top-level module declarations
|
||||||
|
@ -1455,8 +1435,8 @@ fn constrain_def(
|
||||||
def_pattern_state.vars.push(*pattern_var);
|
def_pattern_state.vars.push(*pattern_var);
|
||||||
pattern_types.push(Type::Variable(*pattern_var));
|
pattern_types.push(Type::Variable(*pattern_var));
|
||||||
|
|
||||||
let pattern_con = constraints.equal_types(
|
let pattern_con = constraints.equal_types_var(
|
||||||
Type::Variable(*pattern_var),
|
*pattern_var,
|
||||||
Expected::NoExpectation(loc_ann.clone()),
|
Expected::NoExpectation(loc_ann.clone()),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
@ -1504,8 +1484,8 @@ fn constrain_def(
|
||||||
defs_constraint,
|
defs_constraint,
|
||||||
ret_constraint,
|
ret_constraint,
|
||||||
),
|
),
|
||||||
constraints.equal_types(
|
constraints.equal_types_var(
|
||||||
Type::Variable(closure_var),
|
closure_var,
|
||||||
Expected::FromAnnotation(
|
Expected::FromAnnotation(
|
||||||
def.loc_pattern.clone(),
|
def.loc_pattern.clone(),
|
||||||
arity,
|
arity,
|
||||||
|
@ -1665,8 +1645,8 @@ fn constrain_closure_size(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let finalizer = constraints.equal_types(
|
let finalizer = constraints.equal_types_var(
|
||||||
Type::Variable(closure_var),
|
closure_var,
|
||||||
NoExpectation(closure_type),
|
NoExpectation(closure_type),
|
||||||
Category::ClosureSize,
|
Category::ClosureSize,
|
||||||
region,
|
region,
|
||||||
|
@ -1908,8 +1888,8 @@ pub fn rec_defs_help(
|
||||||
def_pattern_state.vars.push(*pattern_var);
|
def_pattern_state.vars.push(*pattern_var);
|
||||||
pattern_types.push(Type::Variable(*pattern_var));
|
pattern_types.push(Type::Variable(*pattern_var));
|
||||||
|
|
||||||
let pattern_con = constraints.equal_types(
|
let pattern_con = constraints.equal_types_var(
|
||||||
Type::Variable(*pattern_var),
|
*pattern_var,
|
||||||
Expected::NoExpectation(loc_ann.clone()),
|
Expected::NoExpectation(loc_ann.clone()),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
loc_pattern.region,
|
loc_pattern.region,
|
||||||
|
|
|
@ -936,11 +936,13 @@ impl<'a> WasmBackend<'a> {
|
||||||
}
|
}
|
||||||
_ => internal_error!("Cannot create struct {:?} with storage {:?}", sym, storage),
|
_ => internal_error!("Cannot create struct {:?} with storage {:?}", sym, storage),
|
||||||
};
|
};
|
||||||
} else {
|
} else if !fields.is_empty() {
|
||||||
// Struct expression but not Struct layout => single element. Copy it.
|
// Struct expression but not Struct layout => single element. Copy it.
|
||||||
let field_storage = self.storage.get(&fields[0]).to_owned();
|
let field_storage = self.storage.get(&fields[0]).to_owned();
|
||||||
self.storage
|
self.storage
|
||||||
.clone_value(&mut self.code_builder, storage, &field_storage, fields[0]);
|
.clone_value(&mut self.code_builder, storage, &field_storage, fields[0]);
|
||||||
|
} else {
|
||||||
|
// Empty record. Nothing to do.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -435,11 +435,18 @@ fn solve(
|
||||||
copy
|
copy
|
||||||
}
|
}
|
||||||
Eq(type_index, expectation_index, category_index, region) => {
|
Eq(type_index, expectation_index, category_index, region) => {
|
||||||
let typ = &constraints.types[type_index.index()];
|
|
||||||
let expectation = &constraints.expectations[expectation_index.index()];
|
|
||||||
let category = &constraints.categories[category_index.index()];
|
let category = &constraints.categories[category_index.index()];
|
||||||
|
|
||||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
let actual = either_type_index_to_var(
|
||||||
|
constraints,
|
||||||
|
subs,
|
||||||
|
rank,
|
||||||
|
pools,
|
||||||
|
cached_aliases,
|
||||||
|
*type_index,
|
||||||
|
);
|
||||||
|
|
||||||
|
let expectation = &constraints.expectations[expectation_index.index()];
|
||||||
let expected = type_to_var(
|
let expected = type_to_var(
|
||||||
subs,
|
subs,
|
||||||
rank,
|
rank,
|
||||||
|
@ -478,11 +485,16 @@ fn solve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Store(source_index, target, _filename, _linenr) => {
|
Store(source_index, target, _filename, _linenr) => {
|
||||||
let source = &constraints.types[source_index.index()];
|
|
||||||
|
|
||||||
// a special version of Eq that is used to store types in the AST.
|
// a special version of Eq that is used to store types in the AST.
|
||||||
// IT DOES NOT REPORT ERRORS!
|
// IT DOES NOT REPORT ERRORS!
|
||||||
let actual = type_to_var(subs, rank, pools, cached_aliases, source);
|
let actual = either_type_index_to_var(
|
||||||
|
constraints,
|
||||||
|
subs,
|
||||||
|
rank,
|
||||||
|
pools,
|
||||||
|
cached_aliases,
|
||||||
|
*source_index,
|
||||||
|
);
|
||||||
let target = *target;
|
let target = *target;
|
||||||
|
|
||||||
match unify(subs, actual, target, Mode::EQ) {
|
match unify(subs, actual, target, Mode::EQ) {
|
||||||
|
@ -593,11 +605,18 @@ fn solve(
|
||||||
}
|
}
|
||||||
Pattern(type_index, expectation_index, category_index, region)
|
Pattern(type_index, expectation_index, category_index, region)
|
||||||
| PatternPresence(type_index, expectation_index, category_index, region) => {
|
| PatternPresence(type_index, expectation_index, category_index, region) => {
|
||||||
let typ = &constraints.types[type_index.index()];
|
|
||||||
let expectation = &constraints.pattern_expectations[expectation_index.index()];
|
|
||||||
let category = &constraints.pattern_categories[category_index.index()];
|
let category = &constraints.pattern_categories[category_index.index()];
|
||||||
|
|
||||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
let actual = either_type_index_to_var(
|
||||||
|
constraints,
|
||||||
|
subs,
|
||||||
|
rank,
|
||||||
|
pools,
|
||||||
|
cached_aliases,
|
||||||
|
*type_index,
|
||||||
|
);
|
||||||
|
|
||||||
|
let expectation = &constraints.pattern_expectations[expectation_index.index()];
|
||||||
let expected = type_to_var(
|
let expected = type_to_var(
|
||||||
subs,
|
subs,
|
||||||
rank,
|
rank,
|
||||||
|
@ -735,9 +754,15 @@ fn solve(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IsOpenType(type_index) => {
|
IsOpenType(type_index) => {
|
||||||
let typ = &constraints.types[type_index.index()];
|
let actual = either_type_index_to_var(
|
||||||
|
constraints,
|
||||||
|
subs,
|
||||||
|
rank,
|
||||||
|
pools,
|
||||||
|
cached_aliases,
|
||||||
|
*type_index,
|
||||||
|
);
|
||||||
|
|
||||||
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
|
|
||||||
let mut new_desc = subs.get(actual);
|
let mut new_desc = subs.get(actual);
|
||||||
match new_desc.content {
|
match new_desc.content {
|
||||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||||
|
@ -885,6 +910,27 @@ fn put_scratchpad(scratchpad: bumpalo::Bump) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn either_type_index_to_var(
|
||||||
|
constraints: &Constraints,
|
||||||
|
subs: &mut Subs,
|
||||||
|
rank: Rank,
|
||||||
|
pools: &mut Pools,
|
||||||
|
_alias_map: &mut MutMap<Symbol, Variable>,
|
||||||
|
either_type_index: roc_collections::soa::EitherIndex<Type, Variable>,
|
||||||
|
) -> Variable {
|
||||||
|
match either_type_index.split() {
|
||||||
|
Ok(type_index) => {
|
||||||
|
let typ = &constraints.types[type_index.index()];
|
||||||
|
|
||||||
|
type_to_var(subs, rank, pools, _alias_map, typ)
|
||||||
|
}
|
||||||
|
Err(var_index) => {
|
||||||
|
// we cheat, and store the variable directly in the index
|
||||||
|
unsafe { Variable::from_index(var_index.index() as _) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn type_to_var(
|
fn type_to_var(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
|
|
|
@ -4,7 +4,7 @@ set -eux
|
||||||
# We *could* write a build.rs to do this but we'd have nested cargo processes with different targets.
|
# We *could* write a build.rs to do this but we'd have nested cargo processes with different targets.
|
||||||
# That can be solved using two separate target direcories with --target-dir but there isn't a huge win.
|
# That can be solved using two separate target direcories with --target-dir but there isn't a huge win.
|
||||||
# We need to clear RUSTFLAGS for this command, as CI sets normally some flags that are specific to CPU targets.
|
# We need to clear RUSTFLAGS for this command, as CI sets normally some flags that are specific to CPU targets.
|
||||||
RUSTFLAGS="" cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --features wasmer --release
|
RUSTFLAGS="" cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --no-default-features --features wasmer --release
|
||||||
|
|
||||||
# Build & run the test code on *native* target, not WebAssembly
|
# Build & run the test code on *native* target, not WebAssembly
|
||||||
cargo test -p repl_test --features wasm -- --test-threads=1
|
cargo test -p repl_test --features wasm -- --test-threads=1
|
||||||
|
|
|
@ -10,8 +10,9 @@ crate-type = ["cdylib"]
|
||||||
roc_builtins = {path = "../compiler/builtins"}
|
roc_builtins = {path = "../compiler/builtins"}
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = {version = "3.8.0", features = ["collections"]}
|
||||||
futures = { version = "0.3.17", optional = true }
|
console_error_panic_hook = {version = "0.1.7", optional = true}
|
||||||
|
futures = {version = "0.3.17", optional = true}
|
||||||
js-sys = "0.3.56"
|
js-sys = "0.3.56"
|
||||||
wasm-bindgen = "0.2.79"
|
wasm-bindgen = "0.2.79"
|
||||||
wasm-bindgen-futures = "0.4.29"
|
wasm-bindgen-futures = "0.4.29"
|
||||||
|
@ -25,4 +26,8 @@ roc_target = {path = "../compiler/roc_target"}
|
||||||
roc_types = {path = "../compiler/types"}
|
roc_types = {path = "../compiler/types"}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["console_error_panic_hook"]
|
||||||
wasmer = ["futures"]
|
wasmer = ["futures"]
|
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.profiling]
|
||||||
|
wasm-opt = ['-Os', '-g'] # `wasm-pack --profiling` is supposed to have debug info `-g` by default, but doesn't
|
||||||
|
|
|
@ -4,6 +4,8 @@ mod repl;
|
||||||
// Interface with external JS in the browser
|
// Interface with external JS in the browser
|
||||||
//
|
//
|
||||||
#[cfg(not(feature = "wasmer"))]
|
#[cfg(not(feature = "wasmer"))]
|
||||||
|
extern crate console_error_panic_hook;
|
||||||
|
#[cfg(not(feature = "wasmer"))]
|
||||||
mod externs_js;
|
mod externs_js;
|
||||||
#[cfg(not(feature = "wasmer"))]
|
#[cfg(not(feature = "wasmer"))]
|
||||||
pub use externs_js::{entrypoint_from_js, js_create_app, js_get_result_and_memory, js_run_app};
|
pub use externs_js::{entrypoint_from_js, js_create_app, js_get_result_and_memory, js_run_app};
|
||||||
|
|
|
@ -155,6 +155,9 @@ impl<'a> ReplApp<'a> for WasmReplApp<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
|
pub async fn entrypoint_from_js(src: String) -> Result<String, String> {
|
||||||
|
#[cfg(not(feature = "wasmer"))]
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
let arena = &Bump::new();
|
let arena = &Bump::new();
|
||||||
let pre_linked_binary: &'static [u8] = include_bytes!("../data/pre_linked_binary.o");
|
let pre_linked_binary: &'static [u8] = include_bytes!("../data/pre_linked_binary.o");
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ then
|
||||||
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --release
|
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --release
|
||||||
wasm-bindgen --target web --keep-debug target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm --out-dir repl_wasm/pkg/
|
wasm-bindgen --target web --keep-debug target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm --out-dir repl_wasm/pkg/
|
||||||
else
|
else
|
||||||
wasm-pack build --target web repl_wasm
|
# A `--profiling` build is optimized and has debug info, so we get stack traces for compiler `todo!()`
|
||||||
|
wasm-pack build --profiling --target web repl_wasm
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp repl_wasm/pkg/*.wasm $WWW_ROOT
|
cp repl_wasm/pkg/*.wasm $WWW_ROOT
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
console.error = function monkeyPatchedConsoleErrorPleaseForgiveMe(...args) {
|
||||||
|
const message = args.join("\n");
|
||||||
|
updateHistoryEntry(repl.inputHistoryIndex, false, message);
|
||||||
|
};
|
||||||
|
|
||||||
// wasm_bindgen treats our `extern` declarations as JS globals, so let's keep it happy
|
// wasm_bindgen treats our `extern` declarations as JS globals, so let's keep it happy
|
||||||
window.js_create_app = js_create_app;
|
window.js_create_app = js_create_app;
|
||||||
window.js_run_app = js_run_app;
|
window.js_run_app = js_run_app;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue