mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge pull request #2259 from rtfeldman/i/2227-record-layout-hang
Turn invalid record field types into runtime errors
This commit is contained in:
commit
db44d03e66
10 changed files with 162 additions and 65 deletions
|
@ -765,7 +765,8 @@ fn type_to_variable<'a>(
|
||||||
let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext);
|
let temp_ext_var = type_to_variable(arena, mempool, subs, rank, pools, cached, ext);
|
||||||
|
|
||||||
let (it, new_ext_var) =
|
let (it, new_ext_var) =
|
||||||
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var)
|
||||||
|
.expect("Something ended up weird in this record type");
|
||||||
|
|
||||||
let it = it
|
let it = it
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -3325,8 +3325,15 @@ pub fn with_hole<'a>(
|
||||||
mut fields,
|
mut fields,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let sorted_fields =
|
let sorted_fields = match crate::layout::sort_record_fields(
|
||||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
env.arena,
|
||||||
|
record_var,
|
||||||
|
env.subs,
|
||||||
|
env.ptr_bytes,
|
||||||
|
) {
|
||||||
|
Ok(fields) => fields,
|
||||||
|
Err(_) => return Stmt::RuntimeError("Can't create record with improper layout"),
|
||||||
|
};
|
||||||
|
|
||||||
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||||
|
@ -3678,8 +3685,15 @@ pub fn with_hole<'a>(
|
||||||
loc_expr,
|
loc_expr,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let sorted_fields =
|
let sorted_fields = match crate::layout::sort_record_fields(
|
||||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
env.arena,
|
||||||
|
record_var,
|
||||||
|
env.subs,
|
||||||
|
env.ptr_bytes,
|
||||||
|
) {
|
||||||
|
Ok(fields) => fields,
|
||||||
|
Err(_) => return Stmt::RuntimeError("Can't access record with improper layout"),
|
||||||
|
};
|
||||||
|
|
||||||
let mut index = None;
|
let mut index = None;
|
||||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
@ -3821,8 +3835,15 @@ pub fn with_hole<'a>(
|
||||||
// This has the benefit that we don't need to do anything special for reference
|
// This has the benefit that we don't need to do anything special for reference
|
||||||
// counting
|
// counting
|
||||||
|
|
||||||
let sorted_fields =
|
let sorted_fields = match crate::layout::sort_record_fields(
|
||||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
env.arena,
|
||||||
|
record_var,
|
||||||
|
env.subs,
|
||||||
|
env.ptr_bytes,
|
||||||
|
) {
|
||||||
|
Ok(fields) => fields,
|
||||||
|
Err(_) => return Stmt::RuntimeError("Can't update record with improper layout"),
|
||||||
|
};
|
||||||
|
|
||||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
|
||||||
|
@ -4391,11 +4412,7 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RuntimeError(e) => {
|
RuntimeError(e) => Stmt::RuntimeError(env.arena.alloc(format!("{:?}", e))),
|
||||||
eprintln!("emitted runtime error {:?}", &e);
|
|
||||||
|
|
||||||
Stmt::RuntimeError(env.arena.alloc(format!("{:?}", e)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7383,7 +7400,8 @@ fn from_can_pattern_help<'a>(
|
||||||
use crate::layout::UnionVariant::*;
|
use crate::layout::UnionVariant::*;
|
||||||
|
|
||||||
let res_variant =
|
let res_variant =
|
||||||
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes);
|
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes)
|
||||||
|
.map_err(Into::into);
|
||||||
|
|
||||||
let variant = match res_variant {
|
let variant = match res_variant {
|
||||||
Ok(cached) => cached,
|
Ok(cached) => cached,
|
||||||
|
@ -7803,7 +7821,8 @@ fn from_can_pattern_help<'a>(
|
||||||
} => {
|
} => {
|
||||||
// sorted fields based on the type
|
// sorted fields based on the type
|
||||||
let sorted_fields =
|
let sorted_fields =
|
||||||
crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes);
|
crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes)
|
||||||
|
.map_err(RuntimeError::from)?;
|
||||||
|
|
||||||
// sorted fields based on the destruct
|
// sorted fields based on the destruct
|
||||||
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::all::{default_hasher, MutMap};
|
use roc_collections::all::{default_hasher, MutMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
|
use roc_problem::can::RuntimeError;
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{
|
||||||
Content, FlatType, RecordFields, Subs, UnionTags, Variable, VariableSubsSlice,
|
Content, FlatType, RecordFields, Subs, UnionTags, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
|
@ -31,6 +32,15 @@ pub enum LayoutProblem {
|
||||||
Erroneous,
|
Erroneous,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<LayoutProblem> for RuntimeError {
|
||||||
|
fn from(lp: LayoutProblem) -> Self {
|
||||||
|
match lp {
|
||||||
|
LayoutProblem::UnresolvedTypeVar(_) => RuntimeError::UnresolvedTypeVar,
|
||||||
|
LayoutProblem::Erroneous => RuntimeError::ErroneousType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum RawFunctionLayout<'a> {
|
pub enum RawFunctionLayout<'a> {
|
||||||
Function(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>),
|
Function(&'a [Layout<'a>], LambdaSet<'a>, &'a Layout<'a>),
|
||||||
|
@ -1495,23 +1505,17 @@ fn layout_from_flat_type<'a>(
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
// extract any values from the ext_var
|
// extract any values from the ext_var
|
||||||
|
|
||||||
let pairs_it = fields
|
let mut pairs = Vec::with_capacity_in(fields.len(), arena);
|
||||||
.unsorted_iterator(subs, ext_var)
|
for (label, field) in fields.unsorted_iterator(subs, ext_var) {
|
||||||
.filter_map(|(label, field)| {
|
// drop optional fields
|
||||||
// drop optional fields
|
let var = match field {
|
||||||
let var = match field {
|
RecordField::Optional(_) => continue,
|
||||||
RecordField::Optional(_) => return None,
|
RecordField::Required(var) => var,
|
||||||
RecordField::Required(var) => var,
|
RecordField::Demanded(var) => var,
|
||||||
RecordField::Demanded(var) => var,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
Some((
|
pairs.push((label, Layout::from_var(env, var)?));
|
||||||
label,
|
}
|
||||||
Layout::from_var(env, var).expect("invalid layout from var"),
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut pairs = Vec::from_iter_in(pairs_it, arena);
|
|
||||||
|
|
||||||
pairs.sort_by(|(label1, layout1), (label2, layout2)| {
|
pairs.sort_by(|(label1, layout1), (label2, layout2)| {
|
||||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||||
|
@ -1630,12 +1634,14 @@ fn layout_from_flat_type<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type SortedField<'a> = (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>);
|
||||||
|
|
||||||
pub fn sort_record_fields<'a>(
|
pub fn sort_record_fields<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
ptr_bytes: u32,
|
ptr_bytes: u32,
|
||||||
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
) -> Result<Vec<'a, SortedField<'a>>, LayoutProblem> {
|
||||||
let mut env = Env {
|
let mut env = Env {
|
||||||
arena,
|
arena,
|
||||||
subs,
|
subs,
|
||||||
|
@ -1643,7 +1649,10 @@ pub fn sort_record_fields<'a>(
|
||||||
ptr_bytes,
|
ptr_bytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (it, _) = gather_fields_unsorted_iter(subs, RecordFields::empty(), var);
|
let (it, _) = match gather_fields_unsorted_iter(subs, RecordFields::empty(), var) {
|
||||||
|
Ok(it) => it,
|
||||||
|
Err(_) => return Err(LayoutProblem::Erroneous),
|
||||||
|
};
|
||||||
|
|
||||||
let it = it
|
let it = it
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1655,26 +1664,23 @@ pub fn sort_record_fields<'a>(
|
||||||
fn sort_record_fields_help<'a>(
|
fn sort_record_fields_help<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
|
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
|
||||||
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
) -> Result<Vec<'a, SortedField<'a>>, LayoutProblem> {
|
||||||
let ptr_bytes = env.ptr_bytes;
|
let ptr_bytes = env.ptr_bytes;
|
||||||
|
|
||||||
// Sort the fields by label
|
// Sort the fields by label
|
||||||
let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena);
|
let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena);
|
||||||
|
|
||||||
for (label, field) in fields_map {
|
for (label, field) in fields_map {
|
||||||
let var = match field {
|
match field {
|
||||||
RecordField::Demanded(v) => v,
|
RecordField::Demanded(v) | RecordField::Required(v) => {
|
||||||
RecordField::Required(v) => v,
|
let layout = Layout::from_var(env, v)?;
|
||||||
|
sorted_fields.push((label, v, Ok(layout)));
|
||||||
|
}
|
||||||
RecordField::Optional(v) => {
|
RecordField::Optional(v) => {
|
||||||
let layout = Layout::from_var(env, v).expect("invalid layout from var");
|
let layout = Layout::from_var(env, v)?;
|
||||||
sorted_fields.push((label, v, Err(layout)));
|
sorted_fields.push((label, v, Err(layout)));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout = Layout::from_var(env, var).expect("invalid layout from var");
|
|
||||||
|
|
||||||
sorted_fields.push((label, var, Ok(layout)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sorted_fields.sort_by(
|
sorted_fields.sort_by(
|
||||||
|
@ -1690,7 +1696,7 @@ fn sort_record_fields_help<'a>(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
sorted_fields
|
Ok(sorted_fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -2428,7 +2434,10 @@ fn layout_from_tag_union<'a>(
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub fn ext_var_is_empty_record(subs: &Subs, ext_var: Variable) -> bool {
|
pub fn ext_var_is_empty_record(subs: &Subs, ext_var: Variable) -> bool {
|
||||||
// the ext_var is empty
|
// the ext_var is empty
|
||||||
let fields = roc_types::types::gather_fields(subs, RecordFields::empty(), ext_var);
|
let fields = match roc_types::types::gather_fields(subs, RecordFields::empty(), ext_var) {
|
||||||
|
Ok(fields) => fields,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
fields.fields.is_empty()
|
fields.fields.is_empty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -764,7 +764,8 @@ fn type_to_variable<'a>(
|
||||||
let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext);
|
let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext);
|
||||||
|
|
||||||
let (it, new_ext_var) =
|
let (it, new_ext_var) =
|
||||||
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var);
|
gather_fields_unsorted_iter(subs, RecordFields::empty(), temp_ext_var)
|
||||||
|
.expect("Something ended up weird in this record type");
|
||||||
|
|
||||||
let it = it
|
let it = it
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#[cfg(feature = "gen-llvm")]
|
#[cfg(feature = "gen-llvm")]
|
||||||
use crate::helpers::llvm::assert_evals_to;
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
|
#[cfg(feature = "gen-llvm")]
|
||||||
|
use crate::helpers::llvm::expect_runtime_error_panic;
|
||||||
|
|
||||||
// #[cfg(feature = "gen-dev")]
|
// #[cfg(feature = "gen-dev")]
|
||||||
// use crate::helpers::dev::assert_evals_to;
|
// use crate::helpers::dev::assert_evals_to;
|
||||||
|
|
||||||
|
@ -2528,7 +2531,10 @@ fn list_any_empty_with_unknown_element_type() {
|
||||||
// Application crashed with message
|
// Application crashed with message
|
||||||
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
||||||
// Shutting down
|
// Shutting down
|
||||||
assert_evals_to!("List.any [] (\\_ -> True)", false, bool);
|
//
|
||||||
|
// TODO: eventually we should insert the empty type for unresolved type
|
||||||
|
// variables, since that means they're unbound.
|
||||||
|
expect_runtime_error_panic!("List.any [] (\\_ -> True)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2550,7 +2556,10 @@ fn list_all_empty_with_unknown_element_type() {
|
||||||
// Application crashed with message
|
// Application crashed with message
|
||||||
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
// UnresolvedTypeVar compiler/mono/src/ir.rs line 3775
|
||||||
// Shutting down
|
// Shutting down
|
||||||
assert_evals_to!("List.all [] (\\_ -> True)", false, bool);
|
//
|
||||||
|
// TODO: eventually we should insert the empty type for unresolved type
|
||||||
|
// variables, since that means they're unbound.
|
||||||
|
expect_runtime_error_panic!("List.all [] (\\_ -> True)");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#[cfg(feature = "gen-llvm")]
|
#[cfg(feature = "gen-llvm")]
|
||||||
use crate::helpers::llvm::assert_evals_to;
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
|
#[cfg(feature = "gen-llvm")]
|
||||||
|
use crate::helpers::llvm::expect_runtime_error_panic;
|
||||||
|
|
||||||
#[cfg(feature = "gen-dev")]
|
#[cfg(feature = "gen-dev")]
|
||||||
use crate::helpers::dev::assert_evals_to;
|
use crate::helpers::dev::assert_evals_to;
|
||||||
|
|
||||||
|
@ -1007,3 +1010,22 @@ fn both_have_unique_fields() {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
#[should_panic(
|
||||||
|
// TODO: something upstream is escaping the '
|
||||||
|
expected = r#"Roc failed with message: "Can\'t create record with improper layout""#
|
||||||
|
)]
|
||||||
|
fn call_with_bad_record_runtime_error() {
|
||||||
|
expect_runtime_error_panic!(indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
get : {a: Bool} -> Bool
|
||||||
|
get = \{a} -> a
|
||||||
|
get {b: ""}
|
||||||
|
"#
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
@ -28,8 +28,6 @@ pub fn test_builtin_defs(symbol: Symbol, var_store: &mut VarStore) -> Option<Def
|
||||||
builtin_defs_map(symbol, var_store)
|
builtin_defs_map(symbol, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is not actually dead code, but only used by cfg_test modules
|
|
||||||
// so "normally" it is dead, only at testing time is it used
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn create_llvm_module<'a>(
|
fn create_llvm_module<'a>(
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
|
@ -589,16 +587,39 @@ macro_rules! assert_evals_to {
|
||||||
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity);
|
assert_evals_to!($src, $expected, $ty, $crate::helpers::llvm::identity);
|
||||||
}};
|
}};
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
// Same as above, except with an additional transformation argument.
|
// same as above, except with an additional transformation argument.
|
||||||
{
|
{
|
||||||
#[cfg(feature = "wasm-cli-run")]
|
#[cfg(feature = "wasm-cli-run")]
|
||||||
$crate::helpers::llvm::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
$crate::helpers::llvm::assert_wasm_evals_to!(
|
||||||
|
$src, $expected, $ty, $transform, false, false
|
||||||
|
);
|
||||||
|
|
||||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! expect_runtime_error_panic {
|
||||||
|
($src:expr) => {{
|
||||||
|
#[cfg(feature = "wasm-cli-run")]
|
||||||
|
$crate::helpers::llvm::assert_wasm_evals_to!(
|
||||||
|
$src,
|
||||||
|
false, // fake value/type for eval
|
||||||
|
bool,
|
||||||
|
$crate::helpers::llvm::identity,
|
||||||
|
true // ignore problems
|
||||||
|
);
|
||||||
|
|
||||||
|
$crate::helpers::llvm::assert_llvm_evals_to!(
|
||||||
|
$src,
|
||||||
|
false, // fake value/type for eval
|
||||||
|
bool,
|
||||||
|
$crate::helpers::llvm::identity,
|
||||||
|
true // ignore problems
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn identity<T>(value: T) -> T {
|
pub fn identity<T>(value: T) -> T {
|
||||||
value
|
value
|
||||||
|
@ -617,7 +638,7 @@ macro_rules! assert_non_opt_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
// Same as above, except with an additional transformation argument.
|
// Same as above, except with an additional transformation argument.
|
||||||
{
|
{
|
||||||
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
$crate::helpers::llvm::assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
|
||||||
|
@ -633,3 +654,5 @@ pub(crate) use assert_llvm_evals_to;
|
||||||
pub(crate) use assert_non_opt_evals_to;
|
pub(crate) use assert_non_opt_evals_to;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) use assert_wasm_evals_to;
|
pub(crate) use assert_wasm_evals_to;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub(crate) use expect_runtime_error_panic;
|
||||||
|
|
|
@ -519,7 +519,8 @@ fn write_flat_type(env: &Env, flat_type: &FlatType, subs: &Subs, buf: &mut Strin
|
||||||
let RecordStructure {
|
let RecordStructure {
|
||||||
fields: sorted_fields,
|
fields: sorted_fields,
|
||||||
ext,
|
ext,
|
||||||
} = gather_fields(subs, *fields, *ext_var);
|
} = gather_fields(subs, *fields, *ext_var)
|
||||||
|
.expect("Something ended up weird in this record type");
|
||||||
let ext_var = ext;
|
let ext_var = ext;
|
||||||
|
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
|
|
|
@ -2010,7 +2010,8 @@ impl RecordFields {
|
||||||
subs: &'a Subs,
|
subs: &'a Subs,
|
||||||
ext: Variable,
|
ext: Variable,
|
||||||
) -> impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + 'a {
|
) -> impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + 'a {
|
||||||
let (it, _) = crate::types::gather_fields_unsorted_iter(subs, *self, ext);
|
let (it, _) = crate::types::gather_fields_unsorted_iter(subs, *self, ext)
|
||||||
|
.expect("Something weird ended up in a record type");
|
||||||
|
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
@ -2045,7 +2046,8 @@ impl RecordFields {
|
||||||
ext,
|
ext,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let record_structure = crate::types::gather_fields(subs, *self, ext);
|
let record_structure = crate::types::gather_fields(subs, *self, ext)
|
||||||
|
.expect("Something ended up weird in this record type");
|
||||||
|
|
||||||
(
|
(
|
||||||
Box::new(record_structure.fields.into_iter()),
|
Box::new(record_structure.fields.into_iter()),
|
||||||
|
|
|
@ -1701,14 +1701,20 @@ pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lower
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct RecordFieldsError;
|
||||||
|
|
||||||
pub fn gather_fields_unsorted_iter(
|
pub fn gather_fields_unsorted_iter(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
other_fields: RecordFields,
|
other_fields: RecordFields,
|
||||||
mut var: Variable,
|
mut var: Variable,
|
||||||
) -> (
|
) -> Result<
|
||||||
impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + '_,
|
(
|
||||||
Variable,
|
impl Iterator<Item = (&Lowercase, RecordField<Variable>)> + '_,
|
||||||
) {
|
Variable,
|
||||||
|
),
|
||||||
|
RecordFieldsError,
|
||||||
|
> {
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
use crate::subs::FlatType::*;
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
@ -1733,7 +1739,7 @@ pub fn gather_fields_unsorted_iter(
|
||||||
// TODO investigate apparently this one pops up in the reporting tests!
|
// TODO investigate apparently this one pops up in the reporting tests!
|
||||||
RigidVar(_) => break,
|
RigidVar(_) => break,
|
||||||
|
|
||||||
other => unreachable!("something weird ended up in a record type: {:?}", other),
|
_ => return Err(RecordFieldsError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1749,11 +1755,15 @@ pub fn gather_fields_unsorted_iter(
|
||||||
(field_name, record_field)
|
(field_name, record_field)
|
||||||
});
|
});
|
||||||
|
|
||||||
(it, var)
|
Ok((it, var))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_fields(subs: &Subs, other_fields: RecordFields, var: Variable) -> RecordStructure {
|
pub fn gather_fields(
|
||||||
let (it, ext) = gather_fields_unsorted_iter(subs, other_fields, var);
|
subs: &Subs,
|
||||||
|
other_fields: RecordFields,
|
||||||
|
var: Variable,
|
||||||
|
) -> Result<RecordStructure, RecordFieldsError> {
|
||||||
|
let (it, ext) = gather_fields_unsorted_iter(subs, other_fields, var)?;
|
||||||
|
|
||||||
let mut result: Vec<_> = it
|
let mut result: Vec<_> = it
|
||||||
.map(|(ref_label, field)| (ref_label.clone(), field))
|
.map(|(ref_label, field)| (ref_label.clone(), field))
|
||||||
|
@ -1761,10 +1771,10 @@ pub fn gather_fields(subs: &Subs, other_fields: RecordFields, var: Variable) ->
|
||||||
|
|
||||||
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
result.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
RecordStructure {
|
Ok(RecordStructure {
|
||||||
fields: result,
|
fields: result,
|
||||||
ext,
|
ext,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gather_tags_unsorted_iter(
|
pub fn gather_tags_unsorted_iter(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue