Merge branch 'trunk' into double-typecheck

This commit is contained in:
Richard Feldman 2019-12-02 17:18:12 -05:00 committed by GitHub
commit 651e4563f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 286 additions and 214 deletions

View file

@ -59,7 +59,7 @@ pub fn canonicalize_module_defs<'a>(
for loc_def in loc_defs { for loc_def in loc_defs {
buf.push_back(canonicalize_def( buf.push_back(canonicalize_def(
arena, arena,
loc_def.value, &loc_def.value,
loc_def.region, loc_def.region,
home.clone(), home.clone(),
scope, scope,
@ -72,7 +72,7 @@ pub fn canonicalize_module_defs<'a>(
fn canonicalize_def<'a>( fn canonicalize_def<'a>(
arena: &Bump, arena: &Bump,
def: Def<'a>, def: &'a Def<'a>,
region: Region, region: Region,
home: Box<str>, home: Box<str>,
scope: &mut ImMap<Box<str>, (Symbol, Region)>, scope: &mut ImMap<Box<str>, (Symbol, Region)>,
@ -150,8 +150,7 @@ fn canonicalize_def<'a>(
// Ignore spaces // Ignore spaces
Def::SpaceBefore(def, _) | Def::SpaceAfter(def, _) => { Def::SpaceBefore(def, _) | Def::SpaceAfter(def, _) => {
// TODO FIXME performance disaster!!! canonicalize_def(arena, def, region, home, scope, var_store)
canonicalize_def(arena, def.clone(), region, home, scope, var_store)
} }
} }
} }
@ -860,6 +859,12 @@ fn canonicalize_expr(
local_successors(&References::new(), &env.closures) local_successors(&References::new(), &env.closures)
); );
} }
ast::Expr::Nested(sub_expr) => {
let (answer, output) =
canonicalize_expr(rigids, env, var_store, scope, region, sub_expr, expected);
(answer.value, output)
}
ast::Expr::BinaryInt(string) => { ast::Expr::BinaryInt(string) => {
let (constraint, answer) = let (constraint, answer) =
int_expr_from_result(var_store, finish_parsing_bin(string), env, expected, region); int_expr_from_result(var_store, finish_parsing_bin(string), env, expected, region);
@ -903,7 +908,7 @@ fn canonicalize_expr(
} }
ast::Expr::UnaryOp(_, loc_op) => { ast::Expr::UnaryOp(_, loc_op) => {
panic!( panic!(
"A binary operator did not get desugared somehow: {:?}", "A unary operator did not get desugared somehow: {:?}",
loc_op loc_op
); );
} }
@ -1258,7 +1263,7 @@ fn add_idents_from_pattern<'a>(
RecordField(_, _) => { RecordField(_, _) => {
panic!("TODO implement RecordField pattern in add_idents_from_pattern."); panic!("TODO implement RecordField pattern in add_idents_from_pattern.");
} }
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => { SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
// Ignore the newline/comment info; it doesn't matter in canonicalization. // Ignore the newline/comment info; it doesn't matter in canonicalization.
add_idents_from_pattern(region, pattern, scope, answer) add_idents_from_pattern(region, pattern, scope, answer)
} }
@ -1300,7 +1305,7 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
RecordField(_, _) => { RecordField(_, _) => {
panic!("TODO implement RecordField pattern in remove_idents."); panic!("TODO implement RecordField pattern in remove_idents.");
} }
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => { SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
// Ignore the newline/comment info; it doesn't matter in canonicalization. // Ignore the newline/comment info; it doesn't matter in canonicalization.
remove_idents(pattern, idents) remove_idents(pattern, idents)
} }

View file

@ -34,30 +34,41 @@ fn new_op_expr<'a>(
/// Reorder the expression tree based on operator precedence and associativity rules, /// Reorder the expression tree based on operator precedence and associativity rules,
/// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes. /// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes.
pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> { pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Located<Expr<'a>> {
use crate::operator::Associativity::*;
use std::cmp::Ordering;
match &loc_expr.value { match &loc_expr.value {
Float(_) Float(_)
| Nested(Float(_))
| Int(_) | Int(_)
| Nested(Int(_))
| HexInt(_) | HexInt(_)
| Nested(HexInt(_))
| OctalInt(_) | OctalInt(_)
| Nested(OctalInt(_))
| BinaryInt(_) | BinaryInt(_)
| Nested(BinaryInt(_))
| Str(_) | Str(_)
| Nested(Str(_))
| BlockStr(_) | BlockStr(_)
| Nested(BlockStr(_))
| QualifiedField(_, _) | QualifiedField(_, _)
| Nested(QualifiedField(_, _))
| AccessorFunction(_) | AccessorFunction(_)
| Nested(AccessorFunction(_))
| Var(_, _) | Var(_, _)
| Nested(Var(_, _))
| MalformedIdent(_) | MalformedIdent(_)
| Nested(MalformedIdent(_))
| MalformedClosure | MalformedClosure
| Nested(MalformedClosure)
| PrecedenceConflict(_, _, _) | PrecedenceConflict(_, _, _)
| Variant(_, _) => loc_expr, | Nested(PrecedenceConflict(_, _, _))
| Variant(_, _)
| Nested(Variant(_, _)) => loc_expr,
Field(sub_expr, paths) => arena.alloc(Located { Field(sub_expr, paths) | Nested(Field(sub_expr, paths)) => arena.alloc(Located {
region: loc_expr.region, region: loc_expr.region,
value: Field(desugar(arena, sub_expr), paths.clone()), value: Field(desugar(arena, sub_expr), paths.clone()),
}), }),
List(elems) => { List(elems) | Nested(List(elems)) => {
let mut new_elems = Vec::with_capacity_in(elems.len(), arena); let mut new_elems = Vec::with_capacity_in(elems.len(), arena);
for elem in elems { for elem in elems {
@ -70,7 +81,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
value, value,
}) })
} }
Record(fields) => { Record(fields) | Nested(Record(fields)) => {
let mut new_fields = Vec::with_capacity_in(fields.len(), arena); let mut new_fields = Vec::with_capacity_in(fields.len(), arena);
for field in fields { for field in fields {
@ -87,171 +98,14 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
value: Record(new_fields), value: Record(new_fields),
}) })
} }
Closure(loc_patterns, loc_ret) => arena.alloc(Located { Closure(loc_patterns, loc_ret) | Nested(Closure(loc_patterns, loc_ret)) => {
region: loc_expr.region, arena.alloc(Located {
value: Closure(loc_patterns, desugar(arena, loc_ret)), region: loc_expr.region,
}), value: Closure(loc_patterns, desugar(arena, loc_ret)),
BinOp(_) => { })
let mut infixes = Infixes::new(arena.alloc(loc_expr));
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
let mut op_stack: Vec<Located<BinOp>> = Vec::new_in(arena);
while let Some(token) = infixes.next() {
match token {
InfixToken::Arg(next_expr) => arg_stack.push(next_expr),
InfixToken::Op(next_op) => {
match op_stack.pop() {
Some(stack_op) => {
match next_op.value.cmp(&stack_op.value) {
Ordering::Less => {
// Inline
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
infixes.next_op = Some(next_op);
arg_stack.push(arena.alloc(new_op_expr(
arena,
left.clone(),
stack_op,
right.clone(),
)));
}
Ordering::Greater => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
Ordering::Equal => {
match (
next_op.value.associativity(),
stack_op.value.associativity(),
) {
(LeftAssociative, LeftAssociative) => {
// Inline
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
infixes.next_op = Some(next_op);
arg_stack.push(arena.alloc(new_op_expr(
arena,
left.clone(),
stack_op,
right.clone(),
)));
}
(RightAssociative, RightAssociative) => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
(NonAssociative, NonAssociative) => {
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
let bad_op = next_op.clone();
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
let broken_expr = new_op_expr(
arena,
left.clone(),
next_op,
right.clone(),
);
let region = broken_expr.region;
let value = Expr::PrecedenceConflict(
bad_op,
stack_op,
arena.alloc(broken_expr),
);
return arena.alloc(Located { region, value });
}
_ => {
// The operators had the same precedence but different associativity.
//
// In many languages, this case can happen due to (for example) <| and |> having the same
// precedence but different associativity. Languages which support custom operators with
// (e.g. Haskell) can potentially have arbitrarily many of these cases.
//
// By design, Roc neither allows custom operators nor has any built-in operators with
// the same precedence and different associativity, so this should never happen!
panic!("BinOps had the same associativity, but different precedence. This should never happen!");
}
}
}
}
}
None => op_stack.push(next_op),
};
}
}
}
for loc_op in op_stack.into_iter().rev() {
let right = desugar(arena, arg_stack.pop().unwrap());
let left = desugar(arena, arg_stack.pop().unwrap());
let region = Region::span_across(&left.region, &right.region);
let value = match loc_op.value {
Pizza => {
// Rewrite the Pizza operator into an Apply
match &right.value {
Apply(function, arguments, _called_via) => {
let mut args = Vec::with_capacity_in(1 + arguments.len(), arena);
args.push(left);
for arg in arguments {
args.push(arg);
}
Apply(function, args, CalledVia::BinOp(Pizza))
}
expr => {
// e.g. `1 |> (if b then (\a -> a) else (\c -> c))`
let mut args = Vec::with_capacity_in(1, arena);
args.push(*arena.alloc(left));
let function = arena.alloc(Located {
value: expr.clone(),
region: right.region,
});
Apply(function, args, CalledVia::BinOp(Pizza))
}
}
}
binop => {
// This is a normal binary operator like (+), so desugar it
// into the appropriate function call.
let (module_parts, name) = desugar_binop(binop, arena);
let mut args = Vec::with_capacity_in(2, arena);
args.push(left);
args.push(right);
let loc_expr = arena.alloc(Located {
value: Expr::Var(module_parts, name),
region: loc_op.region,
});
Apply(loc_expr, args, CalledVia::BinOp(binop))
}
};
arg_stack.push(arena.alloc(Located { region, value }));
}
assert_eq!(arg_stack.len(), 1);
arg_stack.pop().unwrap()
} }
Defs(defs, loc_ret) => { BinOp(_) | Nested(BinOp(_)) => desugar_bin_op(arena, loc_expr),
Defs(defs, loc_ret) | Nested(Defs(defs, loc_ret)) => {
let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena); let mut desugared_defs = Vec::with_capacity_in(defs.len(), arena);
for loc_def in defs.into_iter() { for loc_def in defs.into_iter() {
@ -265,7 +119,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
region: loc_expr.region, region: loc_expr.region,
}) })
} }
Apply(loc_fn, loc_args, called_via) => { Apply(loc_fn, loc_args, called_via) | Nested(Apply(loc_fn, loc_args, called_via)) => {
let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena); let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena);
for loc_arg in loc_args { for loc_arg in loc_args {
@ -277,15 +131,22 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
region: loc_expr.region, region: loc_expr.region,
}) })
} }
Case(loc_cond_expr, branches) => { Case(loc_cond_expr, branches) | Nested(Case(loc_cond_expr, branches)) => {
let loc_desugared_cond = &*arena.alloc(desugar(arena, &loc_cond_expr)); let loc_desugared_cond = &*arena.alloc(desugar(arena, &loc_cond_expr));
let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena); let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena);
for (loc_pattern, loc_branch_expr) in branches.into_iter() { for (loc_pattern, loc_branch_expr) in branches.into_iter() {
// TODO FIXME cloning performance disaster let desugared = desugar(arena, &loc_branch_expr);
desugared_branches.push(&*arena.alloc(( desugared_branches.push(&*arena.alloc((
loc_pattern.clone(), Located {
desugar(arena, &loc_branch_expr).clone(), region: loc_pattern.region,
value: Pattern::Nested(&loc_pattern.value),
},
Located {
region: desugared.region,
value: Nested(&desugared.value),
},
))); )));
} }
@ -294,7 +155,7 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
region: loc_expr.region, region: loc_expr.region,
}) })
} }
UnaryOp(loc_arg, loc_op) => { UnaryOp(loc_arg, loc_op) | Nested(UnaryOp(loc_arg, loc_op)) => {
use crate::operator::UnaryOp::*; use crate::operator::UnaryOp::*;
let region = loc_op.region; let region = loc_op.region;
@ -317,25 +178,25 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
region: loc_expr.region, region: loc_expr.region,
}) })
} }
SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => { SpaceBefore(expr, _)
| Nested(SpaceBefore(expr, _))
| SpaceAfter(expr, _)
| Nested(SpaceAfter(expr, _))
| ParensAround(expr)
| Nested(ParensAround(expr))
| Nested(Nested(expr)) => {
// Since we've already begun canonicalization, spaces and parens // Since we've already begun canonicalization, spaces and parens
// are no longer needed and should be dropped. // are no longer needed and should be dropped.
desugar( desugar(
arena, arena,
arena.alloc(Located { arena.alloc(Located {
// TODO FIXME performance disaster!!! Must remove this clone! value: Nested(expr),
//
// This won't be easy because:
//
// * If this function takes an &'a Expr, then Infixes hits a problem.
// * If SpaceBefore holds a Loc<&'a Expr>, then Spaceable hits a problem.
// * If all the existing &'a Loc<Expr> values become Loc<&'a Expr>...who knows?
value: (*expr).clone(),
region: loc_expr.region, region: loc_expr.region,
}), }),
) )
} }
If((condition, then_branch, else_branch)) => { If((condition, then_branch, else_branch))
| Nested(If((condition, then_branch, else_branch))) => {
// desugar if into case, meaning that // desugar if into case, meaning that
// //
// if b then x else y // if b then x else y
@ -358,13 +219,15 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
// no type errors will occur here so using this region should be fine // no type errors will occur here so using this region should be fine
let pattern_region = condition.region; let pattern_region = condition.region;
// TODO make False qualified
branches.push(&*arena.alloc(( branches.push(&*arena.alloc((
Located { Located {
value: Pattern::Variant(&[], "False"), value: Pattern::Variant(&[], "False"),
region: pattern_region, region: pattern_region,
}, },
else_branch.clone(), Located {
value: Nested(&else_branch.value),
region: else_branch.region,
},
))); )));
branches.push(&*arena.alloc(( branches.push(&*arena.alloc((
@ -372,7 +235,10 @@ pub fn desugar<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'a>>) -> &'a Loca
value: Pattern::Underscore, value: Pattern::Underscore,
region: pattern_region, region: pattern_region,
}, },
then_branch.clone(), Located {
value: Nested(&then_branch.value),
region: then_branch.region,
},
))); )));
desugar( desugar(
@ -417,23 +283,27 @@ fn desugar_field<'a>(
use crate::parse::ast::AssignedField::*; use crate::parse::ast::AssignedField::*;
match field { match field {
LabeledValue(ref loc_str, spaces, loc_expr) => { LabeledValue(loc_str, spaces, loc_expr) => AssignedField::LabeledValue(
AssignedField::LabeledValue(loc_str.clone(), spaces, desugar(arena, loc_expr)) Located {
} value: loc_str.value,
LabelOnly(ref loc_str) => LabelOnly(loc_str.clone()), region: loc_str.region,
SpaceBefore(ref field, spaces) => { },
SpaceBefore(arena.alloc(desugar_field(arena, field)), spaces) spaces,
} desugar(arena, loc_expr),
SpaceAfter(ref field, spaces) => { ),
SpaceAfter(arena.alloc(desugar_field(arena, field)), spaces) LabelOnly(loc_str) => LabelOnly(Located {
} value: loc_str.value,
region: loc_str.region,
}),
SpaceBefore(field, spaces) => SpaceBefore(arena.alloc(desugar_field(arena, field)), spaces),
SpaceAfter(field, spaces) => SpaceAfter(arena.alloc(desugar_field(arena, field)), spaces),
Malformed(string) => Malformed(string), Malformed(string) => Malformed(string),
} }
} }
#[inline(always)] #[inline(always)]
fn desugar_binop(binop: BinOp, arena: &Bump) -> (&[&str], &str) { fn binop_to_function(binop: BinOp, arena: &Bump) -> (&[&str], &str) {
use self::BinOp::*; use self::BinOp::*;
match binop { match binop {
@ -505,6 +375,189 @@ fn desugar_binop(binop: BinOp, arena: &Bump) -> (&[&str], &str) {
} }
} }
fn desugar_bin_op<'a>(arena: &'a Bump, loc_expr: &'a Located<Expr<'_>>) -> &'a Located<Expr<'a>> {
use crate::operator::Associativity::*;
use std::cmp::Ordering;
let mut infixes = Infixes::new(loc_expr);
let mut arg_stack: Vec<&'a Located<Expr>> = Vec::new_in(arena);
let mut op_stack: Vec<Located<BinOp>> = Vec::new_in(arena);
while let Some(token) = infixes.next() {
match token {
InfixToken::Arg(next_expr) => arg_stack.push(next_expr),
InfixToken::Op(next_op) => {
match op_stack.pop() {
Some(stack_op) => {
match next_op.value.cmp(&stack_op.value) {
Ordering::Less => {
// Inline
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
infixes.next_op = Some(next_op);
arg_stack.push(arena.alloc(new_op_expr(
arena,
Located {
value: Nested(&left.value),
region: left.region,
},
stack_op,
Located {
value: Nested(&right.value),
region: right.region,
},
)));
}
Ordering::Greater => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
Ordering::Equal => {
match (
next_op.value.associativity(),
stack_op.value.associativity(),
) {
(LeftAssociative, LeftAssociative) => {
// Inline
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
infixes.next_op = Some(next_op);
arg_stack.push(arena.alloc(new_op_expr(
arena,
Located {
value: Nested(&left.value),
region: left.region,
},
stack_op,
Located {
value: Nested(&right.value),
region: right.region,
},
)));
}
(RightAssociative, RightAssociative) => {
// Swap
op_stack.push(stack_op);
op_stack.push(next_op);
}
(NonAssociative, NonAssociative) => {
// Both operators were non-associative, e.g. (True == False == False).
// We should tell the author to disambiguate by grouping them with parens.
let bad_op = next_op.clone();
let right = arg_stack.pop().unwrap();
let left = arg_stack.pop().unwrap();
let broken_expr = new_op_expr(
arena,
Located {
value: Nested(&left.value),
region: left.region,
},
next_op,
Located {
value: Nested(&right.value),
region: right.region,
},
);
let region = broken_expr.region;
let value = Expr::PrecedenceConflict(
bad_op,
stack_op,
arena.alloc(broken_expr),
);
return arena.alloc(Located { region, value });
}
_ => {
// The operators had the same precedence but different associativity.
//
// In many languages, this case can happen due to (for example) <| and |> having the same
// precedence but different associativity. Languages which support custom operators with
// (e.g. Haskell) can potentially have arbitrarily many of these cases.
//
// By design, Roc neither allows custom operators nor has any built-in operators with
// the same precedence and different associativity, so this should never happen!
panic!("BinOps had the same associativity, but different precedence. This should never happen!");
}
}
}
}
}
None => op_stack.push(next_op),
};
}
}
}
for loc_op in op_stack.into_iter().rev() {
let right = desugar(arena, arg_stack.pop().unwrap());
let left = desugar(arena, arg_stack.pop().unwrap());
let region = Region::span_across(&left.region, &right.region);
let value = match loc_op.value {
Pizza => {
// Rewrite the Pizza operator into an Apply
match &right.value {
Apply(function, arguments, _called_via) => {
let mut args = Vec::with_capacity_in(1 + arguments.len(), arena);
args.push(left);
for arg in arguments {
args.push(arg);
}
Apply(function, args, CalledVia::BinOp(Pizza))
}
expr => {
// e.g. `1 |> (if b then (\a -> a) else (\c -> c))`
let mut args = Vec::with_capacity_in(1, arena);
args.push(left);
let function = arena.alloc(Located {
value: Nested(expr),
region: right.region,
});
Apply(function, args, CalledVia::BinOp(Pizza))
}
}
}
binop => {
// This is a normal binary operator like (+), so desugar it
// into the appropriate function call.
let (module_parts, name) = binop_to_function(binop, arena);
let mut args = Vec::with_capacity_in(2, arena);
args.push(left);
args.push(right);
let loc_expr = arena.alloc(Located {
value: Expr::Var(module_parts, name),
region: loc_op.region,
});
Apply(loc_expr, args, CalledVia::BinOp(binop))
}
};
arg_stack.push(arena.alloc(Located { region, value }));
}
assert_eq!(arg_stack.len(), 1);
arg_stack.pop().unwrap()
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum InfixToken<'a> { enum InfixToken<'a> {
Arg(&'a Located<Expr<'a>>), Arg(&'a Located<Expr<'a>>),
@ -561,9 +614,10 @@ impl<'a> Iterator for Infixes<'a> {
.remaining_expr .remaining_expr
.take() .take()
.map(|loc_expr| match loc_expr.value { .map(|loc_expr| match loc_expr.value {
Expr::BinOp((left, op, right)) => { Expr::BinOp((left, loc_op, right))
| Expr::Nested(Expr::BinOp((left, loc_op, right))) => {
self.remaining_expr = Some(right); self.remaining_expr = Some(right);
self.next_op = Some(op.clone()); self.next_op = Some(loc_op.clone());
InfixToken::Arg(left) InfixToken::Arg(left)
} }

View file

@ -258,7 +258,7 @@ pub fn canonicalize_pattern<'a>(
}, },
// &EmptyRecordLiteral => Pattern::EmptyRecordLiteral, // &EmptyRecordLiteral => Pattern::EmptyRecordLiteral,
&SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => { &SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) | Nested(sub_pattern) => {
return canonicalize_pattern( return canonicalize_pattern(
env, env,
state, state,
@ -271,6 +271,7 @@ pub fn canonicalize_pattern<'a>(
expected, expected,
) )
} }
_ => panic!("TODO finish restoring can_pattern branch for {:?}", pattern), _ => panic!("TODO finish restoring can_pattern branch for {:?}", pattern),
}; };
@ -356,7 +357,7 @@ fn add_constraints<'a>(
)); ));
} }
SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => { SpaceBefore(pattern, _) | SpaceAfter(pattern, _) | Nested(pattern) => {
add_constraints(pattern, scope, region, expected, state) add_constraints(pattern, scope, region, expected, state)
} }

View file

@ -286,7 +286,7 @@ pub fn is_multiline_expr<'a>(expr: &'a Expr<'a>) -> bool {
is_multiline_expr(&loc_subexpr.value) is_multiline_expr(&loc_subexpr.value)
} }
ParensAround(subexpr) => is_multiline_expr(&subexpr), ParensAround(subexpr) | Nested(subexpr) => is_multiline_expr(&subexpr),
Closure(loc_patterns, loc_body) => { Closure(loc_patterns, loc_body) => {
// check the body first because it's more likely to be multiline // check the body first because it's more likely to be multiline

View file

@ -84,6 +84,10 @@ pub fn fmt_pattern<'a>(
fmt_spaces(buf, spaces.iter(), indent); fmt_spaces(buf, spaces.iter(), indent);
} }
Nested(sub_pattern) => {
fmt_pattern(buf, sub_pattern, indent, apply_needs_parens);
}
// Malformed // Malformed
Malformed(string) => buf.push_str(string), Malformed(string) => buf.push_str(string),
QualifiedIdentifier(maybe_qualified) => { QualifiedIdentifier(maybe_qualified) => {

View file

@ -168,6 +168,10 @@ pub enum Expr<'a> {
SpaceAfter(&'a Expr<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a Expr<'a>, &'a [CommentOrNewline<'a>]),
ParensAround(&'a Expr<'a>), ParensAround(&'a Expr<'a>),
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
/// It lets us take an (&Expr) and create a plain (Expr) from it.
Nested(&'a Expr<'a>),
// Problems // Problems
MalformedIdent(&'a str), MalformedIdent(&'a str),
MalformedClosure, MalformedClosure,
@ -279,6 +283,10 @@ pub enum Pattern<'a> {
/// can only occur inside of a RecordDestructure /// can only occur inside of a RecordDestructure
RecordField(&'a str, &'a Loc<Pattern<'a>>), RecordField(&'a str, &'a Loc<Pattern<'a>>),
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
/// It lets us take an (&Expr) and create a plain (Expr) from it.
Nested(&'a Pattern<'a>),
// Literal // Literal
IntLiteral(&'a str), IntLiteral(&'a str),
HexIntLiteral(&'a str), HexIntLiteral(&'a str),

View file

@ -272,7 +272,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
spaces, spaces,
)), )),
Expr::ParensAround(sub_expr) => expr_to_pattern(arena, sub_expr), Expr::ParensAround(sub_expr) | Expr::Nested(sub_expr) => expr_to_pattern(arena, sub_expr),
Expr::Record(loc_assigned_fields) => { Expr::Record(loc_assigned_fields) => {
let mut loc_patterns = Vec::with_capacity_in(loc_assigned_fields.len(), arena); let mut loc_patterns = Vec::with_capacity_in(loc_assigned_fields.len(), arena);