diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index eedbf56ccc..21e031270d 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -941,7 +941,7 @@ fn canonicalize_field<'a>( match field { // Both a label and a value, e.g. `{ name: "blah" }` - LabeledValue(label, _, loc_expr) => { + RequiredValue(label, _, loc_expr) => { let field_var = var_store.fresh(); let (loc_can_expr, output) = canonicalize_expr(env, var_store, scope, loc_expr.region, &loc_expr.value); @@ -954,6 +954,10 @@ fn canonicalize_field<'a>( ) } + OptionalValue(_, _, _) => { + todo!("TODO gracefully handle an optional field being used in an Expr"); + } + // A label with no value, e.g. `{ name }` (this is sugar for { name: name }) LabelOnly(_) => { panic!("Somehow a LabelOnly record field was not desugared!"); diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index 7ec9b20fe7..ecc40c7652 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -44,7 +44,14 @@ pub struct RecordDestruct { pub var: Variable, pub label: Lowercase, pub symbol: Symbol, - pub guard: Option<(Variable, Located)>, + pub typ: DestructType, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum DestructType { + Required, + Optional(Variable), + Guard(Variable, Located), } pub fn symbols_from_pattern(pattern: &Pattern) -> Vec { @@ -253,7 +260,7 @@ pub fn canonicalize_pattern<'a>( var: var_store.fresh(), label: Lowercase::from(label), symbol, - guard: None, + typ: DestructType::Required, }, }); } @@ -271,7 +278,8 @@ pub fn canonicalize_pattern<'a>( } }; } - RecordField(label, loc_guard) => { + + RequiredField(label, loc_guard) => { // a guard does not introduce the label into scope! let symbol = scope.ignore(label.into(), &mut env.ident_ids); let can_guard = canonicalize_pattern( @@ -289,7 +297,7 @@ pub fn canonicalize_pattern<'a>( var: var_store.fresh(), label: Lowercase::from(label), symbol, - guard: Some((var_store.fresh(), can_guard)), + typ: DestructType::Guard(var_store.fresh(), can_guard), }, }); } @@ -305,7 +313,8 @@ pub fn canonicalize_pattern<'a>( destructs, }) } - RecordField(_name, _loc_pattern) => { + + RequiredField(_name, _loc_pattern) | OptionalField(_name, _loc_pattern) => { unreachable!("should have been handled in RecordDestructure"); } diff --git a/compiler/fmt/src/annotation.rs b/compiler/fmt/src/annotation.rs index 5fb9526a44..f7ea0a5b7d 100644 --- a/compiler/fmt/src/annotation.rs +++ b/compiler/fmt/src/annotation.rs @@ -248,7 +248,7 @@ impl<'a> Formattable<'a> for AssignedField<'a, TypeAnnotation<'a>> { indent: u16, ) { // we abuse the `Newlines` type to decide between multiline or single-line layout - format_assigned_field_help(self, buf, parens, indent, " : ", newlines == Newlines::Yes); + format_assigned_field_help(self, buf, parens, indent, " ", newlines == Newlines::Yes); } } @@ -265,7 +265,7 @@ impl<'a> Formattable<'a> for AssignedField<'a, Expr<'a>> { indent: u16, ) { // we abuse the `Newlines` type to decide between multiline or single-line layout - format_assigned_field_help(self, buf, parens, indent, ": ", newlines == Newlines::Yes); + format_assigned_field_help(self, buf, parens, indent, "", newlines == Newlines::Yes); } } @@ -273,7 +273,9 @@ fn is_multiline_assigned_field_help<'a, T: Formattable<'a>>(afield: &AssignedFie use self::AssignedField::*; match afield { - LabeledValue(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(), + RequiredValue(_, spaces, ann) | OptionalValue(_, spaces, ann) => { + !spaces.is_empty() || ann.value.is_multiline() + } LabelOnly(_) => false, AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true, Malformed(text) => text.chars().any(|c| c == '\n'), @@ -285,7 +287,7 @@ fn format_assigned_field_help<'a, T>( buf: &mut String<'a>, parens: Parens, indent: u16, - separator: &str, + separator_prefix: &str, is_multiline: bool, ) where T: Formattable<'a>, @@ -293,7 +295,7 @@ fn format_assigned_field_help<'a, T>( use self::AssignedField::*; match zelf { - LabeledValue(name, spaces, ann) => { + RequiredValue(name, spaces, ann) => { if is_multiline { newline(buf, indent); } @@ -304,7 +306,23 @@ fn format_assigned_field_help<'a, T>( fmt_spaces(buf, spaces.iter(), indent); } - buf.push_str(separator); + buf.push_str(separator_prefix); + buf.push(':'); + ann.value.format(buf, indent); + } + OptionalValue(name, spaces, ann) => { + if is_multiline { + newline(buf, indent); + } + + buf.push_str(name.value); + + if !spaces.is_empty() { + fmt_spaces(buf, spaces.iter(), indent); + } + + buf.push_str(separator_prefix); + buf.push('?'); ann.value.format(buf, indent); } LabelOnly(name) => { @@ -316,10 +334,24 @@ fn format_assigned_field_help<'a, T>( } AssignedField::SpaceBefore(sub_field, spaces) => { fmt_comments_only(buf, spaces.iter(), indent); - format_assigned_field_help(sub_field, buf, parens, indent, separator, is_multiline); + format_assigned_field_help( + sub_field, + buf, + parens, + indent, + separator_prefix, + is_multiline, + ); } AssignedField::SpaceAfter(sub_field, spaces) => { - format_assigned_field_help(sub_field, buf, parens, indent, separator, is_multiline); + format_assigned_field_help( + sub_field, + buf, + parens, + indent, + separator_prefix, + is_multiline, + ); fmt_comments_only(buf, spaces.iter(), indent); } Malformed(raw) => { diff --git a/compiler/fmt/src/pattern.rs b/compiler/fmt/src/pattern.rs index ac35bcd585..b43f27ac27 100644 --- a/compiler/fmt/src/pattern.rs +++ b/compiler/fmt/src/pattern.rs @@ -25,7 +25,9 @@ impl<'a> Formattable<'a> for Pattern<'a> { Pattern::Nested(nested_pat) => nested_pat.is_multiline(), Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()), - Pattern::RecordField(_, subpattern) => subpattern.is_multiline(), + Pattern::RequiredField(_, subpattern) | Pattern::OptionalField(_, subpattern) => { + subpattern.is_multiline() + } Pattern::Identifier(_) | Pattern::GlobalTag(_) @@ -92,12 +94,18 @@ impl<'a> Formattable<'a> for Pattern<'a> { buf.push_str(" }"); } - RecordField(name, loc_pattern) => { + RequiredField(name, loc_pattern) => { buf.push_str(name); buf.push_str(": "); loc_pattern.format(buf, indent); } + OptionalField(name, loc_pattern) => { + buf.push_str(name); + buf.push_str(" ? "); + loc_pattern.format(buf, indent); + } + NumLiteral(string) => buf.push_str(string), NonBase10Literal { base,