implement mono / lowering for tuples

This commit is contained in:
Joshua Warner 2023-01-24 20:23:17 -08:00
parent 65f8bb3d0d
commit 5a6be05ead
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
42 changed files with 1773 additions and 290 deletions

View file

@ -448,7 +448,7 @@ pub fn find_type_def_symbols(
As(actual, _, _) => {
stack.push(&actual.value);
}
Tuple { fields: _, ext: _ } => {
Tuple { elems: _, ext: _ } => {
todo!("find_type_def_symbols: Tuple");
}
Record { fields, ext } => {
@ -872,8 +872,41 @@ fn can_annotation_help(
}
}
Tuple { fields: _, ext: _ } => {
todo!("tuple");
Tuple { elems, ext } => {
let (ext_type, is_implicit_openness) = can_extension_type(
env,
pol,
scope,
var_store,
introduced_variables,
local_aliases,
references,
ext,
roc_problem::can::ExtensionTypeKind::Record,
);
debug_assert!(
matches!(is_implicit_openness, ExtImplicitOpenness::No),
"tuples should never be implicitly inferred open"
);
debug_assert!(!elems.is_empty()); // We don't allow empty tuples
let elem_types = can_assigned_tuple_elems(
env,
pol,
&elems.items,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
Type::Tuple(
elem_types,
TypeExtension::from_type(ext_type, is_implicit_openness),
)
}
Record { fields, ext } => {
let (ext_type, is_implicit_openness) = can_extension_type(
@ -1440,6 +1473,39 @@ fn can_assigned_fields<'a>(
field_types
}
// TODO trim down these arguments!
#[allow(clippy::too_many_arguments)]
fn can_assigned_tuple_elems<'a>(
env: &mut Env,
pol: CanPolarity,
elems: &&[Loc<TypeAnnotation<'a>>],
scope: &mut Scope,
var_store: &mut VarStore,
introduced_variables: &mut IntroducedVariables,
local_aliases: &mut VecMap<Symbol, Alias>,
references: &mut VecSet<Symbol>,
) -> VecMap<usize, Type> {
let mut elem_types = VecMap::with_capacity(elems.len());
for (index, loc_elem) in elems.iter().enumerate() {
let elem_type = can_annotation_help(
env,
pol,
&loc_elem.value,
loc_elem.region,
scope,
var_store,
introduced_variables,
local_aliases,
references,
);
elem_types.insert(index, elem_type);
}
elem_types
}
// TODO trim down these arguments!
#[allow(clippy::too_many_arguments)]
fn can_tags<'a>(

View file

@ -1,10 +1,9 @@
use crate::{
def::Def,
expr::{
ClosureData, Expr, Field, OpaqueWrapFunctionData, RecordAccessorData, TupleAccessorData,
WhenBranchPattern,
ClosureData, Expr, Field, OpaqueWrapFunctionData, StructAccessorData, WhenBranchPattern,
},
pattern::{DestructType, ListPatterns, Pattern, RecordDestruct},
pattern::{DestructType, ListPatterns, Pattern, RecordDestruct, TupleDestruct},
};
use roc_module::{
ident::{Lowercase, TagName},
@ -513,7 +512,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
field: field.clone(),
},
RecordAccessor(RecordAccessorData {
RecordAccessor(StructAccessorData {
name,
function_var,
record_var,
@ -521,7 +520,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
ext_var,
field_var,
field,
}) => RecordAccessor(RecordAccessorData {
}) => RecordAccessor(StructAccessorData {
name: *name,
function_var: sub!(*function_var),
record_var: sub!(*record_var),
@ -545,24 +544,6 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
index: *index,
},
TupleAccessor(TupleAccessorData {
name,
function_var,
tuple_var: record_var,
closure_var,
ext_var,
elem_var: field_var,
index,
}) => TupleAccessor(TupleAccessorData {
name: *name,
function_var: sub!(*function_var),
tuple_var: sub!(*record_var),
closure_var: sub!(*closure_var),
ext_var: sub!(*ext_var),
elem_var: sub!(*field_var),
index: *index,
}),
RecordUpdate {
record_var,
ext_var,
@ -794,6 +775,30 @@ fn deep_copy_pattern_help<C: CopyEnv>(
})
.collect(),
},
TupleDestructure {
whole_var,
ext_var,
destructs,
} => TupleDestructure {
whole_var: sub!(*whole_var),
ext_var: sub!(*ext_var),
destructs: destructs
.iter()
.map(|lrd| {
lrd.map(
|TupleDestruct {
destruct_index: index,
var,
typ: (tyvar, pat),
}: &crate::pattern::TupleDestruct| TupleDestruct {
destruct_index: *index,
var: sub!(*var),
typ: (sub!(*tyvar), pat.map(|p| go_help!(p))),
},
)
})
.collect(),
},
List {
list_var,
elem_var,

View file

@ -5,7 +5,7 @@ use crate::expr::Expr::{self, *};
use crate::expr::{
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, WhenBranch,
};
use crate::pattern::{Pattern, RecordDestruct};
use crate::pattern::{Pattern, RecordDestruct, TupleDestruct};
use roc_module::symbol::{Interns, ModuleId, Symbol};
@ -335,7 +335,6 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
f.text(format!("@{}", opaque_name.as_str(c.interns)))
}
RecordAccessor(_) => todo!(),
TupleAccessor(_) => todo!(),
RecordUpdate {
symbol, updates, ..
} => f
@ -505,6 +504,19 @@ fn pattern<'a>(
)
.append(f.text("}"))
.group(),
TupleDestructure { destructs, .. } => f
.text("(")
.append(
f.intersperse(
destructs
.iter()
.map(|l| &l.value)
.map(|TupleDestruct { typ: (_, p), .. }| pattern(c, Free, f, &p.value)),
f.text(", "),
),
)
.append(f.text(")"))
.group(),
List { .. } => todo!(),
NumLiteral(_, n, _, _) | IntLiteral(_, _, n, _, _) | FloatLiteral(_, _, n, _, _) => {
f.text(&**n)

View file

@ -15,7 +15,7 @@ use crate::expr::AnnotatedMark;
use crate::expr::ClosureData;
use crate::expr::Declarations;
use crate::expr::Expr::{self, *};
use crate::expr::RecordAccessorData;
use crate::expr::StructAccessorData;
use crate::expr::{canonicalize_expr, Output, Recursive};
use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern};
use crate::procedure::References;
@ -36,6 +36,7 @@ use roc_parse::ast::AssignedField;
use roc_parse::ast::Defs;
use roc_parse::ast::ExtractSpaces;
use roc_parse::ast::TypeHeader;
use roc_parse::ident::Accessor;
use roc_parse::pattern::PatternType;
use roc_problem::can::ShadowKind;
use roc_problem::can::{CycleEntry, Problem, RuntimeError};
@ -45,6 +46,7 @@ use roc_types::subs::{VarStore, Variable};
use roc_types::types::AliasCommon;
use roc_types::types::AliasKind;
use roc_types::types::AliasVar;
use roc_types::types::IndexOrField;
use roc_types::types::LambdaSet;
use roc_types::types::MemberImpl;
use roc_types::types::OptAbleType;
@ -1995,6 +1997,16 @@ fn pattern_to_vars_by_symbol(
vars_by_symbol.insert(*opaque, expr_var);
}
TupleDestructure { destructs, .. } => {
for destruct in destructs {
pattern_to_vars_by_symbol(
vars_by_symbol,
&destruct.value.typ.1.value,
destruct.value.typ.0,
);
}
}
RecordDestructure { destructs, .. } => {
for destruct in destructs {
vars_by_symbol.insert(destruct.value.symbol, destruct.value.var);
@ -2316,19 +2328,23 @@ fn canonicalize_pending_body<'a>(
ident: defined_symbol,
..
},
ast::Expr::RecordAccessorFunction(field),
ast::Expr::AccessorFunction(field),
) => {
let field = match field {
Accessor::RecordField(field) => IndexOrField::Field((*field).into()),
Accessor::TupleIndex(index) => IndexOrField::Index(index.parse().unwrap()),
};
let (loc_can_expr, can_output) = (
Loc::at(
loc_expr.region,
RecordAccessor(RecordAccessorData {
RecordAccessor(StructAccessorData {
name: *defined_symbol,
function_var: var_store.fresh(),
record_var: var_store.fresh(),
ext_var: var_store.fresh(),
closure_var: var_store.fresh(),
field_var: var_store.fresh(),
field: (*field).into(),
field,
}),
),
Output::default(),

View file

@ -81,6 +81,8 @@ enum IndexCtor<'a> {
Opaque,
/// Index a record type. The arguments are the types of the record fields.
Record(&'a [Lowercase]),
/// Index a tuple type.
Tuple,
/// Index a guard constructor. The arguments are a faux guard pattern, and then the real
/// pattern being guarded. E.g. `A B if g` becomes Guard { [True, (A B)] }.
Guard,
@ -113,6 +115,7 @@ impl<'a> IndexCtor<'a> {
}
RenderAs::Opaque => Self::Opaque,
RenderAs::Record(fields) => Self::Record(fields),
RenderAs::Tuple => Self::Tuple,
RenderAs::Guard => Self::Guard,
}
}
@ -366,6 +369,30 @@ fn sketch_pattern(pattern: &crate::pattern::Pattern) -> SketchedPattern {
SP::KnownCtor(union, tag_id, patterns)
}
TupleDestructure { destructs, .. } => {
let tag_id = TagId(0);
let mut patterns = std::vec::Vec::with_capacity(destructs.len());
for Loc {
value: destruct,
region: _,
} in destructs
{
patterns.push(sketch_pattern(&destruct.typ.1.value));
}
let union = Union {
render_as: RenderAs::Tuple,
alternatives: vec![Ctor {
name: CtorName::Tag(TagName("#Record".into())),
tag_id,
arity: destructs.len(),
}],
};
SP::KnownCtor(union, tag_id, patterns)
}
List {
patterns,
list_var: _,

View file

@ -19,12 +19,13 @@ use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel;
use roc_module::symbol::Symbol;
use roc_parse::ast::{self, Defs, StrLiteral};
use roc_parse::ident::Accessor;
use roc_parse::pattern::PatternType::*;
use roc_problem::can::{PrecedenceProblem, Problem, RuntimeError};
use roc_region::all::{Loc, Region};
use roc_types::num::SingleQuoteBound;
use roc_types::subs::{ExhaustiveMark, IllegalCycleMark, RedundantMark, VarStore, Variable};
use roc_types::types::{Alias, Category, LambdaSet, OptAbleVar, Type};
use roc_types::types::{Alias, Category, IndexOrField, LambdaSet, OptAbleVar, Type};
use std::fmt::{Debug, Display};
use std::{char, u32};
@ -186,8 +187,8 @@ pub enum Expr {
field: Lowercase,
},
/// field accessor as a function, e.g. (.foo) expr
RecordAccessor(RecordAccessorData),
/// tuple or field accessor as a function, e.g. (.foo) expr or (.1) expr
RecordAccessor(StructAccessorData),
TupleAccess {
tuple_var: Variable,
@ -197,9 +198,6 @@ pub enum Expr {
index: usize,
},
/// tuple accessor as a function, e.g. (.1) expr
TupleAccessor(TupleAccessorData),
RecordUpdate {
record_var: Variable,
ext_var: Variable,
@ -315,9 +313,8 @@ impl Expr {
Self::Record { .. } => Category::Record,
Self::EmptyRecord => Category::Record,
Self::RecordAccess { field, .. } => Category::RecordAccess(field.clone()),
Self::RecordAccessor(data) => Category::RecordAccessor(data.field.clone()),
Self::RecordAccessor(data) => Category::Accessor(data.field.clone()),
Self::TupleAccess { index, .. } => Category::TupleAccess(*index),
Self::TupleAccessor(data) => Category::TupleAccessor(data.index),
Self::RecordUpdate { .. } => Category::Record,
Self::Tag {
name, arguments, ..
@ -383,43 +380,30 @@ pub struct ClosureData {
pub loc_body: Box<Loc<Expr>>,
}
/// A tuple accessor like `.2`, which is equivalent to `\x -> x.2`
/// TupleAccessors are desugared to closures; they need to have a name
/// A record or tuple accessor like `.foo` or `.0`, which is equivalent to `\r -> r.foo`
/// Struct accessors are desugared to closures; they need to have a name
/// so the closure can have a correct lambda set.
///
/// We distinguish them from closures so we can have better error messages
/// during constraint generation.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TupleAccessorData {
pub name: Symbol,
pub function_var: Variable,
pub tuple_var: Variable,
pub closure_var: Variable,
pub ext_var: Variable,
pub elem_var: Variable,
pub index: usize,
}
/// A record accessor like `.foo`, which is equivalent to `\r -> r.foo`
/// RecordAccessors are desugared to closures; they need to have a name
/// so the closure can have a correct lambda set.
///
/// We distinguish them from closures so we can have better error messages
/// during constraint generation.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RecordAccessorData {
pub struct StructAccessorData {
pub name: Symbol,
pub function_var: Variable,
pub record_var: Variable,
pub closure_var: Variable,
pub ext_var: Variable,
pub field_var: Variable,
pub field: Lowercase,
/// Note that the `field` field is an `IndexOrField` in order to represent both
/// record and tuple accessors. This is different from `TupleAccess` and
/// `RecordAccess` (and RecordFields/TupleElems), which share less of their implementation.
pub field: IndexOrField,
}
impl RecordAccessorData {
impl StructAccessorData {
pub fn to_closure_data(self, record_symbol: Symbol) -> ClosureData {
let RecordAccessorData {
let StructAccessorData {
name,
function_var,
record_var,
@ -436,12 +420,21 @@ impl RecordAccessorData {
// into
//
// (\r -> r.foo)
let body = Expr::RecordAccess {
record_var,
ext_var,
field_var,
loc_expr: Box::new(Loc::at_zero(Expr::Var(record_symbol, record_var))),
field,
let body = match field {
IndexOrField::Index(index) => Expr::TupleAccess {
tuple_var: record_var,
ext_var,
elem_var: field_var,
loc_expr: Box::new(Loc::at_zero(Expr::Var(record_symbol, record_var))),
index,
},
IndexOrField::Field(field) => Expr::RecordAccess {
record_var,
ext_var,
field_var,
loc_expr: Box::new(Loc::at_zero(Expr::Var(record_symbol, record_var))),
field,
},
};
let loc_body = Loc::at_zero(body);
@ -1080,15 +1073,18 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::RecordAccessorFunction(field) => (
RecordAccessor(RecordAccessorData {
ast::Expr::AccessorFunction(field) => (
RecordAccessor(StructAccessorData {
name: scope.gen_unique_symbol(),
function_var: var_store.fresh(),
record_var: var_store.fresh(),
ext_var: var_store.fresh(),
closure_var: var_store.fresh(),
field_var: var_store.fresh(),
field: (*field).into(),
field: match field {
Accessor::RecordField(field) => IndexOrField::Field((*field).into()),
Accessor::TupleIndex(index) => IndexOrField::Index(index.parse().unwrap()),
},
}),
Output::default(),
),
@ -1106,18 +1102,6 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::TupleAccessorFunction(index) => (
TupleAccessor(TupleAccessorData {
name: scope.gen_unique_symbol(),
function_var: var_store.fresh(),
tuple_var: var_store.fresh(),
ext_var: var_store.fresh(),
closure_var: var_store.fresh(),
elem_var: var_store.fresh(),
index: index.parse().unwrap(),
}),
Output::default(),
),
ast::Expr::Tag(tag) => {
let variant_var = var_store.fresh();
let ext_var = var_store.fresh();
@ -1874,7 +1858,6 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
| other @ RuntimeError(_)
| other @ EmptyRecord
| other @ RecordAccessor { .. }
| other @ TupleAccessor { .. }
| other @ RecordUpdate { .. }
| other @ Var(..)
| other @ AbilityMember(..)
@ -3004,7 +2987,6 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
| Expr::Str(_)
| Expr::ZeroArgumentTag { .. }
| Expr::RecordAccessor(_)
| Expr::TupleAccessor(_)
| Expr::SingleQuote(..)
| Expr::EmptyRecord
| Expr::TypedHole(_)

View file

@ -918,6 +918,15 @@ fn fix_values_captured_in_closure_pattern(
}
}
}
TupleDestructure { destructs, .. } => {
for loc_destruct in destructs.iter_mut() {
fix_values_captured_in_closure_pattern(
&mut loc_destruct.value.typ.1.value,
no_capture_symbols,
closure_captures,
)
}
}
List { patterns, .. } => {
for loc_pat in patterns.patterns.iter_mut() {
fix_values_captured_in_closure_pattern(
@ -1087,8 +1096,7 @@ fn fix_values_captured_in_closure_expr(
| TypedHole { .. }
| RuntimeError(_)
| ZeroArgumentTag { .. }
| RecordAccessor { .. }
| TupleAccessor { .. } => {}
| RecordAccessor { .. } => {}
List { loc_elems, .. } => {
for elem in loc_elems.iter_mut() {

View file

@ -130,8 +130,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
| NonBase10Int { .. }
| Str(_)
| SingleQuote(_)
| RecordAccessorFunction(_)
| TupleAccessorFunction(_)
| AccessorFunction(_)
| Var { .. }
| Underscore { .. }
| MalformedIdent(_, _)

View file

@ -58,6 +58,11 @@ pub enum Pattern {
ext_var: Variable,
destructs: Vec<Loc<RecordDestruct>>,
},
TupleDestructure {
whole_var: Variable,
ext_var: Variable,
destructs: Vec<Loc<TupleDestruct>>,
},
List {
list_var: Variable,
elem_var: Variable,
@ -100,6 +105,7 @@ impl Pattern {
AppliedTag { whole_var, .. } => Some(*whole_var),
UnwrappedOpaque { whole_var, .. } => Some(*whole_var),
RecordDestructure { whole_var, .. } => Some(*whole_var),
TupleDestructure { whole_var, .. } => Some(*whole_var),
List {
list_var: whole_var,
..
@ -130,7 +136,21 @@ impl Pattern {
| UnsupportedPattern(..)
| MalformedPattern(..)
| AbilityMemberSpecialization { .. } => true,
RecordDestructure { destructs, .. } => destructs.is_empty(),
RecordDestructure { destructs, .. } => {
// If all destructs are surely exhaustive, then this is surely exhaustive.
destructs.iter().all(|d| match &d.value.typ {
DestructType::Required | DestructType::Optional(_, _) => false,
DestructType::Guard(_, pat) => pat.value.surely_exhaustive(),
})
}
TupleDestructure { destructs, .. } => {
// If all destructs are surely exhaustive, then this is surely exhaustive.
destructs
.iter()
.all(|d| d.value.typ.1.value.surely_exhaustive())
}
As(pattern, _identifier) => pattern.value.surely_exhaustive(),
List { patterns, .. } => patterns.surely_exhaustive(),
AppliedTag { .. }
@ -160,6 +180,7 @@ impl Pattern {
UnwrappedOpaque { opaque, .. } => C::Opaque(*opaque),
RecordDestructure { destructs, .. } if destructs.is_empty() => C::EmptyRecord,
RecordDestructure { .. } => C::Record,
TupleDestructure { .. } => C::Tuple,
List { .. } => C::List,
NumLiteral(..) => C::Num,
IntLiteral(..) => C::Int,
@ -215,6 +236,13 @@ pub struct RecordDestruct {
pub typ: DestructType,
}
#[derive(Clone, Debug)]
pub struct TupleDestruct {
pub var: Variable,
pub destruct_index: usize,
pub typ: (Variable, Loc<Pattern>),
}
#[derive(Clone, Debug)]
pub enum DestructType {
Required,
@ -554,8 +582,38 @@ pub fn canonicalize_pattern<'a>(
)
}
Tuple(_patterns) => {
todo!("canonicalize_pattern: Tuple")
Tuple(patterns) => {
let ext_var = var_store.fresh();
let whole_var = var_store.fresh();
let mut destructs = Vec::with_capacity(patterns.len());
for (i, loc_pattern) in patterns.iter().enumerate() {
let can_guard = canonicalize_pattern(
env,
var_store,
scope,
output,
pattern_type,
&loc_pattern.value,
loc_pattern.region,
permit_shadows,
);
destructs.push(Loc {
region: loc_pattern.region,
value: TupleDestruct {
destruct_index: i,
var: var_store.fresh(),
typ: (var_store.fresh(), can_guard),
},
});
}
Pattern::TupleDestructure {
whole_var,
ext_var,
destructs,
}
}
RecordDestructure(patterns) => {
@ -861,7 +919,8 @@ pub enum BindingsFromPattern<'a> {
pub enum BindingsFromPatternWork<'a> {
Pattern(&'a Loc<Pattern>),
Destruct(&'a Loc<RecordDestruct>),
RecordDestruct(&'a Loc<RecordDestruct>),
TupleDestruct(&'a Loc<TupleDestruct>),
}
impl<'a> BindingsFromPattern<'a> {
@ -911,8 +970,12 @@ impl<'a> BindingsFromPattern<'a> {
let (_, loc_arg) = &**argument;
stack.push(Pattern(loc_arg));
}
TupleDestructure { destructs, .. } => {
let it = destructs.iter().rev().map(TupleDestruct);
stack.extend(it);
}
RecordDestructure { destructs, .. } => {
let it = destructs.iter().rev().map(Destruct);
let it = destructs.iter().rev().map(RecordDestruct);
stack.extend(it);
}
NumLiteral(..)
@ -930,7 +993,7 @@ impl<'a> BindingsFromPattern<'a> {
}
}
}
BindingsFromPatternWork::Destruct(loc_destruct) => {
BindingsFromPatternWork::RecordDestruct(loc_destruct) => {
match &loc_destruct.value.typ {
DestructType::Required | DestructType::Optional(_, _) => {
return Some((loc_destruct.value.symbol, loc_destruct.region));
@ -941,6 +1004,10 @@ impl<'a> BindingsFromPattern<'a> {
}
}
}
BindingsFromPatternWork::TupleDestruct(loc_destruct) => {
let inner = &loc_destruct.value.typ.1;
stack.push(BindingsFromPatternWork::Pattern(inner))
}
}
}

View file

@ -9,9 +9,9 @@ use crate::{
def::{Annotation, Declaration, Def},
expr::{
self, AnnotatedMark, ClosureData, Declarations, Expr, Field, OpaqueWrapFunctionData,
RecordAccessorData, TupleAccessorData,
StructAccessorData,
},
pattern::{DestructType, Pattern, RecordDestruct},
pattern::{DestructType, Pattern, RecordDestruct, TupleDestruct},
};
macro_rules! visit_list {
@ -242,7 +242,7 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
record_var: _,
ext_var: _,
} => visitor.visit_expr(&loc_expr.value, loc_expr.region, *field_var),
Expr::RecordAccessor(RecordAccessorData { .. }) => { /* terminal */ }
Expr::RecordAccessor(StructAccessorData { .. }) => { /* terminal */ }
Expr::TupleAccess {
elem_var,
loc_expr,
@ -250,7 +250,6 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
tuple_var: _,
ext_var: _,
} => visitor.visit_expr(&loc_expr.value, loc_expr.region, *elem_var),
Expr::TupleAccessor(TupleAccessorData { .. }) => { /* terminal */ }
Expr::OpaqueWrapFunction(OpaqueWrapFunctionData { .. }) => { /* terminal */ }
Expr::RecordUpdate {
record_var: _,
@ -483,6 +482,16 @@ pub trait Visitor: Sized {
walk_record_destruct(self, destruct);
}
}
fn visit_tuple_destruct(&mut self, destruct: &TupleDestruct, region: Region) {
if self.should_visit(region) {
self.visit_pattern(
&destruct.typ.1.value,
destruct.typ.1.region,
Some(destruct.typ.0),
)
}
}
}
pub fn walk_pattern<V: Visitor>(visitor: &mut V, pattern: &Pattern) {
@ -503,6 +512,9 @@ pub fn walk_pattern<V: Visitor>(visitor: &mut V, pattern: &Pattern) {
RecordDestructure { destructs, .. } => destructs
.iter()
.for_each(|d| visitor.visit_record_destruct(&d.value, d.region)),
TupleDestructure { destructs, .. } => destructs
.iter()
.for_each(|d| visitor.visit_tuple_destruct(&d.value, d.region)),
List {
patterns, elem_var, ..
} => patterns