Add RigidOptional record field variant

This commit is contained in:
Ayaz Hafiz 2022-08-09 09:39:06 -07:00
parent b911e01136
commit 81bb889e93
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
13 changed files with 102 additions and 12 deletions

View file

@ -2152,6 +2152,8 @@ fn constrain_typed_def(
Region::span_across(&annotation.region, &def.loc_expr.region),
));
dbg!(&signature);
// when a def is annotated, and its body is a closure, treat this
// as a named function (in elm terms) for error messages.
//

View file

@ -2149,7 +2149,7 @@ fn layout_from_flat_type<'a>(
RecordField::Required(field_var) | RecordField::Demanded(field_var) => {
sortables.push((label, Layout::from_var(env, field_var)?));
}
RecordField::Optional(_) => {
RecordField::Optional(_) | RecordField::RigidOptional(_) => {
// drop optional fields
}
}
@ -2250,7 +2250,7 @@ fn sort_record_fields_help<'a>(
let layout = Layout::from_var(env, v)?;
sorted_fields.push((label, v, Ok(layout)));
}
RecordField::Optional(v) => {
RecordField::Optional(v) | RecordField::RigidOptional(v) => {
let layout = Layout::from_var(env, v)?;
sorted_fields.push((label, v, Err(layout)));
}

View file

@ -804,7 +804,7 @@ impl Layout {
let it = slice.indices().zip(fields.iter_all());
for (target_index, (_, field_index, var_index)) in it {
match subs.record_fields[field_index.index as usize] {
RecordField::Optional(_) => {
RecordField::Optional(_) | RecordField::RigidOptional(_) => {
// do nothing
}
RecordField::Required(_) | RecordField::Demanded(_) => {

View file

@ -32,7 +32,7 @@ use roc_types::subs::{
use roc_types::types::Type::{self, *};
use roc_types::types::{
gather_fields_unsorted_iter, AliasCommon, AliasKind, Category, OptAbleType, OptAbleVar, Reason,
TypeExtension, Uls,
RecordField, TypeExtension, Uls,
};
use roc_unify::unify::{
unify, unify_introduced_ability_specialization, Env as UEnv, Mode, Obligated,
@ -2238,6 +2238,7 @@ fn type_to_variable<'a>(
Optional(t) => Optional(helper!(t)),
Required(t) => Required(helper!(t)),
Demanded(t) => Demanded(helper!(t)),
RigidOptional(t) => RigidOptional(helper!(t)),
}
};
@ -3478,11 +3479,35 @@ fn deep_copy_var_help(
let new_variables =
copy_sequence!(fields.len(), fields.iter_variables());
// When copying a let-generalized record to a specialized region, rigid
// optionals just become optionals.
let field_types = subs.get_subs_slice(fields.record_fields());
let has_rigid_optional_field = field_types
.iter()
.any(|f| matches!(f, RecordField::RigidOptional(..)));
let new_field_types_start = if has_rigid_optional_field {
let field_types = field_types.to_vec();
let slice = SubsSlice::extend_new(
&mut subs.record_fields,
field_types.into_iter().map(|f| match f {
RecordField::RigidOptional(()) => RecordField::Optional(()),
RecordField::Demanded(_)
| RecordField::Required(_)
| RecordField::Optional(_) => f,
}),
);
slice.start
} else {
fields.field_types_start
};
RecordFields {
length: fields.length,
field_names_start: fields.field_names_start,
variables_start: new_variables.start,
field_types_start: fields.field_types_start,
field_types_start: new_field_types_start,
}
};

View file

@ -1104,6 +1104,7 @@ fn write_flat_type<'a>(
Optional(_) => buf.push_str(" ? "),
Required(_) => buf.push_str(" : "),
Demanded(_) => buf.push_str(" : "),
RigidOptional(_) => buf.push_str(" ? "),
};
write_content(

View file

@ -922,6 +922,7 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
for (name, content) in it {
let separator = match content {
RecordField::Optional(_) => '?',
RecordField::RigidOptional(_) => '?',
RecordField::Required(_) => ':',
RecordField::Demanded(_) => ':',
};
@ -3789,6 +3790,7 @@ fn flat_type_to_err_type(
Optional(_) => Optional(error_type),
Required(_) => Required(error_type),
Demanded(_) => Demanded(error_type),
RigidOptional(_) => RigidOptional(error_type),
};
err_fields.insert(label, err_record_field);

View file

@ -29,13 +29,16 @@ const GREEK_LETTERS: &[char] = &[
/// Cannot unify with an Optional field, but can unify with a Required field
/// - Required: introduced by record literals and type annotations.
/// Can unify with Optional and Demanded
/// - Optional: introduced by pattern matches and annotations.
/// - Optional: introduced by pattern matches, e.g. { x ? "" } ->
/// Can unify with Required, but not with Demanded
/// - RigidOptional: introduced by annotations, e.g. { x ? Str}
/// Can only unify with Optional, to prevent a required field being typed as Optional
#[derive(PartialEq, Eq, Clone, Hash)]
pub enum RecordField<T> {
Optional(T),
Required(T),
Demanded(T),
Required(T),
Optional(T),
RigidOptional(T),
}
impl<T: Copy> Copy for RecordField<T> {}
@ -48,6 +51,7 @@ impl<T: fmt::Debug> fmt::Debug for RecordField<T> {
Optional(typ) => write!(f, "Optional({:?})", typ),
Required(typ) => write!(f, "Required({:?})", typ),
Demanded(typ) => write!(f, "Demanded({:?})", typ),
RigidOptional(typ) => write!(f, "RigidOptional({:?})", typ),
}
}
}
@ -60,6 +64,7 @@ impl<T> RecordField<T> {
Optional(t) => t,
Required(t) => t,
Demanded(t) => t,
RigidOptional(t) => t,
}
}
@ -70,6 +75,7 @@ impl<T> RecordField<T> {
Optional(t) => t,
Required(t) => t,
Demanded(t) => t,
RigidOptional(t) => t,
}
}
@ -80,6 +86,7 @@ impl<T> RecordField<T> {
Optional(t) => t,
Required(t) => t,
Demanded(t) => t,
RigidOptional(t) => t,
}
}
@ -92,6 +99,7 @@ impl<T> RecordField<T> {
Optional(t) => Optional(f(t)),
Required(t) => Required(f(t)),
Demanded(t) => Demanded(f(t)),
RigidOptional(t) => RigidOptional(f(t)),
}
}
}
@ -104,6 +112,7 @@ impl RecordField<Type> {
Optional(typ) => typ.substitute(substitutions),
Required(typ) => typ.substitute(substitutions),
Demanded(typ) => typ.substitute(substitutions),
RigidOptional(typ) => typ.substitute(substitutions),
}
}
@ -119,6 +128,7 @@ impl RecordField<Type> {
Optional(typ) => typ.substitute_alias(rep_symbol, rep_args, actual),
Required(typ) => typ.substitute_alias(rep_symbol, rep_args, actual),
Demanded(typ) => typ.substitute_alias(rep_symbol, rep_args, actual),
RigidOptional(typ) => typ.substitute_alias(rep_symbol, rep_args, actual),
}
}
@ -137,6 +147,7 @@ impl RecordField<Type> {
Optional(typ) => typ.instantiate_aliases(region, aliases, var_store, introduced),
Required(typ) => typ.instantiate_aliases(region, aliases, var_store, introduced),
Demanded(typ) => typ.instantiate_aliases(region, aliases, var_store, introduced),
RigidOptional(typ) => typ.instantiate_aliases(region, aliases, var_store, introduced),
}
}
@ -147,6 +158,7 @@ impl RecordField<Type> {
Optional(typ) => typ.contains_symbol(rep_symbol),
Required(typ) => typ.contains_symbol(rep_symbol),
Demanded(typ) => typ.contains_symbol(rep_symbol),
RigidOptional(typ) => typ.contains_symbol(rep_symbol),
}
}
pub fn contains_variable(&self, rep_variable: Variable) -> bool {
@ -156,6 +168,7 @@ impl RecordField<Type> {
Optional(typ) => typ.contains_variable(rep_variable),
Required(typ) => typ.contains_variable(rep_variable),
Demanded(typ) => typ.contains_variable(rep_variable),
RigidOptional(typ) => typ.contains_variable(rep_variable),
}
}
}
@ -564,6 +577,9 @@ impl fmt::Debug for Type {
RecordField::Optional(_) => write!(f, "{:?} ? {:?}", label, field_type)?,
RecordField::Required(_) => write!(f, "{:?} : {:?}", label, field_type)?,
RecordField::Demanded(_) => write!(f, "{:?} : {:?}", label, field_type)?,
RecordField::RigidOptional(_) => {
write!(f, "{:?} ? {:?}", label, field_type)?
}
}
if any_written_yet {
@ -1595,6 +1611,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
Optional(x) => variables_help(x, accum),
Required(x) => variables_help(x, accum),
Demanded(x) => variables_help(x, accum),
RigidOptional(x) => variables_help(x, accum),
};
}
@ -1736,6 +1753,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
Optional(x) => variables_help_detailed(x, accum),
Required(x) => variables_help_detailed(x, accum),
Demanded(x) => variables_help_detailed(x, accum),
RigidOptional(x) => variables_help_detailed(x, accum),
};
}
@ -2312,7 +2330,7 @@ fn write_error_type_help(
buf.push_str(label.as_str());
let content = match field {
Optional(content) => {
Optional(content) | RigidOptional(content) => {
buf.push_str(" ? ");
content
}
@ -2466,7 +2484,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
buf.push_str(label.as_str());
let content = match field {
Optional(content) => {
Optional(content) | RigidOptional(content) => {
buf.push_str(" ? ");
content
}

View file

@ -1772,8 +1772,10 @@ fn unify_shared_fields<M: MetaCollector>(
// Unification of optional fields
//
// Demanded does not unify with Optional
// RigidOptional does not unify with Required or Demanded
// Unifying Required with Demanded => Demanded
// Unifying Optional with Required => Required
// Unifying Optional with RigidOptional => RigidOptional
// Unifying X with X => X
let actual = match (actual, expected) {
(Demanded(_), Optional(_)) | (Optional(_), Demanded(_)) => {
@ -1787,6 +1789,15 @@ fn unify_shared_fields<M: MetaCollector>(
(Required(val), Optional(_)) => Required(val),
(Optional(val), Required(_)) => Required(val),
(Optional(val), Optional(_)) => Optional(val),
(RigidOptional(val), Optional(_)) | (Optional(_), RigidOptional(val)) => {
RigidOptional(val)
}
(RigidOptional(_), Demanded(_) | Required(_))
| (Demanded(_) | Required(_), RigidOptional(_)) => {
// this is an error, but we continue to give better error messages
continue;
}
(RigidOptional(val), RigidOptional(_)) => RigidOptional(val),
};
matching_fields.push((name, actual));