| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548 |
- // SPDX-License-Identifier: Apache-2.0
- use num_bigint::BigInt;
- use super::encoding::{abi_decode, abi_encode};
- use super::expression::{assign_single, default_gas, emit_function_call, expression};
- use super::Options;
- use super::{
- cfg::{ControlFlowGraph, Instr},
- vartable::Vartable,
- };
- use crate::codegen::constructor::call_constructor;
- use crate::codegen::events::new_event_emitter;
- use crate::codegen::unused_variable::{
- should_remove_assignment, should_remove_variable, SideEffectsCheckParameters,
- };
- use crate::codegen::yul::inline_assembly_cfg;
- use crate::codegen::Expression;
- use crate::sema::ast::{
- self, ArrayLength, CallTy, DestructureField, Function, Namespace, RetrieveType, Statement,
- TryCatch, Type, Type::Uint,
- };
- use crate::sema::Recurse;
- use num_traits::Zero;
- use solang_parser::pt::{self, CodeLocation, Loc::Codegen};
- /// Resolve a statement, which might be a block of statements or an entire body of a function
- pub(crate) fn statement(
- stmt: &Statement,
- func: &Function,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- ns: &Namespace,
- vartab: &mut Vartable,
- loops: &mut LoopScopes,
- placeholder: Option<&Instr>,
- return_override: Option<&Instr>,
- opt: &Options,
- ) {
- match stmt {
- Statement::Block { statements, .. } => {
- for stmt in statements {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- }
- }
- Statement::VariableDecl(loc, pos, _, Some(init)) => {
- if should_remove_variable(*pos, func, opt) {
- let mut params = SideEffectsCheckParameters {
- cfg,
- contract_no,
- func: Some(func),
- ns,
- vartab,
- opt,
- };
- //If we remove the assignment, we must keep expressions that have side effects
- init.recurse(&mut params, process_side_effects_expressions);
- return;
- }
- let mut expression = expression(init, cfg, contract_no, Some(func), ns, vartab, opt);
- // Let's check if the declaration is a declaration of a dynamic array
- if let Expression::AllocDynamicBytes {
- loc: loc_dyn_arr,
- ty: ty_dyn_arr @ Type::Array(..),
- size,
- initializer: opt,
- } = expression
- {
- let temp_res = vartab.temp_name("array_length", &Uint(32));
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: temp_res,
- expr: *size,
- },
- );
- // If expression is an AllocDynamic array, replace the expression with AllocDynamicArray(_,_,tempvar,_) to avoid inserting size twice in the cfg
- expression = Expression::AllocDynamicBytes {
- loc: loc_dyn_arr,
- ty: ty_dyn_arr,
- size: Box::new(Expression::Variable {
- loc: *loc,
- ty: Uint(32),
- var_no: temp_res,
- }),
- initializer: opt,
- };
- cfg.array_lengths_temps.insert(*pos, temp_res);
- } else if let Expression::Variable { var_no, .. } = &expression {
- // If declaration happens with an existing array, check if the size of the array is known.
- // If the size of the right hand side is known (is in the array_length_map), make the left hand side track it
- // Now, we will have two keys in the map that point to the same temporary variable
- if let Some(to_add) = cfg.array_lengths_temps.clone().get(var_no) {
- cfg.array_lengths_temps.insert(*pos, *to_add);
- }
- }
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *pos,
- expr: expression,
- },
- );
- }
- Statement::VariableDecl(loc, pos, param, None) => {
- if should_remove_variable(*pos, func, opt) {
- return;
- }
- // Add variable as undefined
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: *pos,
- expr: Expression::Undefined {
- ty: param.ty.clone(),
- },
- },
- );
- // Handling arrays without size, defaulting the initial size with zero
- if matches!(param.ty, Type::Array(..)) {
- let num = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: BigInt::zero(),
- };
- let temp_res = vartab.temp_name("array_length", &Uint(32));
- cfg.add(
- vartab,
- Instr::Set {
- loc: *loc,
- res: temp_res,
- expr: num,
- },
- );
- cfg.array_lengths_temps.insert(*pos, temp_res);
- }
- }
- Statement::Return(_, expr) => {
- if let Some(return_instr) = return_override {
- cfg.add(vartab, return_instr.clone());
- } else {
- match expr {
- None => cfg.add(vartab, Instr::Return { value: Vec::new() }),
- Some(expr) => returns(expr, cfg, contract_no, func, ns, vartab, opt),
- }
- }
- }
- Statement::Expression(_, reachable, expr) => {
- if let ast::Expression::Assign { left, right, .. } = &expr {
- if should_remove_assignment(ns, left, func, opt) {
- let mut params = SideEffectsCheckParameters {
- cfg,
- contract_no,
- func: Some(func),
- ns,
- vartab,
- opt,
- };
- right.recurse(&mut params, process_side_effects_expressions);
- if !reachable {
- cfg.add(vartab, Instr::Unreachable);
- }
- return;
- }
- }
- let _ = expression(expr, cfg, contract_no, Some(func), ns, vartab, opt);
- if !reachable {
- cfg.add(vartab, Instr::Unreachable);
- }
- }
- Statement::Delete(_, ty, expr) => {
- let var_expr = expression(expr, cfg, contract_no, Some(func), ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::ClearStorage {
- ty: ty.clone(),
- storage: var_expr,
- },
- );
- }
- Statement::Break(_) => {
- cfg.add(
- vartab,
- Instr::Branch {
- block: loops.do_break(),
- },
- );
- }
- Statement::Continue(_) => {
- cfg.add(
- vartab,
- Instr::Branch {
- block: loops.do_continue(),
- },
- );
- }
- Statement::If(_, _, cond, then_stmt, else_stmt) if else_stmt.is_empty() => {
- if_then(
- cond,
- then_stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- }
- Statement::If(_, _, cond, then_stmt, else_stmt) => if_then_else(
- cond,
- then_stmt,
- else_stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- ),
- Statement::DoWhile(_, _, body_stmt, cond_expr) => {
- let body = cfg.new_basic_block("body".to_string());
- let cond = cfg.new_basic_block("conf".to_string());
- let end = cfg.new_basic_block("enddowhile".to_string());
- cfg.add(vartab, Instr::Branch { block: body });
- cfg.set_basic_block(body);
- vartab.new_dirty_tracker();
- loops.new_scope(end, cond);
- let mut body_reachable = true;
- for stmt in body_stmt {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- body_reachable = stmt.reachable();
- }
- if body_reachable {
- cfg.add(vartab, Instr::Branch { block: cond });
- }
- cfg.set_basic_block(cond);
- let cond_expr = expression(cond_expr, cfg, contract_no, Some(func), ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: cond_expr,
- true_block: body,
- false_block: end,
- },
- );
- let set = vartab.pop_dirty_tracker();
- cfg.set_phis(end, set.clone());
- cfg.set_phis(body, set.clone());
- cfg.set_phis(cond, set);
- cfg.set_basic_block(end);
- }
- Statement::While(_, _, cond_expr, body_stmt) => {
- let cond = cfg.new_basic_block("cond".to_string());
- let body = cfg.new_basic_block("body".to_string());
- let end = cfg.new_basic_block("endwhile".to_string());
- cfg.add(vartab, Instr::Branch { block: cond });
- cfg.set_basic_block(cond);
- let cond_expr = expression(cond_expr, cfg, contract_no, Some(func), ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: cond_expr,
- true_block: body,
- false_block: end,
- },
- );
- cfg.set_basic_block(body);
- vartab.new_dirty_tracker();
- loops.new_scope(end, cond);
- let mut body_reachable = true;
- for stmt in body_stmt {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- body_reachable = stmt.reachable();
- }
- if body_reachable {
- cfg.add(vartab, Instr::Branch { block: cond });
- }
- loops.leave_scope();
- let set = vartab.pop_dirty_tracker();
- cfg.set_phis(end, set.clone());
- cfg.set_phis(cond, set);
- cfg.set_basic_block(end);
- }
- Statement::For {
- init,
- cond: None,
- next,
- body,
- ..
- } => {
- let body_block = cfg.new_basic_block("body".to_string());
- let next_block = cfg.new_basic_block("next".to_string());
- let end_block = cfg.new_basic_block("endfor".to_string());
- for stmt in init {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- }
- cfg.add(vartab, Instr::Branch { block: body_block });
- cfg.set_basic_block(body_block);
- loops.new_scope(
- end_block,
- if next.is_empty() {
- body_block
- } else {
- next_block
- },
- );
- vartab.new_dirty_tracker();
- let mut body_reachable = true;
- for stmt in body {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- body_reachable = stmt.reachable();
- }
- if body_reachable {
- cfg.add(vartab, Instr::Branch { block: next_block });
- }
- loops.leave_scope();
- if body_reachable {
- cfg.set_basic_block(next_block);
- if !next.is_empty() {
- for stmt in next {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- body_reachable = stmt.reachable();
- }
- }
- if body_reachable {
- cfg.add(vartab, Instr::Branch { block: body_block });
- }
- }
- let set = vartab.pop_dirty_tracker();
- cfg.set_phis(next_block, set.clone());
- cfg.set_phis(body_block, set.clone());
- cfg.set_phis(end_block, set);
- cfg.set_basic_block(end_block);
- }
- Statement::For {
- init,
- cond: Some(cond_expr),
- next,
- body,
- ..
- } => {
- let body_block = cfg.new_basic_block("body".to_string());
- let cond_block = cfg.new_basic_block("cond".to_string());
- let next_block = cfg.new_basic_block("next".to_string());
- let end_block = cfg.new_basic_block("endfor".to_string());
- for stmt in init {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- }
- cfg.add(vartab, Instr::Branch { block: cond_block });
- cfg.set_basic_block(cond_block);
- let cond_expr = expression(cond_expr, cfg, contract_no, Some(func), ns, vartab, opt);
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: cond_expr,
- true_block: body_block,
- false_block: end_block,
- },
- );
- cfg.set_basic_block(body_block);
- // continue goes to next
- loops.new_scope(end_block, next_block);
- vartab.new_dirty_tracker();
- let mut body_reachable = true;
- for stmt in body {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- body_reachable = stmt.reachable();
- }
- if body_reachable {
- cfg.add(vartab, Instr::Branch { block: next_block });
- }
- loops.leave_scope();
- cfg.set_basic_block(next_block);
- let mut next_reachable = true;
- for stmt in next {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- next_reachable = stmt.reachable();
- }
- if next_reachable {
- cfg.add(vartab, Instr::Branch { block: cond_block });
- }
- cfg.set_basic_block(end_block);
- let set = vartab.pop_dirty_tracker();
- cfg.set_phis(next_block, set.clone());
- cfg.set_phis(end_block, set.clone());
- cfg.set_phis(cond_block, set);
- }
- Statement::Destructure(_, fields, expr) => {
- destructure(fields, expr, cfg, contract_no, func, ns, vartab, opt)
- }
- Statement::TryCatch(_, _, try_stmt) => try_catch(
- try_stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- ),
- Statement::Emit {
- loc,
- event_no,
- args,
- ..
- } => {
- let emitter = new_event_emitter(loc, *event_no, args, ns);
- emitter.emit(contract_no, func, cfg, vartab, opt);
- }
- Statement::Underscore(_) => {
- // ensure we get phi nodes for the return values
- if let Some(instr @ Instr::Call { res, .. }) = placeholder {
- for var_no in res {
- vartab.set_dirty(*var_no);
- }
- cfg.add(vartab, instr.clone());
- } else {
- panic!("placeholder should be provided for modifiers");
- }
- }
- Statement::Assembly(inline_assembly, ..) => {
- inline_assembly_cfg(inline_assembly, contract_no, ns, cfg, vartab, opt);
- }
- }
- }
- /// Generate if-then-no-else
- fn if_then(
- cond: &ast::Expression,
- then_stmt: &[Statement],
- func: &Function,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- ns: &Namespace,
- vartab: &mut Vartable,
- loops: &mut LoopScopes,
- placeholder: Option<&Instr>,
- return_override: Option<&Instr>,
- opt: &Options,
- ) {
- let cond = expression(cond, cfg, contract_no, Some(func), ns, vartab, opt);
- let then = cfg.new_basic_block("then".to_string());
- let endif = cfg.new_basic_block("endif".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: then,
- false_block: endif,
- },
- );
- cfg.set_basic_block(then);
- vartab.new_dirty_tracker();
- let mut reachable = true;
- for stmt in then_stmt {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- reachable = stmt.reachable();
- }
- if reachable {
- cfg.add(vartab, Instr::Branch { block: endif });
- }
- cfg.set_phis(endif, vartab.pop_dirty_tracker());
- cfg.set_basic_block(endif);
- }
- /// Generate if-then-else
- fn if_then_else(
- cond: &ast::Expression,
- then_stmt: &[Statement],
- else_stmt: &[Statement],
- func: &Function,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- ns: &Namespace,
- vartab: &mut Vartable,
- loops: &mut LoopScopes,
- placeholder: Option<&Instr>,
- return_override: Option<&Instr>,
- opt: &Options,
- ) {
- let cond = expression(cond, cfg, contract_no, Some(func), ns, vartab, opt);
- let then = cfg.new_basic_block("then".to_string());
- let else_ = cfg.new_basic_block("else".to_string());
- let endif = cfg.new_basic_block("endif".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: then,
- false_block: else_,
- },
- );
- // then
- cfg.set_basic_block(then);
- vartab.new_dirty_tracker();
- let mut then_reachable = true;
- for stmt in then_stmt {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- then_reachable = stmt.reachable();
- }
- if then_reachable {
- cfg.add(vartab, Instr::Branch { block: endif });
- }
- // else
- cfg.set_basic_block(else_);
- let mut else_reachable = true;
- for stmt in else_stmt {
- statement(
- stmt,
- func,
- cfg,
- contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- else_reachable = stmt.reachable();
- }
- if else_reachable {
- cfg.add(vartab, Instr::Branch { block: endif });
- }
- cfg.set_phis(endif, vartab.pop_dirty_tracker());
- cfg.set_basic_block(endif);
- }
- fn returns(
- expr: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: &Function,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) {
- // Can only be another function call without returns
- let uncast_values = match expr {
- // Explicitly recurse for conditinal operator expressions.
- // `return a ? b : c` is transformed into pseudo code `a ? return b : return c`
- ast::Expression::ConditionalOperator {
- cond,
- true_option: left,
- false_option: right,
- ..
- } => {
- let cond = expression(cond, cfg, contract_no, Some(func), ns, vartab, opt);
- let left_block = cfg.new_basic_block("left".to_string());
- let right_block = cfg.new_basic_block("right".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: left_block,
- false_block: right_block,
- },
- );
- vartab.new_dirty_tracker();
- cfg.set_basic_block(left_block);
- returns(left, cfg, contract_no, func, ns, vartab, opt);
- cfg.set_basic_block(right_block);
- returns(right, cfg, contract_no, func, ns, vartab, opt);
- return;
- }
- ast::Expression::Builtin {
- kind: ast::Builtin::AbiDecode,
- ..
- }
- | ast::Expression::InternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCallRaw { .. } => {
- emit_function_call(expr, contract_no, cfg, Some(func), ns, vartab, opt)
- }
- ast::Expression::List { list, .. } => list
- .iter()
- .map(|e| expression(e, cfg, contract_no, Some(func), ns, vartab, opt))
- .collect::<Vec<Expression>>(),
- // Can be any other expression
- _ => {
- vec![expression(
- expr,
- cfg,
- contract_no,
- Some(func),
- ns,
- vartab,
- opt,
- )]
- }
- };
- let cast_values = func
- .returns
- .iter()
- .zip(uncast_values.into_iter())
- .map(|(left, right)| try_load_and_cast(&right.loc(), &right, &left.ty, ns, cfg, vartab))
- .collect();
- cfg.add(vartab, Instr::Return { value: cast_values });
- }
- fn destructure(
- fields: &[DestructureField],
- expr: &ast::Expression,
- cfg: &mut ControlFlowGraph,
- contract_no: usize,
- func: &Function,
- ns: &Namespace,
- vartab: &mut Vartable,
- opt: &Options,
- ) {
- if let ast::Expression::ConditionalOperator {
- cond,
- true_option: left,
- false_option: right,
- ..
- } = expr
- {
- let cond = expression(cond, cfg, contract_no, Some(func), ns, vartab, opt);
- let left_block = cfg.new_basic_block("left".to_string());
- let right_block = cfg.new_basic_block("right".to_string());
- let done_block = cfg.new_basic_block("done".to_string());
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond,
- true_block: left_block,
- false_block: right_block,
- },
- );
- vartab.new_dirty_tracker();
- cfg.set_basic_block(left_block);
- destructure(fields, left, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(vartab, Instr::Branch { block: done_block });
- cfg.set_basic_block(right_block);
- destructure(fields, right, cfg, contract_no, func, ns, vartab, opt);
- cfg.add(vartab, Instr::Branch { block: done_block });
- cfg.set_phis(done_block, vartab.pop_dirty_tracker());
- cfg.set_basic_block(done_block);
- return;
- }
- let mut values = match expr {
- ast::Expression::List { list, .. } => {
- let mut values = Vec::new();
- for expr in list {
- let loc = expr.loc();
- let expr = expression(expr, cfg, contract_no, Some(func), ns, vartab, opt);
- let ty = expr.ty();
- let res = vartab.temp_anonymous(&ty);
- cfg.add(vartab, Instr::Set { loc, res, expr });
- values.push(Expression::Variable {
- loc,
- ty,
- var_no: res,
- });
- }
- values
- }
- _ => {
- // must be function call, either internal or external
- emit_function_call(expr, contract_no, cfg, Some(func), ns, vartab, opt)
- }
- };
- for field in fields.iter() {
- let right = values.remove(0);
- match field {
- DestructureField::None => {
- // nothing to do
- }
- DestructureField::VariableDecl(res, param) => {
- let expr = try_load_and_cast(¶m.loc, &right, ¶m.ty, ns, cfg, vartab);
- if should_remove_variable(*res, func, opt) {
- continue;
- }
- cfg.add(
- vartab,
- Instr::Set {
- loc: param.loc,
- res: *res,
- expr,
- },
- );
- }
- DestructureField::Expression(left) => {
- let expr = try_load_and_cast(&left.loc(), &right, &left.ty(), ns, cfg, vartab);
- if should_remove_assignment(ns, left, func, opt) {
- continue;
- }
- assign_single(left, expr, cfg, contract_no, Some(func), ns, vartab, opt);
- }
- }
- }
- }
- /// During a destructure statement, sema only checks if the cast is possible. During codegen, we
- /// perform the real cast and add an instruction to the CFG to load a value from the storage if want it.
- /// The existing codegen cast function does not manage the CFG, so the loads must be done here.
- fn try_load_and_cast(
- loc: &pt::Loc,
- expr: &Expression,
- to_ty: &Type,
- ns: &Namespace,
- cfg: &mut ControlFlowGraph,
- vartab: &mut Vartable,
- ) -> Expression {
- match expr.ty() {
- Type::StorageRef(_, ty) => {
- if let Expression::Subscript { array_ty, .. } = &expr {
- if array_ty.is_storage_bytes() {
- return expr.cast(to_ty, ns);
- }
- }
- if matches!(to_ty, Type::StorageRef(..)) {
- // If we want a storage reference, there is no need to load from storage
- return expr.cast(to_ty, ns);
- }
- let anonymous_no = vartab.temp_anonymous(&ty);
- cfg.add(
- vartab,
- Instr::LoadStorage {
- res: anonymous_no,
- ty: (*ty).clone(),
- storage: expr.cast(to_ty, ns),
- },
- );
- Expression::Variable {
- loc: *loc,
- ty: (*ty).clone(),
- var_no: anonymous_no,
- }
- }
- Type::Ref(ty) => match *ty {
- Type::Array(_, _) => expr.cast(to_ty, ns),
- _ => Expression::Load {
- loc: pt::Loc::Builtin,
- ty: *ty,
- expr: expr.clone().into(),
- }
- .cast(to_ty, ns),
- },
- _ => expr.cast(to_ty, ns),
- }
- }
- /// Resolve try catch statement
- fn try_catch(
- try_stmt: &TryCatch,
- func: &Function,
- cfg: &mut ControlFlowGraph,
- callee_contract_no: usize,
- ns: &Namespace,
- vartab: &mut Vartable,
- loops: &mut LoopScopes,
- placeholder: Option<&Instr>,
- return_override: Option<&Instr>,
- opt: &Options,
- ) {
- let success = vartab.temp(
- &pt::Identifier {
- loc: try_stmt.expr.loc(),
- name: "success".to_owned(),
- },
- &Type::Bool,
- );
- let success_block = cfg.new_basic_block("success".to_string());
- let catch_block = cfg.new_basic_block("catch".to_string());
- let finally_block = cfg.new_basic_block("finally".to_string());
- match &try_stmt.expr {
- ast::Expression::ExternalFunctionCall {
- loc,
- function,
- args,
- call_args,
- ..
- } => {
- if let Type::ExternalFunction {
- returns: func_returns,
- ..
- } = function.ty()
- {
- let value = if let Some(value) = &call_args.value {
- expression(value, cfg, callee_contract_no, Some(func), ns, vartab, opt)
- } else {
- Expression::NumberLiteral {
- loc: Codegen,
- ty: Type::Value,
- value: BigInt::zero(),
- }
- };
- let gas = if let Some(gas) = &call_args.gas {
- expression(gas, cfg, callee_contract_no, Some(func), ns, vartab, opt)
- } else {
- default_gas(ns)
- };
- let function = expression(
- function,
- cfg,
- callee_contract_no,
- Some(func),
- ns,
- vartab,
- opt,
- );
- let mut args = args
- .iter()
- .map(|a| expression(a, cfg, callee_contract_no, Some(func), ns, vartab, opt))
- .collect::<Vec<Expression>>();
- let selector = function.external_function_selector();
- let address = function.external_function_address();
- args.insert(0, selector);
- let (payload, _) = abi_encode(loc, args, ns, vartab, cfg, false);
- cfg.add(
- vartab,
- Instr::ExternalCall {
- success: Some(success),
- address: Some(address),
- accounts: None,
- seeds: None,
- payload,
- value,
- gas,
- callty: CallTy::Regular,
- contract_function_no: None,
- },
- );
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: Expression::Variable {
- loc: try_stmt.expr.loc(),
- ty: Type::Bool,
- var_no: success,
- },
- true_block: success_block,
- false_block: catch_block,
- },
- );
- cfg.set_basic_block(success_block);
- if func_returns != vec![Type::Void] {
- let mut res = Vec::new();
- for ret in &try_stmt.returns {
- res.push(match ret {
- (Some(pos), _) => *pos,
- (None, param) => vartab.temp_anonymous(¶m.ty),
- });
- }
- let buf = &Expression::ReturnData { loc: Codegen };
- let decoded = abi_decode(&Codegen, buf, &func_returns, ns, vartab, cfg, None);
- for instruction in res.iter().zip(decoded).map(|(var, expr)| Instr::Set {
- loc: Codegen,
- res: *var,
- expr,
- }) {
- cfg.add(vartab, instruction)
- }
- }
- } else {
- // dynamic dispatch
- unimplemented!();
- }
- }
- ast::Expression::Constructor {
- loc,
- contract_no,
- constructor_no,
- args,
- call_args,
- ..
- } => {
- let address_res = match try_stmt.returns.get(0) {
- Some((Some(pos), _)) => *pos,
- _ => vartab.temp_anonymous(&Type::Contract(*contract_no)),
- };
- call_constructor(
- loc,
- *contract_no,
- callee_contract_no,
- constructor_no,
- args,
- call_args,
- address_res,
- Some(success),
- Some(func),
- ns,
- vartab,
- cfg,
- opt,
- );
- cfg.add(
- vartab,
- Instr::BranchCond {
- cond: Expression::Variable {
- loc: try_stmt.expr.loc(),
- ty: Type::Bool,
- var_no: success,
- },
- true_block: success_block,
- false_block: catch_block,
- },
- );
- cfg.set_basic_block(success_block);
- }
- _ => unreachable!(),
- }
- vartab.new_dirty_tracker();
- let mut finally_reachable = true;
- for stmt in &try_stmt.ok_stmt {
- statement(
- stmt,
- func,
- cfg,
- callee_contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- finally_reachable = stmt.reachable();
- }
- if finally_reachable {
- cfg.add(
- vartab,
- Instr::Branch {
- block: finally_block,
- },
- );
- }
- cfg.set_basic_block(catch_block);
- for (error_param_pos, error_param, error_stmt) in &try_stmt.errors {
- let no_reason_block = cfg.new_basic_block("no_reason".to_string());
- let error_var = match error_param_pos {
- Some(pos) => *pos,
- _ => vartab.temp_anonymous(&Type::String),
- };
- // Expect the returned data to match the 4 bytes function selector for "Error(string)"
- let buf = &Expression::ReturnData { loc: Codegen };
- let tys = &[Uint(32), error_param.ty.clone()];
- let decoded = abi_decode(&Codegen, buf, tys, ns, vartab, cfg, None);
- let err_id = Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: 0x08c3_79a0.into(),
- }
- .into();
- let cond = Expression::Equal {
- loc: Codegen,
- left: decoded[0].clone().into(),
- right: err_id,
- };
- let match_err_id = cfg.new_basic_block("match_err_id".into());
- let no_match_err_id = cfg.new_basic_block("no_match_err_id".into());
- let instruction = Instr::BranchCond {
- cond,
- true_block: match_err_id,
- false_block: no_match_err_id,
- };
- cfg.add(vartab, instruction);
- cfg.set_basic_block(no_match_err_id);
- cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
- cfg.set_basic_block(match_err_id);
- let instruction = Instr::Set {
- loc: Codegen,
- res: error_var,
- expr: decoded[1].clone(),
- };
- cfg.add(vartab, instruction);
- let mut reachable = true;
- for stmt in error_stmt {
- statement(
- stmt,
- func,
- cfg,
- callee_contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- reachable = stmt.reachable();
- }
- if reachable {
- cfg.add(
- vartab,
- Instr::Branch {
- block: finally_block,
- },
- );
- }
- cfg.set_basic_block(no_reason_block);
- }
- if let Some(res) = try_stmt.catch_param_pos {
- let instruction = Instr::Set {
- loc: Codegen,
- res,
- expr: Expression::ReturnData { loc: Codegen },
- };
- cfg.add(vartab, instruction);
- }
- let mut reachable = true;
- for stmt in &try_stmt.catch_stmt {
- statement(
- stmt,
- func,
- cfg,
- callee_contract_no,
- ns,
- vartab,
- loops,
- placeholder,
- return_override,
- opt,
- );
- reachable = stmt.reachable();
- }
- if reachable {
- cfg.add(
- vartab,
- Instr::Branch {
- block: finally_block,
- },
- );
- }
- let mut set = vartab.pop_dirty_tracker();
- if let Some(pos) = &try_stmt.catch_param_pos {
- set.remove(pos);
- }
- for (pos, _, _) in &try_stmt.errors {
- if let Some(pos) = pos {
- set.remove(pos);
- }
- }
- cfg.set_phis(finally_block, set);
- cfg.set_basic_block(finally_block);
- }
- pub struct LoopScope {
- break_bb: usize,
- continue_bb: usize,
- }
- pub struct LoopScopes(Vec<LoopScope>);
- impl LoopScopes {
- pub fn new() -> Self {
- LoopScopes(Vec::new())
- }
- pub(crate) fn new_scope(&mut self, break_bb: usize, continue_bb: usize) {
- self.0.push(LoopScope {
- break_bb,
- continue_bb,
- })
- }
- pub(crate) fn leave_scope(&mut self) -> LoopScope {
- self.0.pop().expect("should be in loop scope")
- }
- pub(crate) fn do_break(&mut self) -> usize {
- self.0.last().unwrap().break_bb
- }
- pub(crate) fn do_continue(&mut self) -> usize {
- self.0.last().unwrap().continue_bb
- }
- }
- impl Type {
- /// Default value for a type, e.g. an empty string. Some types cannot have a default value,
- /// for example a reference to a variable in storage.
- pub fn default(&self, ns: &Namespace) -> Option<Expression> {
- match self {
- Type::Address(_) | Uint(_) | Type::Int(_) => Some(Expression::NumberLiteral {
- loc: Codegen,
- ty: self.clone(),
- value: BigInt::from(0),
- }),
- Type::Bool => Some(Expression::BoolLiteral {
- loc: Codegen,
- value: false,
- }),
- Type::Bytes(n) => {
- let mut l = Vec::new();
- l.resize(*n as usize, 0);
- Some(Expression::BytesLiteral {
- loc: Codegen,
- ty: self.clone(),
- value: l,
- })
- }
- Type::Enum(e) => ns.enums[*e].ty.default(ns),
- Type::Struct(struct_ty) => {
- // make sure all our fields have default values
- for field in &struct_ty.definition(ns).fields {
- field.ty.default(ns)?;
- }
- Some(Expression::StructLiteral {
- loc: Codegen,
- ty: self.clone(),
- values: Vec::new(),
- })
- }
- Type::Ref(ty) => {
- assert!(matches!(ty.as_ref(), Type::Address(_)));
- Some(Expression::GetRef {
- loc: Codegen,
- ty: Type::Ref(Box::new(ty.as_ref().clone())),
- expr: Box::new(Expression::NumberLiteral {
- loc: Codegen,
- ty: ty.as_ref().clone(),
- value: BigInt::from(0),
- }),
- })
- }
- Type::StorageRef(..) => None,
- Type::String | Type::DynamicBytes => Some(Expression::AllocDynamicBytes {
- loc: Codegen,
- ty: self.clone(),
- size: Box::new(Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: BigInt::zero(),
- }),
- initializer: None,
- }),
- Type::InternalFunction { .. } | Type::Contract(_) | Type::ExternalFunction { .. } => {
- None
- }
- Type::Array(ty, dims) => {
- ty.default(ns)?;
- if dims.last() == Some(&ArrayLength::Dynamic) {
- Some(Expression::AllocDynamicBytes {
- loc: Codegen,
- ty: self.clone(),
- size: Box::new(Expression::NumberLiteral {
- loc: Codegen,
- ty: Uint(32),
- value: BigInt::zero(),
- }),
- initializer: None,
- })
- } else {
- Some(Expression::ArrayLiteral {
- loc: Codegen,
- ty: self.clone(),
- dimensions: Vec::new(),
- values: Vec::new(),
- })
- }
- }
- _ => None,
- }
- }
- }
- impl Namespace {
- /// Phoney default constructor
- pub fn default_constructor(&self, contract_no: usize) -> Function {
- let mut func = Function::new(
- Codegen,
- "".to_owned(),
- Some(contract_no),
- vec![],
- pt::FunctionTy::Constructor,
- None,
- pt::Visibility::Public(None),
- Vec::new(),
- Vec::new(),
- self,
- );
- func.body = vec![Statement::Return(Codegen, None)];
- func.has_body = true;
- func
- }
- }
- /// This function looks for expressions that have side effects during code execution and
- /// processes them.
- /// They must be added to the cfg event if we remove the assignment
- pub fn process_side_effects_expressions(
- exp: &ast::Expression,
- ctx: &mut SideEffectsCheckParameters,
- ) -> bool {
- match &exp {
- ast::Expression::InternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCall { .. }
- | ast::Expression::ExternalFunctionCallRaw { .. }
- | ast::Expression::Constructor { .. }
- | ast::Expression::Assign { .. } => {
- let _ = expression(
- exp,
- ctx.cfg,
- ctx.contract_no,
- ctx.func,
- ctx.ns,
- ctx.vartab,
- ctx.opt,
- );
- false
- }
- ast::Expression::Builtin {
- kind: builtin_type, ..
- } => match &builtin_type {
- ast::Builtin::PayableSend
- | ast::Builtin::ArrayPush
- | ast::Builtin::ArrayPop
- // PayableTransfer, Revert, Require and SelfDestruct do not occur inside an expression
- // for they return no value. They should not bother the unused variable elimination.
- | ast::Builtin::PayableTransfer
- | ast::Builtin::Revert
- | ast::Builtin::Require
- | ast::Builtin::SelfDestruct
- | ast::Builtin::WriteInt8
- | ast::Builtin::WriteInt16LE
- | ast::Builtin::WriteInt32LE
- | ast::Builtin::WriteInt64LE
- | ast::Builtin::WriteInt128LE
- | ast::Builtin::WriteInt256LE
- | ast::Builtin::WriteUint16LE
- | ast::Builtin::WriteUint32LE
- | ast::Builtin::WriteUint64LE
- | ast::Builtin::WriteUint128LE
- | ast::Builtin::WriteUint256LE
- | ast::Builtin::WriteAddress => {
- let _ = expression(exp, ctx.cfg, ctx.contract_no, ctx.func, ctx.ns, ctx.vartab, ctx.opt);
- false
- }
- _ => true,
- },
- _ => true,
- }
- }
|