From 46a559430910b9ba976adbd0439130043bc4cc0c Mon Sep 17 00:00:00 2001 From: brock elmore Date: Fri, 19 Jul 2024 17:40:51 -0700 Subject: [PATCH] more fixes --- crates/cli/src/main.rs | 4 + crates/graph/src/nodes/var_ty.rs | 2 +- crates/graph/src/var_type.rs | 2 +- crates/pyrometer/src/analyzer.rs | 2 + crates/pyrometer/src/analyzer_backend.rs | 4 + .../tests/test_data/function_calls.sol | 4 +- crates/shared/src/analyzer_like.rs | 1 + .../src/context_builder/flattened.rs | 122 +++- .../src/func_call/func_caller.rs | 6 +- .../src/func_call/intrinsic_call/address.rs | 35 +- .../intrinsic_call/intrinsic_caller.rs | 2 +- crates/solc-expressions/src/literal.rs | 4 + .../src/member_access/builtin_access.rs | 33 +- crates/solc-expressions/src/require.rs | 559 ------------------ 14 files changed, 169 insertions(+), 611 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 7b272ef4..735e2586 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -110,6 +110,9 @@ struct Args { #[clap(long)] pub minimize_debug: Option, + + #[clap(long, default_value = "false")] + pub debug_stack: bool, } pub fn subscriber() { @@ -243,6 +246,7 @@ fn main() { root: Root::RemappingsDirectory(env::current_dir().unwrap()), debug_panic: args.debug_panic || args.minimize_debug.is_some(), minimize_debug: args.minimize_debug, + debug_stack: args.debug_stack, ..Default::default() }; diff --git a/crates/graph/src/nodes/var_ty.rs b/crates/graph/src/nodes/var_ty.rs index 3bd9c5ea..cc26a73a 100644 --- a/crates/graph/src/nodes/var_ty.rs +++ b/crates/graph/src/nodes/var_ty.rs @@ -60,7 +60,7 @@ impl VarNode { let init = analyzer.parse_expr(arena, &expr, Some(parent)); let underlying = self.underlying(analyzer)?; let target_ty = VarType::try_from_idx(analyzer, underlying.ty).unwrap(); - let initer_ty = ContextVarNode::from(init).ty(analyzer)?.clone(); + let initer_ty = VarType::try_from_idx(analyzer, init).unwrap(); let mut set = false; if let Some(initer) = initer_ty.try_cast(&target_ty, analyzer)? { diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 886d1b04..862dce9d 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -277,7 +277,7 @@ impl VarType { (Self::Concrete(from), Self::BuiltIn(to, _)) => { let from = from.underlying(analyzer)?.as_builtin(); let to = to.underlying(analyzer)?; - Ok(from.implicitly_castable_to(&to)) + Ok(from.implicitly_castable_to(to)) } (Self::BuiltIn(from, _), Self::Concrete(to)) => { let from = from.underlying(analyzer)?; diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 884b0356..621b7cbb 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -150,6 +150,7 @@ pub struct Analyzer { pub flattened: Vec, pub expr_flag: Option, pub current_asm_block: usize, + pub debug_stack: bool, } impl Default for Analyzer { @@ -191,6 +192,7 @@ impl Default for Analyzer { flattened: vec![], expr_flag: None, current_asm_block: 0, + debug_stack: false, }; a.builtin_fn_inputs = builtin_fns::builtin_fns_inputs(&mut a); diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index 410b468c..7e674e5d 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -581,4 +581,8 @@ impl AnalyzerLike for Analyzer { fn expr_stack_mut(&mut self) -> &mut Vec { &mut self.flattened } + + fn debug_stack(&self) -> bool { + self.debug_stack + } } diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index fad4a930..2a84953a 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -126,7 +126,7 @@ contract C is S1, S2 { return 0; } - function a(uint256 x) internal pure override(A, B) returns (uint) { + function a(uint256 x) internal pure override(S1, S2) returns (uint) { return 50; } } @@ -144,7 +144,7 @@ contract D is S2, S1 { return 0; } - function a(uint256 x) internal pure override(A, B) returns (uint) { + function a(uint256 x) internal pure override(S1, S2) returns (uint) { return 50; } } diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index 016c8189..c435d347 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -201,4 +201,5 @@ pub trait AnalyzerLike: GraphLike { fn current_asm_block(&self) -> usize; fn expr_stack(&self) -> &[Self::FlatExpr]; fn expr_stack_mut(&mut self) -> &mut Vec; + fn debug_stack(&self) -> bool; } diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index f198bfd5..bb89e873 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -8,12 +8,12 @@ use crate::{ ExprTyParser, }; use graph::{ - elem::{Elem, RangeOp}, + elem::{Elem, RangeExpr, RangeOp}, nodes::{ Builtin, Concrete, ConcreteNode, Context, ContextNode, ContextVar, ContextVarNode, - ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, YulFunction, + ContractNode, ExprRet, FunctionNode, KilledKind, StructNode, TmpConstruction, YulFunction, }, - AnalyzerBackend, ContextEdge, Edge, Node, TypeNode, VarType, + AnalyzerBackend, ContextEdge, Edge, Node, SolcRange, TypeNode, VarType, }; use shared::{ @@ -922,7 +922,9 @@ pub trait Flatten: ctx.peek_expr_flag(self) ); - // ctx.debug_expr_stack(self); + if self.debug_stack() { + let _ = ctx.debug_expr_stack(self); + } match next { Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), @@ -955,9 +957,11 @@ pub trait Flatten: Variable(..) => self.interp_var(arena, ctx, stack, next, parse_idx), Assign(..) => self.interp_assign(arena, ctx, next), List(_, _) => self.interp_list(ctx, stack, next, parse_idx), + This(_) => self.interp_this(ctx, next), + Delete(_) => self.interp_delete(ctx, next), // Conditional - If { .. } => self.interp_if(arena, ctx, stack, next, parse_idx), + If { .. } => self.interp_if(arena, ctx, stack, next), Requirement(..) => { ctx.set_expr_flag(self, ExprFlag::Requirement); Ok(()) @@ -998,7 +1002,6 @@ pub trait Flatten: | BitwiseAnd(loc, ..) | BitwiseXor(loc, ..) | BitwiseOr(loc, ..) => self.interp_op(arena, ctx, next, loc, false), - BitwiseNot(..) => self.interp_bit_not(arena, ctx, next), AssignAdd(loc, ..) | AssignSubtract(loc, ..) | AssignMultiply(loc, ..) @@ -1009,6 +1012,7 @@ pub trait Flatten: | AssignXor(loc, ..) | AssignShiftLeft(loc, ..) | AssignShiftRight(loc, ..) => self.interp_op(arena, ctx, next, loc, true), + BitwiseNot(..) => self.interp_bit_not(arena, ctx, next), // Comparator Not(..) => self.interp_not(arena, ctx, next), Equal(loc) | NotEqual(loc) | Less(loc) | More(loc) | LessEqual(loc) @@ -1029,8 +1033,8 @@ pub trait Flatten: } // Semi useless - Parameter(_, _, _) => Ok(()), Super(..) => unreachable!(), + Parameter(_, _, _) => Ok(()), Emit(loc) => { let _ = ctx.pop_n_latest_exprs(1, loc, self).into_expr_err(loc)?; Ok(()) @@ -1038,8 +1042,6 @@ pub trait Flatten: Null(loc) => ctx.push_expr(ExprRet::Null, self).into_expr_err(loc), // Todo - This(_) => todo!(), - Delete(_) => todo!(), UnaryPlus(_) => todo!(), Try { .. } => todo!(), @@ -1089,6 +1091,56 @@ pub trait Flatten: } } + fn interp_delete(&mut self, ctx: ContextNode, next: FlatExpr) -> Result<(), ExprErr> { + let FlatExpr::Delete(loc) = next else { + unreachable!() + }; + + let to_delete = ctx + .pop_n_latest_exprs(1, loc, self) + .into_expr_err(loc)? + .swap_remove(0); + + self.delete_match(ctx, to_delete, loc) + } + + fn delete_match( + &mut self, + ctx: ContextNode, + to_delete: ExprRet, + loc: Loc, + ) -> Result<(), ExprErr> { + match to_delete { + ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), + ExprRet::Single(cvar) | ExprRet::SingleLiteral(cvar) => { + let mut new_var = self.advance_var_in_ctx(cvar.into(), loc, ctx).unwrap(); + new_var.sol_delete_range(self).into_expr_err(loc) + } + ExprRet::Multi(inner) => inner + .into_iter() + .try_for_each(|i| self.delete_match(ctx, i, loc)), + ExprRet::Null => Ok(()), + } + } + + fn interp_this(&mut self, ctx: ContextNode, next: FlatExpr) -> Result<(), ExprErr> { + let FlatExpr::This(loc) = next else { + unreachable!() + }; + + let var = ContextVar::new_from_contract( + loc, + ctx.associated_contract(self).into_expr_err(loc)?, + self, + ) + .into_expr_err(loc)?; + let cvar = self.add_node(Node::ContextVar(var)); + ctx.add_var(cvar.into(), self).into_expr_err(loc)?; + self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); + ctx.push_expr(ExprRet::Single(cvar), self) + .into_expr_err(loc) + } + fn interp_test_command( &mut self, arena: &mut RangeArena>, @@ -1370,7 +1422,6 @@ pub trait Flatten: ctx: ContextNode, stack: &mut Vec, if_expr: FlatExpr, - parse_idx: usize, ) -> Result<(), ExprErr> { let FlatExpr::If { loc, @@ -1409,6 +1460,7 @@ pub trait Flatten: let false_killed = false_subctx.is_killed(self).into_expr_err(loc)? || false_subctx.unreachable(self, arena).into_expr_err(loc)?; + println!("true killed: {true_killed}, false_killed: {false_killed}"); match (true_killed, false_killed) { (true, true) => { // both have been killed, delete the child and dont process the bodies @@ -1421,7 +1473,7 @@ pub trait Flatten: // point the parse index of the parent ctx to the false body ctx.underlying_mut(self).unwrap().parse_idx = - parse_idx + true_cond + false_cond + true_body; + ctx.parse_idx(self) + true_cond + false_cond + true_body; for _ in 0..false_body { self.interpret_step(arena, ctx, loc, stack)?; } @@ -1501,7 +1553,53 @@ pub trait Flatten: self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Gte, loc) } FlatExpr::Or(..) => { - self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::Or, loc) + let lhs = ContextVarNode::from(lhs.expect_single().into_expr_err(loc)?); + let rhs = ContextVarNode::from(rhs.expect_single().into_expr_err(loc)?); + let elem = Elem::Expr(RangeExpr::new(lhs.into(), RangeOp::Or, rhs.into())); + let range = SolcRange::new(elem.clone(), elem, vec![]); + let new_lhs_underlying = ContextVar { + loc: Some(loc), + name: format!( + "tmp{}({} {} {})", + ctx.new_tmp(self).into_expr_err(loc)?, + lhs.name(self).into_expr_err(loc)?, + RangeOp::Or.to_string(), + rhs.name(self).into_expr_err(loc)? + ), + display_name: format!( + "({} {} {})", + lhs.display_name(self).into_expr_err(loc)?, + RangeOp::Or.to_string(), + rhs.display_name(self).into_expr_err(loc)? + ), + storage: None, + is_tmp: true, + is_symbolic: lhs.is_symbolic(self).into_expr_err(loc)? + || rhs.is_symbolic(self).into_expr_err(loc)?, + is_return: false, + tmp_of: Some(TmpConstruction::new(lhs, RangeOp::Or, Some(rhs))), + dep_on: { + let mut deps = lhs.dependent_on(self, true).into_expr_err(loc)?; + deps.extend(rhs.dependent_on(self, true).into_expr_err(loc)?); + Some(deps) + }, + ty: VarType::BuiltIn( + self.builtin_or_add(Builtin::Bool).into(), + Some(range), + ), + }; + let or_var = ContextVarNode::from(self.add_node(new_lhs_underlying)); + let node = self.add_concrete_var(ctx, Concrete::Bool(true), loc)?; + ctx.add_var(node, self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + self.handle_require_inner( + arena, + ctx, + &ExprRet::Single(or_var.into()), + &ExprRet::Single(node.into()), + RangeOp::Eq, + loc, + ) } FlatExpr::And(..) => { self.handle_require_inner(arena, ctx, &lhs, &rhs, RangeOp::And, loc) diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index 9a375bd7..ce2fde4b 100644 --- a/crates/solc-expressions/src/func_call/func_caller.rs +++ b/crates/solc-expressions/src/func_call/func_caller.rs @@ -142,9 +142,9 @@ pub trait FuncCaller: modifier_state: &Option, ) -> Result<(), ExprErr> { if !entry_call { - // if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { - // return Ok(()); - // } + if let Ok(true) = self.apply(arena, ctx, loc, func_node, params, inputs, &mut vec![]) { + return Ok(()); + } } // pseudocode: diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs index 53b4dca0..cc656d23 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -14,36 +14,6 @@ pub trait AddressCaller: AnalyzerBackend + fn address_call(&mut self, ctx: ContextNode, func_name: &str, loc: Loc) -> Result<(), ExprErr> { match func_name { "delegatecall" | "staticcall" | "call" => self.external_call(ctx, func_name, loc), - "code" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::DynamicBytes); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } - "codehash" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Bytes(32)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } - "balance" => { - // TODO: try to be smarter based on the address input - let bn = self.builtin_or_add(Builtin::Uint(256)); - let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; - let node = self.add_node(cvar); - ctx.add_var(node.into(), self).into_expr_err(loc)?; - self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - ctx.push_expr(ExprRet::Single(node), self) - .into_expr_err(loc) - } "send" => { let bn = self.builtin_or_add(Builtin::Bool); let cvar = ContextVar::new_from_builtin(loc, bn.into(), self).into_expr_err(loc)?; @@ -53,6 +23,11 @@ pub trait AddressCaller: AnalyzerBackend + ctx.push_expr(ExprRet::Single(node), self) .into_expr_err(loc) } + "transfer" => { + // TODO: handle balance stuff. but otherwise, transfer does not + // produce a return value. + Ok(()) + } _ => Err(ExprErr::FunctionNotFound( loc, format!( diff --git a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs index 26acbc60..9754646b 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/intrinsic_caller.rs @@ -170,7 +170,7 @@ pub trait IntrinsicFuncCaller: // abi _ if name.starts_with("abi.") => self.abi_call_inner(ctx, name, inputs, loc), // address - "delegatecall" | "staticcall" | "call" | "code" | "balance" => { + "delegatecall" | "staticcall" | "call" | "send" | "transfer" => { self.address_call(ctx, name, loc) } // array diff --git a/crates/solc-expressions/src/literal.rs b/crates/solc-expressions/src/literal.rs index 5a36a31e..fe6ef683 100644 --- a/crates/solc-expressions/src/literal.rs +++ b/crates/solc-expressions/src/literal.rs @@ -286,6 +286,10 @@ pub trait Literal: AnalyzerBackend + Sized { let range = split .get(4) .copied()? + .chars() + .filter(|c| !c.is_whitespace()) + .collect::(); + let range = range .trim_start_matches('[') .trim_end_matches(']') .split(',') diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index 1831fd03..52ac6120 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -34,14 +34,43 @@ pub trait BuiltinAccess: match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { - "delegatecall" | "call" | "staticcall" | "code" | "codehash" - | "balance" | "send" | "transfer" => { + "delegatecall" | "call" | "staticcall" | "send" | "transfer" => { // TODO: check if the address is known to be a certain type and the function signature is known // and call into the function let builtin_name = name.split('(').collect::>()[0]; let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); Ok((ExprRet::Single(func_node), true)) } + "codehash" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Bytes(32)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } + "code" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::DynamicBytes); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } + "balance" => { + // TODO: try to be smarter based on the address input + let bn = self.builtin_or_add(Builtin::Uint(256)); + let cvar = ContextVar::new_from_builtin(loc, bn.into(), self) + .into_expr_err(loc)?; + let node = self.add_node(cvar); + ctx.add_var(node.into(), self).into_expr_err(loc)?; + self.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); + Ok((ExprRet::Single(node), false)) + } _ => Err(ExprErr::MemberAccessNotFound( loc, format!( diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 7e82c489..3ce75046 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -23,565 +23,6 @@ impl Require for T where T: Variable + BinOp + Sized + AnalyzerBackend {} /// Deals with require and assert statements, as well as adjusts bounds for variables pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { - /// Inverts a comparator expression - fn inverse_expr(&self, expr: Expression) -> Expression { - match expr { - Expression::Equal(loc, lhs, rhs) => Expression::NotEqual(loc, lhs, rhs), - Expression::NotEqual(loc, lhs, rhs) => Expression::Equal(loc, lhs, rhs), - Expression::Less(loc, lhs, rhs) => Expression::MoreEqual(loc, lhs, rhs), - Expression::More(loc, lhs, rhs) => Expression::LessEqual(loc, lhs, rhs), - Expression::MoreEqual(loc, lhs, rhs) => Expression::Less(loc, lhs, rhs), - Expression::LessEqual(loc, lhs, rhs) => Expression::More(loc, lhs, rhs), - // Expression::And(loc, lhs, rhs) => { - // Expression::Or(loc, Box::new(self.inverse_expr(*lhs)), Box::new(self.inverse_expr(*rhs))) - // } - // Expression::Or(loc, lhs, rhs) => { - // Expression::And(loc, Box::new(self.inverse_expr(*lhs)), Box::new(self.inverse_expr(*rhs))) - // } - // Expression::Not(loc, lhs) => { - // Expression::Equal(loc, lhs, Box::new(Expression::BoolLiteral(loc, true))) - // } - e => Expression::Not(e.loc(), Box::new(e)), - } - } - - // /// Handles a require expression - // #[tracing::instrument(level = "trace", skip_all)] - // fn handle_require( - // &mut self, - // arena: &mut RangeArena>, - // inputs: &[Expression], - // ctx: ContextNode, - // ) -> Result<(), ExprErr> { - // ctx.add_gas_cost(self, shared::gas::BIN_OP_GAS) - // .into_expr_err(inputs[0].loc())?; - // match inputs.first().expect("No lhs input for require statement") { - // Expression::Equal(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `==` had no right hand side".to_string(), - // )); - // }; - - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `==` had no left hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // }) - // } - // Expression::NotEqual(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `!=` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `!=` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Neq, - // loc, - // ) - // }) - // }) - // } - // Expression::Less(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `<` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `<` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Lt, - // loc, - // ) - // }) - // }) - // } - // Expression::More(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `>` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `>` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Gt, - // loc, - // ) - // }) - // }) - // } - // Expression::MoreEqual(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `>=` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `>=` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Gte, - // loc, - // ) - // }) - // }) - // } - // Expression::LessEqual(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, rhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoRhs( - // loc, - // "Require operation `<=` had no right hand side".to_string(), - // )); - // }; - // let rhs_paths = rhs_paths.flatten(); - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, lhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `<=` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths.flatten(), - // &rhs_paths, - // RangeOp::Lte, - // loc, - // ) - // }) - // }) - // } - // Expression::Not(loc, lhs) => { - // self.parse_ctx_expr(arena, lhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `NOT` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::Bool(false))), - // ); - // let tmp_false = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let rhs_paths = - // ExprRet::Single(ContextVarNode::from(analyzer.add_node(tmp_false)).into()); - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths, - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // } - // Expression::And(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, lhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `&&` had no left hand side".to_string(), - // )); - // }; - - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, rhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `&&` had no left hand side".to_string(), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::Bool(true))), - // ); - // let tmp_true = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let node = analyzer.add_node(tmp_true); - // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - // let tmp_rhs_paths = ExprRet::Single(node); - - // // NOTE: the following is *sequence dependent* - // // we want to update the parts *before* the `And` op - // // to ensure the ctx_dep is correct - - // // update the part's bounds - // let lhs_cvar = - // ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // let underlying = lhs_cvar.underlying(analyzer).into_expr_err(loc)?; - // if let Some(tmp) = underlying.tmp_of { - // if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { - // analyzer.handle_require_inner( - // arena, - // ctx, - // &ExprRet::Single(tmp.lhs.into()), - // &ExprRet::Single(tmp.rhs.unwrap().into()), - // op, - // loc, - // )?; - // } - // } - - // // update the part's bounds - // let rhs_cvar = - // ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); - // let underlying = rhs_cvar.underlying(analyzer).into_expr_err(loc)?; - // if let Some(tmp) = underlying.tmp_of { - // if let Some((op, _inv_op, pair)) = tmp.op.require_parts() { - // analyzer.handle_require_inner( - // arena, - // ctx, - // &ExprRet::Single(tmp.lhs.into()), - // &ExprRet::Single(tmp.rhs.unwrap().into()), - // op, - // loc, - // )?; - // } - // } - - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths, - // &tmp_rhs_paths, - // RangeOp::Eq, - // loc, - // )?; - - // analyzer.handle_require_inner( - // arena, - // ctx, - // &rhs_paths, - // &tmp_rhs_paths, - // RangeOp::Eq, - // loc, - // )?; - - // Ok(()) - // }) - // }) - // } - // Expression::Or(loc, lhs, rhs) => { - // self.parse_ctx_expr(arena, lhs, ctx)?; - // self.apply_to_edges(ctx, *loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `||` had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // analyzer.parse_ctx_expr(arena, rhs, ctx)?; - // analyzer.apply_to_edges(ctx, loc, arena, &|analyzer, arena, ctx, loc| { - // let Some(rhs_paths) = - // ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation `||` had no left hand side".to_string(), - // )); - // }; - - // if matches!(rhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(rhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let lhs_cvar = - // ContextVarNode::from(lhs_paths.expect_single().into_expr_err(loc)?); - // let rhs_cvar = - // ContextVarNode::from(rhs_paths.expect_single().into_expr_err(loc)?); - - // let elem = Elem::Expr(RangeExpr::new( - // lhs_cvar.into(), - // RangeOp::Or, - // rhs_cvar.into(), - // )); - // let range = SolcRange::new(elem.clone(), elem, vec![]); - - // let new_lhs_underlying = ContextVar { - // loc: Some(loc), - // name: format!( - // "tmp{}({} {} {})", - // ctx.new_tmp(analyzer).into_expr_err(loc)?, - // lhs_cvar.name(analyzer).into_expr_err(loc)?, - // RangeOp::Or.to_string(), - // rhs_cvar.name(analyzer).into_expr_err(loc)? - // ), - // display_name: format!( - // "({} {} {})", - // lhs_cvar.display_name(analyzer).into_expr_err(loc)?, - // RangeOp::Or.to_string(), - // rhs_cvar.display_name(analyzer).into_expr_err(loc)? - // ), - // storage: None, - // is_tmp: true, - // is_symbolic: lhs_cvar.is_symbolic(analyzer).into_expr_err(loc)? - // || rhs_cvar.is_symbolic(analyzer).into_expr_err(loc)?, - // is_return: false, - // tmp_of: Some(TmpConstruction::new( - // lhs_cvar, - // RangeOp::Or, - // Some(rhs_cvar), - // )), - // dep_on: { - // let mut deps = - // lhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?; - // deps.extend( - // rhs_cvar.dependent_on(analyzer, true).into_expr_err(loc)?, - // ); - // Some(deps) - // }, - // ty: VarType::BuiltIn( - // analyzer.builtin_or_add(Builtin::Bool).into(), - // Some(range), - // ), - // }; - // let or_var = ContextVarNode::from( - // analyzer.add_node(Node::ContextVar(new_lhs_underlying)), - // ); - // let cnode = ConcreteNode::from( - // analyzer.add_node(Node::Concrete(Concrete::Bool(true))), - // ); - // let tmp_true = Node::ContextVar( - // ContextVar::new_from_concrete(Loc::Implicit, ctx, cnode, analyzer) - // .into_expr_err(loc)?, - // ); - // let node = analyzer.add_node(tmp_true); - // ctx.add_var(node.into(), analyzer).into_expr_err(loc)?; - // analyzer.add_edge(node, ctx, Edge::Context(ContextEdge::Variable)); - // let rhs_paths = ExprRet::Single(node); - // analyzer.handle_require_inner( - // arena, - // ctx, - // &ExprRet::Single(or_var.into()), - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // }) - // } - // other => { - // self.parse_ctx_expr(arena, other, ctx)?; - // self.apply_to_edges(ctx, other.loc(), arena, &|analyzer, arena, ctx, loc| { - // let Some(lhs_paths) = ctx.pop_expr_latest(loc, analyzer).into_expr_err(loc)? - // else { - // return Err(ExprErr::NoLhs( - // loc, - // "Require operation had no left hand side".to_string(), - // )); - // }; - // if matches!(lhs_paths, ExprRet::CtxKilled(_)) { - // ctx.push_expr(lhs_paths, analyzer).into_expr_err(loc)?; - // return Ok(()); - // } - - // let tmp_true = analyzer.add_concrete_var(ctx, Concrete::Bool(true), loc)?; - // let rhs_paths = ExprRet::Single(tmp_true.0.into()); - // analyzer.handle_require_inner( - // arena, - // ctx, - // &lhs_paths, - // &rhs_paths, - // RangeOp::Eq, - // loc, - // ) - // }) - // } - // } - // } - /// Do matching on [`ExprRet`]s to actually perform the require statement evaluation fn handle_require_inner( &mut self,