From 8f51188a8ec74febddec53826adf6d408ac1a3a8 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 7 Jan 2016 05:49:46 -0500 Subject: [PATCH] Factor mir::[Mut]Visitor implementations into a common macro. --- src/librustc/mir/repr.rs | 17 + src/librustc/mir/visit.rs | 720 ++++++++++++++------------------------ 2 files changed, 281 insertions(+), 456 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index b97d5939cf3b0..2bdaa22269aac 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -20,6 +20,7 @@ use syntax::codemap::Span; use std::borrow::{Cow, IntoCow}; use std::fmt::{self, Debug, Formatter, Write}; use std::{iter, u32}; +use std::ops::{Index, IndexMut}; /// Lowered representation of a single function. #[derive(RustcEncodable, RustcDecodable)] @@ -67,6 +68,22 @@ impl<'tcx> Mir<'tcx> { } } +impl<'tcx> Index for Mir<'tcx> { + type Output = BasicBlockData<'tcx>; + + #[inline] + fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { + self.basic_block_data(index) + } +} + +impl<'tcx> IndexMut for Mir<'tcx> { + #[inline] + fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { + self.basic_block_data_mut(index) + } +} + /////////////////////////////////////////////////////////////////////////// // Mutability and borrow kinds diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c05e4c83cd4f0..7c8ea22de8e92 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -14,545 +14,353 @@ use mir::repr::*; use rustc_data_structures::tuple_slice::TupleSlice; use syntax::codemap::Span; -pub trait Visitor<'tcx> { - // Override these, and call `self.super_xxx` to revert back to the - // default behavior. +macro_rules! make_mir_visitor { + ($visitor_trait_name:ident, $($mutability:ident)*) => { + pub trait $visitor_trait_name<'tcx> { + // Override these, and call `self.super_xxx` to revert back to the + // default behavior. - fn visit_mir(&mut self, mir: &Mir<'tcx>) { - self.super_mir(mir); - } - - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { - self.super_basic_block_data(block, data); - } - - fn visit_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { - self.super_statement(block, statement); - } - - fn visit_assign(&mut self, block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { - self.super_assign(block, lvalue, rvalue); - } - - fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { - self.super_terminator(block, terminator); - } - - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { - self.super_rvalue(rvalue); - } - - fn visit_operand(&mut self, operand: &Operand<'tcx>) { - self.super_operand(operand); - } - - fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) { - self.super_lvalue(lvalue, context); - } - - fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { - self.super_branch(source, target); - } - - fn visit_constant(&mut self, constant: &Constant<'tcx>) { - self.super_constant(constant); - } - - fn visit_literal(&mut self, literal: &Literal<'tcx>) { - self.super_literal(literal); - } - - fn visit_def_id(&mut self, def_id: DefId) { - self.super_def_id(def_id); - } - - fn visit_span(&mut self, span: Span) { - self.super_span(span); - } - - // The `super_xxx` methods comprise the default behavior and are - // not meant to be overidden. - - fn super_mir(&mut self, mir: &Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = mir.basic_block_data(block); - self.visit_basic_block_data(block, data); - } - } - - fn super_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { - for statement in &data.statements { - self.visit_statement(block, statement); - } - data.terminator.as_ref().map(|r| self.visit_terminator(block, r)); - } - - fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { - self.visit_span(statement.span); - - match statement.kind { - StatementKind::Assign(ref lvalue, ref rvalue) => { - self.visit_assign(block, lvalue, rvalue); + fn visit_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { + self.super_mir(mir); } - StatementKind::Drop(_, ref lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Drop); - } - } - } - fn super_assign(&mut self, _block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { - self.visit_lvalue(lvalue, LvalueContext::Store); - self.visit_rvalue(rvalue); - } - - fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { - match *terminator { - Terminator::Goto { target } => { - self.visit_branch(block, target); + fn visit_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)* BasicBlockData<'tcx>) { + self.super_basic_block_data(block, data); } - Terminator::If { ref cond, ref targets } => { - self.visit_operand(cond); - for &target in targets.as_slice() { - self.visit_branch(block, target); - } + fn visit_statement(&mut self, + block: BasicBlock, + statement: & $($mutability)* Statement<'tcx>) { + self.super_statement(block, statement); } - Terminator::Switch { ref discr, adt_def: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); - } + fn visit_assign(&mut self, + block: BasicBlock, + lvalue: & $($mutability)* Lvalue<'tcx>, + rvalue: & $($mutability)* Rvalue<'tcx>) { + self.super_assign(block, lvalue, rvalue); } - Terminator::SwitchInt { ref discr, switch_ty: _, values: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); - } + fn visit_terminator(&mut self, + block: BasicBlock, + terminator: & $($mutability)* Terminator<'tcx>) { + self.super_terminator(block, terminator); } - Terminator::Resume | - Terminator::Return => { + fn visit_rvalue(&mut self, + rvalue: & $($mutability)* Rvalue<'tcx>) { + self.super_rvalue(rvalue); } - Terminator::Call { ref func, ref args, ref kind } => { - if let Some(ref destination) = kind.destination() { - self.visit_lvalue(destination, LvalueContext::Store); - } - self.visit_operand(func); - for arg in args { - self.visit_operand(arg); - } - for &target in kind.successors() { - self.visit_branch(block, target); - } + fn visit_operand(&mut self, + operand: & $($mutability)* Operand<'tcx>) { + self.super_operand(operand); } - } - } - fn super_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { - match *rvalue { - Rvalue::Use(ref operand) => { - self.visit_operand(operand); + fn visit_lvalue(&mut self, + lvalue: & $($mutability)* Lvalue<'tcx>, + context: LvalueContext) { + self.super_lvalue(lvalue, context); } - Rvalue::Repeat(ref value, ref len) => { - self.visit_operand(value); - self.visit_constant(len); + fn visit_branch(&mut self, + source: BasicBlock, + target: BasicBlock) { + self.super_branch(source, target); } - Rvalue::Ref(r, bk, ref path) => { - self.visit_lvalue(path, LvalueContext::Borrow { - region: r, - kind: bk - }); + fn visit_constant(&mut self, + constant: & $($mutability)* Constant<'tcx>) { + self.super_constant(constant); } - Rvalue::Len(ref path) => { - self.visit_lvalue(path, LvalueContext::Inspect); + fn visit_literal(&mut self, + literal: & $($mutability)* Literal<'tcx>) { + self.super_literal(literal); } - Rvalue::Cast(_, ref operand, _) => { - self.visit_operand(operand); + fn visit_def_id(&mut self, + def_id: & $($mutability)* DefId) { + self.super_def_id(def_id); } - Rvalue::BinaryOp(_, ref lhs, ref rhs) => { - self.visit_operand(lhs); - self.visit_operand(rhs); + fn visit_span(&mut self, + span: & $($mutability)* Span) { + self.super_span(span); } - Rvalue::UnaryOp(_, ref op) => { - self.visit_operand(op); - } + // The `super_xxx` methods comprise the default behavior and are + // not meant to be overidden. - Rvalue::Box(_) => { + fn super_mir(&mut self, + mir: & $($mutability)* Mir<'tcx>) { + for block in mir.all_basic_blocks() { + let data = & $($mutability)* mir[block]; + self.visit_basic_block_data(block, data); + } } - Rvalue::Aggregate(_, ref operands) => { - for operand in operands { - self.visit_operand(operand); + fn super_basic_block_data(&mut self, + block: BasicBlock, + data: & $($mutability)* BasicBlockData<'tcx>) { + for statement in & $($mutability)* data.statements { + self.visit_statement(block, statement); } - } - Rvalue::Slice { ref input, from_start, from_end } => { - self.visit_lvalue(input, LvalueContext::Slice { - from_start: from_start, - from_end: from_end, - }); + if let Some(ref $($mutability)* terminator) = data.terminator { + self.visit_terminator(block, terminator); + } } - Rvalue::InlineAsm(_) => { - } - } - } + fn super_statement(&mut self, + block: BasicBlock, + statement: & $($mutability)* Statement<'tcx>) { + self.visit_span(& $($mutability)* statement.span); - fn super_operand(&mut self, operand: &Operand<'tcx>) { - match *operand { - Operand::Consume(ref lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Consume); - } - Operand::Constant(ref constant) => { - self.visit_constant(constant); - } - } - } - - fn super_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: LvalueContext) { - match *lvalue { - Lvalue::Var(_) | - Lvalue::Temp(_) | - Lvalue::Arg(_) | - Lvalue::Static(_) | - Lvalue::ReturnPointer => { - } - Lvalue::Projection(ref proj) => { - self.visit_lvalue(&proj.base, LvalueContext::Projection); + match statement.kind { + StatementKind::Assign(ref $($mutability)* lvalue, + ref $($mutability)* rvalue) => { + self.visit_assign(block, lvalue, rvalue); + } + StatementKind::Drop(_, ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Drop); + } + } } - } - } - - fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { - } - - fn super_constant(&mut self, constant: &Constant<'tcx>) { - self.visit_span(constant.span); - self.visit_literal(&constant.literal); - } - fn super_literal(&mut self, literal: &Literal<'tcx>) { - match *literal { - Literal::Item { def_id, .. } => { - self.visit_def_id(def_id); - }, - Literal::Value { .. } => { - // Nothing to do + fn super_assign(&mut self, + _block: BasicBlock, + lvalue: &$($mutability)* Lvalue<'tcx>, + rvalue: &$($mutability)* Rvalue<'tcx>) { + self.visit_lvalue(lvalue, LvalueContext::Store); + self.visit_rvalue(rvalue); } - } - } - - fn super_def_id(&mut self, _def_id: DefId) { - } - - fn super_span(&mut self, _span: Span) { - } -} - -#[derive(Copy, Clone, Debug)] -pub enum LvalueContext { - // Appears as LHS of an assignment or as dest of a call - Store, - - // Being dropped - Drop, - - // Being inspected in some way, like loading a len - Inspect, - - // Being borrowed - Borrow { region: Region, kind: BorrowKind }, - - // Being sliced -- this should be same as being borrowed, probably - Slice { from_start: usize, from_end: usize }, - - // Used as base for another lvalue, e.g. `x` in `x.y` - Projection, - - // Consumed as part of an operand - Consume, -} - -pub trait MutVisitor<'tcx> { - // Override these, and call `self.super_xxx` to revert back to the - // default behavior. - - fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { - self.super_mir(mir); - } - - fn visit_basic_block_data(&mut self, - block: BasicBlock, - data: &mut BasicBlockData<'tcx>) { - self.super_basic_block_data(block, data); - } - fn visit_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>) { - self.super_statement(block, statement); - } - - fn visit_assign(&mut self, - block: BasicBlock, - lvalue: &mut Lvalue<'tcx>, - rvalue: &mut Rvalue<'tcx>) { - self.super_assign(block, lvalue, rvalue); - } - - fn visit_terminator(&mut self, - block: BasicBlock, - terminator: &mut Terminator<'tcx>) { - self.super_terminator(block, terminator); - } - - fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>) { - self.super_rvalue(rvalue); - } + fn super_terminator(&mut self, + block: BasicBlock, + terminator: &$($mutability)* Terminator<'tcx>) { + match *terminator { + Terminator::Goto { target } => { + self.visit_branch(block, target); + } - fn visit_operand(&mut self, operand: &mut Operand<'tcx>) { - self.super_operand(operand); - } + Terminator::If { ref $($mutability)* cond, + ref $($mutability)* targets } => { + self.visit_operand(cond); + for &target in targets.as_slice() { + self.visit_branch(block, target); + } + } - fn visit_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - context: LvalueContext) { - self.super_lvalue(lvalue, context); - } + Terminator::Switch { ref $($mutability)* discr, + adt_def: _, + ref targets } => { + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } - fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { - self.super_branch(source, target); - } + Terminator::SwitchInt { ref $($mutability)* discr, + switch_ty: _, + values: _, + ref targets } => { + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } - fn visit_constant(&mut self, constant: &mut Constant<'tcx>) { - self.super_constant(constant); - } + Terminator::Resume | + Terminator::Return => { + } - fn visit_literal(&mut self, literal: &mut Literal<'tcx>) { - self.super_literal(literal); - } + Terminator::Call { ref $($mutability)* func, + ref $($mutability)* args, + ref $($mutability)* kind } => { + self.visit_operand(func); + for arg in args { + self.visit_operand(arg); + } + match *kind { + CallKind::Converging { + ref $($mutability)* destination, + .. + } | + CallKind::ConvergingCleanup { + ref $($mutability)* destination, + .. + } => { + self.visit_lvalue(destination, LvalueContext::Store); + } + CallKind::Diverging | + CallKind::DivergingCleanup(_) => {} + } + for &target in kind.successors() { + self.visit_branch(block, target); + } + } + } + } - fn visit_def_id(&mut self, def_id: &mut DefId) { - self.super_def_id(def_id); - } + fn super_rvalue(&mut self, + rvalue: & $($mutability)* Rvalue<'tcx>) { + match *rvalue { + Rvalue::Use(ref $($mutability)* operand) => { + self.visit_operand(operand); + } - fn visit_span(&mut self, span: &mut Span) { - self.super_span(span); - } + Rvalue::Repeat(ref $($mutability)* value, + ref $($mutability)* len) => { + self.visit_operand(value); + self.visit_constant(len); + } - // The `super_xxx` methods comprise the default behavior and are - // not meant to be overidden. + Rvalue::Ref(r, bk, ref $($mutability)* path) => { + self.visit_lvalue(path, LvalueContext::Borrow { + region: r, + kind: bk + }); + } - fn super_mir(&mut self, mir: &mut Mir<'tcx>) { - for block in mir.all_basic_blocks() { - let data = mir.basic_block_data_mut(block); - self.visit_basic_block_data(block, data); - } - } + Rvalue::Len(ref $($mutability)* path) => { + self.visit_lvalue(path, LvalueContext::Inspect); + } - fn super_basic_block_data(&mut self, - block: BasicBlock, - data: &mut BasicBlockData<'tcx>) { - for statement in &mut data.statements { - self.visit_statement(block, statement); - } - data.terminator.as_mut().map(|r| self.visit_terminator(block, r)); - } + Rvalue::Cast(_, ref $($mutability)* operand, _) => { + self.visit_operand(operand); + } - fn super_statement(&mut self, - block: BasicBlock, - statement: &mut Statement<'tcx>) { - self.visit_span(&mut statement.span); + Rvalue::BinaryOp(_, + ref $($mutability)* lhs, + ref $($mutability)* rhs) => { + self.visit_operand(lhs); + self.visit_operand(rhs); + } - match statement.kind { - StatementKind::Assign(ref mut lvalue, ref mut rvalue) => { - self.visit_assign(block, lvalue, rvalue); - } - StatementKind::Drop(_, ref mut lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Drop); - } - } - } + Rvalue::UnaryOp(_, ref $($mutability)* op) => { + self.visit_operand(op); + } - fn super_assign(&mut self, - _block: BasicBlock, - lvalue: &mut Lvalue<'tcx>, - rvalue: &mut Rvalue<'tcx>) { - self.visit_lvalue(lvalue, LvalueContext::Store); - self.visit_rvalue(rvalue); - } + Rvalue::Box(_) => { + } - fn super_terminator(&mut self, - block: BasicBlock, - terminator: &mut Terminator<'tcx>) { - match *terminator { - Terminator::Goto { target } => { - self.visit_branch(block, target); - } + Rvalue::Aggregate(ref $($mutability)* kind, + ref $($mutability)* operands) => { + match *kind { + AggregateKind::Closure(ref $($mutability)* def_id, _) => { + self.visit_def_id(def_id); + } + _ => { /* nothing to do */ } + } + + for operand in & $($mutability)* operands[..] { + self.visit_operand(operand); + } + } - Terminator::If { ref mut cond, ref mut targets } => { - self.visit_operand(cond); - for &target in targets.as_slice() { - self.visit_branch(block, target); - } - } + Rvalue::Slice { ref $($mutability)* input, + from_start, + from_end } => { + self.visit_lvalue(input, LvalueContext::Slice { + from_start: from_start, + from_end: from_end, + }); + } - Terminator::Switch { ref mut discr, adt_def: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); + Rvalue::InlineAsm(_) => { + } } } - Terminator::SwitchInt { ref mut discr, switch_ty: _, values: _, ref targets } => { - self.visit_lvalue(discr, LvalueContext::Inspect); - for &target in targets { - self.visit_branch(block, target); + fn super_operand(&mut self, + operand: & $($mutability)* Operand<'tcx>) { + match *operand { + Operand::Consume(ref $($mutability)* lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Consume); + } + Operand::Constant(ref $($mutability)* constant) => { + self.visit_constant(constant); + } } } - Terminator::Resume | - Terminator::Return => { - } - - Terminator::Call { ref mut func, ref mut args, ref mut kind } => { - if let Some(ref mut destination) = kind.destination() { - self.visit_lvalue(destination, LvalueContext::Store); - } - self.visit_operand(func); - for arg in args { - self.visit_operand(arg); - } - for &target in kind.successors() { - self.visit_branch(block, target); + fn super_lvalue(&mut self, + lvalue: & $($mutability)* Lvalue<'tcx>, + _context: LvalueContext) { + match *lvalue { + Lvalue::Var(_) | + Lvalue::Temp(_) | + Lvalue::Arg(_) | + Lvalue::ReturnPointer => { + } + Lvalue::Static(ref $($mutability)* def_id) => { + self.visit_def_id(def_id); + } + Lvalue::Projection(ref $($mutability)* proj) => { + self.visit_lvalue(& $($mutability)* proj.base, + LvalueContext::Projection); + } } } - } - } - - fn super_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>) { - match *rvalue { - Rvalue::Use(ref mut operand) => { - self.visit_operand(operand); - } - - Rvalue::Repeat(ref mut value, ref mut len) => { - self.visit_operand(value); - self.visit_constant(len); - } - - Rvalue::Ref(r, bk, ref mut path) => { - self.visit_lvalue(path, LvalueContext::Borrow { - region: r, - kind: bk - }); - } - - Rvalue::Len(ref mut path) => { - self.visit_lvalue(path, LvalueContext::Inspect); - } - - Rvalue::Cast(_, ref mut operand, _) => { - self.visit_operand(operand); - } - - Rvalue::BinaryOp(_, ref mut lhs, ref mut rhs) => { - self.visit_operand(lhs); - self.visit_operand(rhs); - } - Rvalue::UnaryOp(_, ref mut op) => { - self.visit_operand(op); + fn super_branch(&mut self, + _source: BasicBlock, + _target: BasicBlock) { } - Rvalue::Box(_) => { + fn super_constant(&mut self, + constant: & $($mutability)* Constant<'tcx>) { + self.visit_span(& $($mutability)* constant.span); + self.visit_literal(& $($mutability)* constant.literal); } - Rvalue::Aggregate(ref mut kind, ref mut operands) => { - match *kind { - AggregateKind::Closure(ref mut def_id, _) => { + fn super_literal(&mut self, + literal: & $($mutability)* Literal<'tcx>) { + match *literal { + Literal::Item { ref $($mutability)* def_id, .. } => { self.visit_def_id(def_id); + }, + Literal::Value { .. } => { + // Nothing to do } - _ => { /* nothing to do */ } - } - - for operand in &mut operands[..] { - self.visit_operand(operand); } } - Rvalue::Slice { ref mut input, from_start, from_end } => { - self.visit_lvalue(input, LvalueContext::Slice { - from_start: from_start, - from_end: from_end, - }); + fn super_def_id(&mut self, _def_id: & $($mutability)* DefId) { } - Rvalue::InlineAsm(_) => { + fn super_span(&mut self, _span: & $($mutability)* Span) { } } } +} - fn super_operand(&mut self, operand: &mut Operand<'tcx>) { - match *operand { - Operand::Consume(ref mut lvalue) => { - self.visit_lvalue(lvalue, LvalueContext::Consume); - } - Operand::Constant(ref mut constant) => { - self.visit_constant(constant); - } - } - } +make_mir_visitor!(Visitor,); +make_mir_visitor!(MutVisitor,mut); - fn super_lvalue(&mut self, - lvalue: &mut Lvalue<'tcx>, - _context: LvalueContext) { - match *lvalue { - Lvalue::Var(_) | - Lvalue::Temp(_) | - Lvalue::Arg(_) | - Lvalue::ReturnPointer => { - } - Lvalue::Static(ref mut def_id) => { - self.visit_def_id(def_id); - } - Lvalue::Projection(ref mut proj) => { - self.visit_lvalue(&mut proj.base, LvalueContext::Projection); - } - } - } +#[derive(Copy, Clone, Debug)] +pub enum LvalueContext { + // Appears as LHS of an assignment or as dest of a call + Store, - fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { - } + // Being dropped + Drop, - fn super_constant(&mut self, constant: &mut Constant<'tcx>) { - self.visit_span(&mut constant.span); - self.visit_literal(&mut constant.literal); - } + // Being inspected in some way, like loading a len + Inspect, - fn super_literal(&mut self, literal: &mut Literal<'tcx>) { - match *literal { - Literal::Item { ref mut def_id, .. } => { - self.visit_def_id(def_id); - }, - Literal::Value { .. } => { - // Nothing to do - } - } - } + // Being borrowed + Borrow { region: Region, kind: BorrowKind }, - fn super_def_id(&mut self, _def_id: &mut DefId) { - } + // Being sliced -- this should be same as being borrowed, probably + Slice { from_start: usize, from_end: usize }, - fn super_span(&mut self, _span: &mut Span) { - } + // Used as base for another lvalue, e.g. `x` in `x.y` + Projection, + + // Consumed as part of an operand + Consume, }