diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 681cad1b..76f398bd 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -104,6 +104,7 @@ pub enum Node { Block(Block), /// A yul-based function YulFunction(YulFunction), + // TODO: Handle events } pub fn as_dot_str( diff --git a/crates/graph/src/nodes/builtin.rs b/crates/graph/src/nodes/builtin.rs index 98f1080c..f374ebb0 100644 --- a/crates/graph/src/nodes/builtin.rs +++ b/crates/graph/src/nodes/builtin.rs @@ -35,6 +35,16 @@ impl BuiltInNode { } /// Checks if this builtin is implicitly castable to another builtin + pub fn castable_to( + &self, + other: &Self, + analyzer: &impl GraphBackend, + ) -> Result { + Ok(self + .underlying(analyzer)? + .castable_to(other.underlying(analyzer)?)) + } + pub fn implicitly_castable_to( &self, other: &Self, @@ -219,6 +229,44 @@ impl Builtin { builtins } + pub fn possible_upcast_builtins(&self) -> Vec { + let mut builtins = vec![]; + match self { + Builtin::Uint(size) => { + let mut s = *size; + while s <= 256 { + builtins.push(Builtin::Uint(s)); + s += 8; + } + } + Builtin::Int(size) => { + let mut s = *size; + while s <= 256 { + builtins.push(Builtin::Int(s)); + s += 8; + } + } + Builtin::Bytes(size) => { + let mut s = *size; + while s <= 32 { + builtins.push(Builtin::Bytes(s)); + s += 1; + } + } + Builtin::Address => builtins.push(Builtin::Address), + Builtin::AddressPayable => { + builtins.push(Builtin::Address); + builtins.push(Builtin::AddressPayable); + } + Builtin::Payable => { + builtins.push(Builtin::Address); + builtins.push(Builtin::AddressPayable); + } + _ => {} + } + builtins + } + /// Construct a [`SolcRange`] that is zero pub fn zero_range(&self) -> Option { match self { @@ -365,7 +413,7 @@ impl Builtin { } /// Checks if self is implicitly castable to another builtin - pub fn implicitly_castable_to(&self, other: &Self) -> bool { + pub fn castable_to(&self, other: &Self) -> bool { use Builtin::*; match (self, other) { (Address, Address) => true, @@ -389,6 +437,32 @@ impl Builtin { } } + /// Checks if self is implicitly castable to another builtin + pub fn implicitly_castable_to(&self, other: &Self) -> bool { + use Builtin::*; + + let res = match (self, other) { + (Address, Address) => true, + (AddressPayable, Address) => true, + (AddressPayable, Payable) => true, + (AddressPayable, AddressPayable) => true, + (Payable, Address) => true, + (Payable, AddressPayable) => true, + (Payable, Payable) => true, + (Bool, Bool) => true, + (Rational, Rational) => true, + (DynamicBytes, DynamicBytes) => true, + (String, String) => true, + (Uint(from_size), Uint(to_size)) => from_size <= to_size, + (Int(from_size), Int(to_size)) => from_size <= to_size, + (Bytes(from_size), Bytes(to_size)) => from_size <= to_size, + _ => false, + }; + + println!("{self:?} implicitly castable to {other:?}, res: {res:?}"); + res + } + /// Returns the max size version of this builtin pub fn max_size(&self) -> Self { use Builtin::*; diff --git a/crates/graph/src/nodes/concrete.rs b/crates/graph/src/nodes/concrete.rs index f048c3f5..64e2d472 100644 --- a/crates/graph/src/nodes/concrete.rs +++ b/crates/graph/src/nodes/concrete.rs @@ -705,6 +705,34 @@ impl Concrete { } } + pub fn alt_lit_builtins(&self) -> Vec { + let mut alts = vec![]; + if let Concrete::Uint(size, val) = self { + // literal(u160) -> address + if *size == 160 { + alts.push(Builtin::Address); + } + + // uint -> int, all size steps between + let mut new_size = *size; + let imax = U256::from(2).pow((*size - 1).into()); + // we may have to bump size by 8 bits + if val > &imax { + new_size += 8; + } + // if a valid + while new_size <= 256 { + alts.push(Builtin::Int(new_size)); + new_size += 8; + } + + // exact bytesX + let bytes_size = size / 8; + alts.push(Builtin::Bytes(bytes_size as u8)); + } + alts + } + /// Converts a concrete into a `U256`. pub fn into_u256(&self) -> Option { match self { diff --git a/crates/graph/src/nodes/context/expr_ret.rs b/crates/graph/src/nodes/context/expr_ret.rs index 92d20c13..94961ca9 100644 --- a/crates/graph/src/nodes/context/expr_ret.rs +++ b/crates/graph/src/nodes/context/expr_ret.rs @@ -304,4 +304,17 @@ impl ExprRet { panic!("Expected a Vec of length {} but it was {}", N, v.len()) }) } + + pub fn implicitly_castable_to( + &self, + analyzer: &impl GraphBackend, + to_ty: &VarType, + ) -> Result { + let idx = self.expect_single()?; + let is_lit = self.has_literal(); + let Some(self_ty) = VarType::try_from_idx(analyzer, idx) else { + return Ok(false); + }; + self_ty.implicitly_castable_to(to_ty, analyzer, is_lit) + } } diff --git a/crates/graph/src/nodes/context/querying.rs b/crates/graph/src/nodes/context/querying.rs index dc03ac77..5b0364ed 100644 --- a/crates/graph/src/nodes/context/querying.rs +++ b/crates/graph/src/nodes/context/querying.rs @@ -177,7 +177,7 @@ impl ContextNode { return Ok(vis.clone()); } if let Some(contract) = self.maybe_associated_contract(analyzer)? { - let mut mapping = contract.linearized_functions(analyzer)?; + let mut mapping = contract.linearized_functions(analyzer, false)?; // extend with free floating functions mapping.extend( analyzer diff --git a/crates/graph/src/nodes/contract_ty.rs b/crates/graph/src/nodes/contract_ty.rs index c3bb19bd..b51ee77d 100644 --- a/crates/graph/src/nodes/contract_ty.rs +++ b/crates/graph/src/nodes/contract_ty.rs @@ -111,6 +111,21 @@ impl ContractNode { .push(ContractNode::from(*found)); analyzer.add_edge(*found, *self, Edge::InheritedContract); }); + self.order_inherits(analyzer); + } + + pub fn order_inherits(&self, analyzer: &mut impl AnalyzerBackend) { + let raw_inherits = self.underlying(analyzer).unwrap().raw_inherits.clone(); + let inherits = self.underlying(analyzer).unwrap().inherits.clone(); + + let mut tmp_inherits = vec![]; + tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0))); + inherits.into_iter().for_each(|inherited| { + let i_name = inherited.name(analyzer).unwrap(); + let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); + tmp_inherits[position] = inherited; + }); + self.underlying_mut(analyzer).unwrap().inherits = tmp_inherits; } pub fn direct_inherited_contracts(&self, analyzer: &impl GraphBackend) -> Vec { @@ -215,27 +230,34 @@ impl ContractNode { pub fn linearized_functions( &self, analyzer: &mut (impl Search + AnalyzerBackend), + super_func: bool, ) -> Result, GraphError> { - if let Some(funcs) = &self.underlying(analyzer)?.cached_functions { - Ok(funcs.clone()) - } else { - let mut mapping = self.funcs_mapping(analyzer); - self.direct_inherited_contracts(analyzer) - .iter() - .for_each(|inherited| { - inherited - .linearized_functions(analyzer) - .unwrap() - .iter() - .for_each(|(name, func)| { - if !mapping.contains_key(name) { - mapping.insert(name.to_string(), *func); - } - }); - }); - self.underlying_mut(analyzer)?.cached_functions = Some(mapping.clone()); - Ok(mapping) + if !super_func { + if let Some(funcs) = &self.underlying(analyzer)?.cached_functions { + return Ok(funcs.clone()); + } } + + let mut mapping = if !super_func { + self.funcs_mapping(analyzer) + } else { + Default::default() + }; + self.direct_inherited_contracts(analyzer) + .iter() + .for_each(|inherited| { + inherited + .linearized_functions(analyzer, false) + .unwrap() + .iter() + .for_each(|(name, func)| { + if !mapping.contains_key(name) { + mapping.insert(name.to_string(), *func); + } + }); + }); + self.underlying_mut(analyzer)?.cached_functions = Some(mapping.clone()); + Ok(mapping) } pub fn structs(&self, analyzer: &(impl GraphBackend + Search)) -> Vec { @@ -359,6 +381,8 @@ pub struct Contract { pub ty: ContractTy, /// An optional name in the form of an identifier (`(Loc, String)`) pub name: Option, + /// + pub raw_inherits: Vec, /// A list of contracts that this contract inherits (TODO: inheritance linearization) pub inherits: Vec, /// Cached linearized functions @@ -379,10 +403,12 @@ impl Contract { imports: &[Option], analyzer: &impl GraphBackend, ) -> (Contract, Vec) { + let mut raw_inherits = vec![]; let mut inherits = vec![]; let mut unhandled_inherits = vec![]; con.base.iter().for_each(|base| { let inherited_name = &base.name.identifiers[0].name; + raw_inherits.push(inherited_name.clone()); let mut found = false; for contract in analyzer .search_children_exclude_via(source, &Edge::Contract, &[Edge::Func]) @@ -416,15 +442,32 @@ impl Contract { unhandled_inherits.push(inherited_name.clone()); } }); - ( - Contract { - loc: con.loc, - ty: con.ty, - name: con.name, - inherits, - cached_functions: None, - }, - unhandled_inherits, - ) + + raw_inherits.reverse(); + let mut this = Contract { + loc: con.loc, + ty: con.ty, + name: con.name, + raw_inherits, + inherits, + cached_functions: None, + }; + + this.order_inherits(analyzer); + (this, unhandled_inherits) + } + + pub fn order_inherits(&mut self, analyzer: &impl GraphBackend) { + let raw_inherits = self.raw_inherits.clone(); + let inherits = self.inherits.clone(); + + let mut tmp_inherits = vec![]; + tmp_inherits.resize(inherits.len(), ContractNode::from(NodeIdx::from(0))); + inherits.into_iter().for_each(|inherited| { + let i_name = inherited.name(analyzer).unwrap(); + let position = raw_inherits.iter().position(|raw| &i_name == raw).unwrap(); + tmp_inherits[position] = inherited; + }); + self.inherits = tmp_inherits; } } diff --git a/crates/graph/src/var_type.rs b/crates/graph/src/var_type.rs index 8d0ae594..886d1b04 100644 --- a/crates/graph/src/var_type.rs +++ b/crates/graph/src/var_type.rs @@ -260,6 +260,55 @@ impl VarType { } } + pub fn implicitly_castable_to( + &self, + other: &Self, + analyzer: &impl GraphBackend, + from_lit: bool, + ) -> Result { + if self.ty_idx() == other.ty_idx() { + return Ok(true); + } + + let res = match (self, other) { + (Self::BuiltIn(from_bn, _), Self::BuiltIn(to_bn, _)) => { + from_bn.implicitly_castable_to(to_bn, analyzer) + } + (Self::Concrete(from), Self::BuiltIn(to, _)) => { + let from = from.underlying(analyzer)?.as_builtin(); + let to = to.underlying(analyzer)?; + Ok(from.implicitly_castable_to(&to)) + } + (Self::BuiltIn(from, _), Self::Concrete(to)) => { + let from = from.underlying(analyzer)?; + let to = to.underlying(analyzer)?.as_builtin(); + Ok(from.implicitly_castable_to(&to)) + } + (Self::Concrete(from), Self::Concrete(to)) => { + let from = from.underlying(analyzer)?.as_builtin(); + let to = to.underlying(analyzer)?.as_builtin(); + Ok(from.implicitly_castable_to(&to)) + } + _ => Ok(false), + }; + + let impl_cast = res?; + if !impl_cast && from_lit { + match (self, other) { + (Self::Concrete(from), Self::BuiltIn(to, _)) => { + let froms = from.underlying(analyzer)?.alt_lit_builtins(); + let to = to.underlying(analyzer)?; + + // exact matches only (i.e. uint160 -> address, uint8 -> bytes1, etc) + Ok(froms.iter().any(|from| from == to)) + } + _ => Ok(impl_cast), + } + } else { + Ok(impl_cast) + } + } + pub fn try_cast( self, other: &Self, @@ -287,7 +336,7 @@ impl VarType { } } (Self::BuiltIn(from_bn, sr), Self::BuiltIn(to_bn, _)) => { - if from_bn.implicitly_castable_to(to_bn, analyzer)? { + if from_bn.castable_to(to_bn, analyzer)? { Ok(Some(Self::BuiltIn(*to_bn, sr))) } else { Ok(None) @@ -371,7 +420,7 @@ impl VarType { } } (Self::BuiltIn(from_bn, sr), Self::BuiltIn(to_bn, _)) => { - if from_bn.implicitly_castable_to(to_bn, analyzer)? { + if from_bn.castable_to(to_bn, analyzer)? { Ok(Some(Self::BuiltIn(*to_bn, sr))) } else { Ok(None) @@ -401,21 +450,18 @@ impl VarType { } } - pub fn implicitly_castable_to( + pub fn castable_to( &self, other: &Self, analyzer: &impl GraphBackend, ) -> Result { match (self, other) { (Self::BuiltIn(from_bn, _), Self::BuiltIn(to_bn, _)) => { - from_bn.implicitly_castable_to(to_bn, analyzer) + from_bn.castable_to(to_bn, analyzer) } (Self::Concrete(from_c), Self::BuiltIn(to_bn, _)) => { let to = to_bn.underlying(analyzer)?; - Ok(from_c - .underlying(analyzer)? - .as_builtin() - .implicitly_castable_to(to)) + Ok(from_c.underlying(analyzer)?.as_builtin().castable_to(to)) } _ => Ok(false), } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 546e7c23..884b0356 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -1148,7 +1148,7 @@ impl Analyzer { }) .copied() .collect(); - + println!("relevant_funcs: {relevant_funcs:#?}"); if matches!(self.node(scope_node), Node::Contract(_)) { self.add_edge( scope_node, @@ -1158,6 +1158,7 @@ impl Analyzer { } relevant_funcs.iter().for_each(|func| { + println!("connecting: {:#?}, {:#?}", self.node(ty_idx), func); self.add_edge(ty_idx, *func, Edge::LibraryFunction(scope_node)); }); break; diff --git a/crates/pyrometer/src/builtin_fns.rs b/crates/pyrometer/src/builtin_fns.rs index 1c3caeaa..519cc300 100644 --- a/crates/pyrometer/src/builtin_fns.rs +++ b/crates/pyrometer/src/builtin_fns.rs @@ -317,6 +317,15 @@ pub fn builtin_fns() -> AHashMap { Loc::Builtin, )))], ), + builtin_fn!( + name: Some(Identifier { + loc: Loc::Builtin, + name: "codehash".to_string(), + }), + attributes: vec![FunctionAttribute::Visibility(Visibility::External(Some( + Loc::Builtin, + )))], + ), ]; funcs .into_iter() diff --git a/crates/pyrometer/tests/test_data/cast.sol b/crates/pyrometer/tests/test_data/cast.sol index 392e792a..926ed199 100644 --- a/crates/pyrometer/tests/test_data/cast.sol +++ b/crates/pyrometer/tests/test_data/cast.sol @@ -1,319 +1,319 @@ // SPDX-License-Identifier: MIT or APACHE2 pragma solidity ^0.8.0; -type ShortString is bytes32; -type MyUint is uint256; -type MyInt is int256; +// type ShortString is bytes32; +// type MyUint is uint256; +// type MyInt is int256; contract Cast { - function u_int(uint256 x) public pure { - uint248 x_uint248 = uint248(x); - uint240 x_uint240 = uint240(x); - uint232 x_uint232 = uint232(x); - uint224 x_uint224 = uint224(x); - uint216 x_uint216 = uint216(x); - uint208 x_uint208 = uint208(x); - uint200 x_uint200 = uint200(x); - uint192 x_uint192 = uint192(x); - uint184 x_uint184 = uint184(x); - uint176 x_uint176 = uint176(x); - uint168 x_uint168 = uint168(x); - uint160 x_uint160 = uint160(x); - uint152 x_uint152 = uint152(x); - uint144 x_uint144 = uint144(x); - uint136 x_uint136 = uint136(x); - uint128 x_uint128 = uint128(x); - uint120 x_uint120 = uint120(x); - uint112 x_uint112 = uint112(x); - uint104 x_uint104 = uint104(x); - uint96 x_uint96 = uint96(x); - uint88 x_uint88 = uint88(x); - uint80 x_uint80 = uint80(x); - uint72 x_uint72 = uint72(x); - uint64 x_uint64 = uint64(x); - uint56 x_uint56 = uint56(x); - uint48 x_uint48 = uint48(x); - uint40 x_uint40 = uint40(x); - uint32 x_uint32 = uint32(x); - uint24 x_uint24 = uint24(x); - uint16 x_uint16 = uint16(x); - uint8 x_uint8 = uint8(x); - uint256 x_uint256 = uint256(x_uint8); - x_uint256; - x_uint248; - x_uint240; - x_uint232; - x_uint224; - x_uint216; - x_uint208; - x_uint200; - x_uint192; - x_uint184; - x_uint176; - x_uint168; - x_uint160; - x_uint152; - x_uint144; - x_uint136; - x_uint128; - x_uint120; - x_uint112; - x_uint104; - x_uint96; - x_uint88; - x_uint80; - x_uint72; - x_uint64; - x_uint56; - x_uint48; - x_uint40; - x_uint32; - x_uint24; - x_uint16; - } + // function u_int(uint256 x) public pure { + // uint248 x_uint248 = uint248(x); + // uint240 x_uint240 = uint240(x); + // uint232 x_uint232 = uint232(x); + // uint224 x_uint224 = uint224(x); + // uint216 x_uint216 = uint216(x); + // uint208 x_uint208 = uint208(x); + // uint200 x_uint200 = uint200(x); + // uint192 x_uint192 = uint192(x); + // uint184 x_uint184 = uint184(x); + // uint176 x_uint176 = uint176(x); + // uint168 x_uint168 = uint168(x); + // uint160 x_uint160 = uint160(x); + // uint152 x_uint152 = uint152(x); + // uint144 x_uint144 = uint144(x); + // uint136 x_uint136 = uint136(x); + // uint128 x_uint128 = uint128(x); + // uint120 x_uint120 = uint120(x); + // uint112 x_uint112 = uint112(x); + // uint104 x_uint104 = uint104(x); + // uint96 x_uint96 = uint96(x); + // uint88 x_uint88 = uint88(x); + // uint80 x_uint80 = uint80(x); + // uint72 x_uint72 = uint72(x); + // uint64 x_uint64 = uint64(x); + // uint56 x_uint56 = uint56(x); + // uint48 x_uint48 = uint48(x); + // uint40 x_uint40 = uint40(x); + // uint32 x_uint32 = uint32(x); + // uint24 x_uint24 = uint24(x); + // uint16 x_uint16 = uint16(x); + // uint8 x_uint8 = uint8(x); + // uint256 x_uint256 = uint256(x_uint8); + // x_uint256; + // x_uint248; + // x_uint240; + // x_uint232; + // x_uint224; + // x_uint216; + // x_uint208; + // x_uint200; + // x_uint192; + // x_uint184; + // x_uint176; + // x_uint168; + // x_uint160; + // x_uint152; + // x_uint144; + // x_uint136; + // x_uint128; + // x_uint120; + // x_uint112; + // x_uint104; + // x_uint96; + // x_uint88; + // x_uint80; + // x_uint72; + // x_uint64; + // x_uint56; + // x_uint48; + // x_uint40; + // x_uint32; + // x_uint24; + // x_uint16; + // } - function u_int_conc() public pure { - u_int(100); - } + // function u_int_conc() public pure { + // u_int(100); + // } - function i_nt(int256 x) public pure { - int248 x_int248 = int248(x); - int240 x_int240 = int240(x); - int232 x_int232 = int232(x); - int224 x_int224 = int224(x); - int216 x_int216 = int216(x); - int208 x_int208 = int208(x); - int200 x_int200 = int200(x); - int192 x_int192 = int192(x); - int184 x_int184 = int184(x); - int176 x_int176 = int176(x); - int168 x_int168 = int168(x); - int160 x_int160 = int160(x); - int152 x_int152 = int152(x); - int144 x_int144 = int144(x); - int136 x_int136 = int136(x); - int128 x_int128 = int128(x); - int120 x_int120 = int120(x); - int112 x_int112 = int112(x); - int104 x_int104 = int104(x); - int96 x_int96 = int96(x); - int88 x_int88 = int88(x); - int80 x_int80 = int80(x); - int72 x_int72 = int72(x); - int64 x_int64 = int64(x); - int56 x_int56 = int56(x); - int48 x_int48 = int48(x); - int40 x_int40 = int40(x); - int32 x_int32 = int32(x); - int24 x_int24 = int24(x); - int16 x_int16 = int16(x); - int8 x_int8 = int8(x); - int256 x_int256 = int256(x_int8); - x_int256; - x_int248; - x_int240; - x_int232; - x_int224; - x_int216; - x_int208; - x_int200; - x_int192; - x_int184; - x_int176; - x_int168; - x_int160; - x_int152; - x_int144; - x_int136; - x_int128; - x_int120; - x_int112; - x_int104; - x_int96; - x_int88; - x_int80; - x_int72; - x_int64; - x_int56; - x_int48; - x_int40; - x_int32; - x_int24; - x_int16; - } + // function i_nt(int256 x) public pure { + // int248 x_int248 = int248(x); + // int240 x_int240 = int240(x); + // int232 x_int232 = int232(x); + // int224 x_int224 = int224(x); + // int216 x_int216 = int216(x); + // int208 x_int208 = int208(x); + // int200 x_int200 = int200(x); + // int192 x_int192 = int192(x); + // int184 x_int184 = int184(x); + // int176 x_int176 = int176(x); + // int168 x_int168 = int168(x); + // int160 x_int160 = int160(x); + // int152 x_int152 = int152(x); + // int144 x_int144 = int144(x); + // int136 x_int136 = int136(x); + // int128 x_int128 = int128(x); + // int120 x_int120 = int120(x); + // int112 x_int112 = int112(x); + // int104 x_int104 = int104(x); + // int96 x_int96 = int96(x); + // int88 x_int88 = int88(x); + // int80 x_int80 = int80(x); + // int72 x_int72 = int72(x); + // int64 x_int64 = int64(x); + // int56 x_int56 = int56(x); + // int48 x_int48 = int48(x); + // int40 x_int40 = int40(x); + // int32 x_int32 = int32(x); + // int24 x_int24 = int24(x); + // int16 x_int16 = int16(x); + // int8 x_int8 = int8(x); + // int256 x_int256 = int256(x_int8); + // x_int256; + // x_int248; + // x_int240; + // x_int232; + // x_int224; + // x_int216; + // x_int208; + // x_int200; + // x_int192; + // x_int184; + // x_int176; + // x_int168; + // x_int160; + // x_int152; + // x_int144; + // x_int136; + // x_int128; + // x_int120; + // x_int112; + // x_int104; + // x_int96; + // x_int88; + // x_int80; + // x_int72; + // x_int64; + // x_int56; + // x_int48; + // x_int40; + // x_int32; + // x_int24; + // x_int16; + // } - function i_nt_conc_pos() public pure { - i_nt(100); - } + // function i_nt_conc_pos() public pure { + // i_nt(100); + // } - function i_nt_conc_neg() public pure { - i_nt(-100); - } + // function i_nt_conc_neg() public pure { + // i_nt(-100); + // } - function u_i_nt(int256 x) public pure { - uint256 x_uint256 = uint256(x); - int248 x_int248 = int248(x); - uint248 x_uint248 = uint248(x_int248); - int240 x_int240 = int240(x); - uint240 x_uint240 = uint240(x_int240); - int232 x_int232 = int232(x); - uint232 x_uint232 = uint232(x_int232); - int224 x_int224 = int224(x); - uint224 x_uint224 = uint224(x_int224); - int216 x_int216 = int216(x); - uint216 x_uint216 = uint216(x_int216); - int208 x_int208 = int208(x); - uint208 x_uint208 = uint208(x_int208); - int200 x_int200 = int200(x); - uint200 x_uint200 = uint200(x_int200); - int192 x_int192 = int192(x); - uint192 x_uint192 = uint192(x_int192); - int184 x_int184 = int184(x); - uint184 x_uint184 = uint184(x_int184); - int176 x_int176 = int176(x); - uint176 x_uint176 = uint176(x_int176); - int168 x_int168 = int168(x); - uint168 x_uint168 = uint168(x_int168); - int160 x_int160 = int160(x); - uint160 x_uint160 = uint160(x_int160); - int152 x_int152 = int152(x); - uint152 x_uint152 = uint152(x_int152); - int144 x_int144 = int144(x); - uint144 x_uint144 = uint144(x_int144); - int136 x_int136 = int136(x); - uint136 x_uint136 = uint136(x_int136); - int128 x_int128 = int128(x); - uint128 x_uint128 = uint128(x_int128); - int120 x_int120 = int120(x); - uint120 x_uint120 = uint120(x_int120); - int112 x_int112 = int112(x); - uint112 x_uint112 = uint112(x_int112); - int104 x_int104 = int104(x); - uint104 x_uint104 = uint104(x_int104); - int96 x_int96 = int96(x); - uint96 x_uint96 = uint96(x_int96); - int88 x_int88 = int88(x); - uint88 x_uint88 = uint88(x_int88); - int80 x_int80 = int80(x); - uint80 x_uint80 = uint80(x_int80); - int72 x_int72 = int72(x); - uint72 x_uint72 = uint72(x_int72); - int64 x_int64 = int64(x); - uint64 x_uint64 = uint64(x_int64); - int56 x_int56 = int56(x); - uint56 x_uint56 = uint56(x_int56); - int48 x_int48 = int48(x); - uint48 x_uint48 = uint48(x_int48); - int40 x_int40 = int40(x); - uint40 x_uint40 = uint40(x_int40); - int32 x_int32 = int32(x); - uint32 x_uint32 = uint32(x_int32); - int24 x_int24 = int24(x); - uint24 x_uint24 = uint24(x_int24); - int16 x_int16 = int16(x); - uint16 x_uint16 = uint16(x_int16); - int8 x_int8 = int8(x); - uint8 x_uint8 = uint8(x_int8); - x_uint256 = uint256(int256(x_int8)); - x_uint248; - x_uint240; - x_uint232; - x_uint224; - x_uint216; - x_uint208; - x_uint200; - x_uint192; - x_uint184; - x_uint176; - x_uint168; - x_uint160; - x_uint152; - x_uint144; - x_uint136; - x_uint128; - x_uint120; - x_uint112; - x_uint104; - x_uint96; - x_uint88; - x_uint80; - x_uint72; - x_uint64; - x_uint56; - x_uint48; - x_uint40; - x_uint32; - x_uint24; - x_uint16; - x_uint8; - } + // function u_i_nt(int256 x) public pure { + // uint256 x_uint256 = uint256(x); + // int248 x_int248 = int248(x); + // uint248 x_uint248 = uint248(x_int248); + // int240 x_int240 = int240(x); + // uint240 x_uint240 = uint240(x_int240); + // int232 x_int232 = int232(x); + // uint232 x_uint232 = uint232(x_int232); + // int224 x_int224 = int224(x); + // uint224 x_uint224 = uint224(x_int224); + // int216 x_int216 = int216(x); + // uint216 x_uint216 = uint216(x_int216); + // int208 x_int208 = int208(x); + // uint208 x_uint208 = uint208(x_int208); + // int200 x_int200 = int200(x); + // uint200 x_uint200 = uint200(x_int200); + // int192 x_int192 = int192(x); + // uint192 x_uint192 = uint192(x_int192); + // int184 x_int184 = int184(x); + // uint184 x_uint184 = uint184(x_int184); + // int176 x_int176 = int176(x); + // uint176 x_uint176 = uint176(x_int176); + // int168 x_int168 = int168(x); + // uint168 x_uint168 = uint168(x_int168); + // int160 x_int160 = int160(x); + // uint160 x_uint160 = uint160(x_int160); + // int152 x_int152 = int152(x); + // uint152 x_uint152 = uint152(x_int152); + // int144 x_int144 = int144(x); + // uint144 x_uint144 = uint144(x_int144); + // int136 x_int136 = int136(x); + // uint136 x_uint136 = uint136(x_int136); + // int128 x_int128 = int128(x); + // uint128 x_uint128 = uint128(x_int128); + // int120 x_int120 = int120(x); + // uint120 x_uint120 = uint120(x_int120); + // int112 x_int112 = int112(x); + // uint112 x_uint112 = uint112(x_int112); + // int104 x_int104 = int104(x); + // uint104 x_uint104 = uint104(x_int104); + // int96 x_int96 = int96(x); + // uint96 x_uint96 = uint96(x_int96); + // int88 x_int88 = int88(x); + // uint88 x_uint88 = uint88(x_int88); + // int80 x_int80 = int80(x); + // uint80 x_uint80 = uint80(x_int80); + // int72 x_int72 = int72(x); + // uint72 x_uint72 = uint72(x_int72); + // int64 x_int64 = int64(x); + // uint64 x_uint64 = uint64(x_int64); + // int56 x_int56 = int56(x); + // uint56 x_uint56 = uint56(x_int56); + // int48 x_int48 = int48(x); + // uint48 x_uint48 = uint48(x_int48); + // int40 x_int40 = int40(x); + // uint40 x_uint40 = uint40(x_int40); + // int32 x_int32 = int32(x); + // uint32 x_uint32 = uint32(x_int32); + // int24 x_int24 = int24(x); + // uint24 x_uint24 = uint24(x_int24); + // int16 x_int16 = int16(x); + // uint16 x_uint16 = uint16(x_int16); + // int8 x_int8 = int8(x); + // uint8 x_uint8 = uint8(x_int8); + // x_uint256 = uint256(int256(x_int8)); + // x_uint248; + // x_uint240; + // x_uint232; + // x_uint224; + // x_uint216; + // x_uint208; + // x_uint200; + // x_uint192; + // x_uint184; + // x_uint176; + // x_uint168; + // x_uint160; + // x_uint152; + // x_uint144; + // x_uint136; + // x_uint128; + // x_uint120; + // x_uint112; + // x_uint104; + // x_uint96; + // x_uint88; + // x_uint80; + // x_uint72; + // x_uint64; + // x_uint56; + // x_uint48; + // x_uint40; + // x_uint32; + // x_uint24; + // x_uint16; + // x_uint8; + // } - function b_ytes(bytes32 x) public pure { - bytes31 x_bytes31 = bytes31(x); - bytes30 x_bytes30 = bytes30(x); - bytes29 x_bytes29 = bytes29(x); - bytes28 x_bytes28 = bytes28(x); - bytes27 x_bytes27 = bytes27(x); - bytes26 x_bytes26 = bytes26(x); - bytes25 x_bytes25 = bytes25(x); - bytes24 x_bytes24 = bytes24(x); - bytes23 x_bytes23 = bytes23(x); - bytes22 x_bytes22 = bytes22(x); - bytes21 x_bytes21 = bytes21(x); - bytes20 x_bytes20 = bytes20(x); - bytes19 x_bytes19 = bytes19(x); - bytes18 x_bytes18 = bytes18(x); - bytes17 x_bytes17 = bytes17(x); - bytes16 x_bytes16 = bytes16(x); - bytes15 x_bytes15 = bytes15(x); - bytes14 x_bytes14 = bytes14(x); - bytes13 x_bytes13 = bytes13(x); - bytes12 x_bytes12 = bytes12(x); - bytes11 x_bytes11 = bytes11(x); - bytes10 x_bytes10 = bytes10(x); - bytes9 x_bytes9 = bytes9(x); - bytes8 x_bytes8 = bytes8(x); - bytes7 x_bytes7 = bytes7(x); - bytes6 x_bytes6 = bytes6(x); - bytes5 x_bytes5 = bytes5(x); - bytes4 x_bytes4 = bytes4(x); - bytes3 x_bytes3 = bytes3(x); - bytes2 x_bytes2 = bytes2(x); - bytes1 x_bytes1 = bytes1(x); - bytes32 x_bytes32 = bytes32(x_bytes1); - x_bytes31; - x_bytes30; - x_bytes29; - x_bytes28; - x_bytes27; - x_bytes26; - x_bytes25; - x_bytes24; - x_bytes23; - x_bytes22; - x_bytes21; - x_bytes20; - x_bytes19; - x_bytes18; - x_bytes17; - x_bytes16; - x_bytes15; - x_bytes14; - x_bytes13; - x_bytes12; - x_bytes11; - x_bytes10; - x_bytes9; - x_bytes8; - x_bytes7; - x_bytes6; - x_bytes5; - x_bytes4; - x_bytes3; - x_bytes2; - x_bytes1; - x_bytes32; - } + // function b_ytes(bytes32 x) public pure { + // bytes31 x_bytes31 = bytes31(x); + // bytes30 x_bytes30 = bytes30(x); + // bytes29 x_bytes29 = bytes29(x); + // bytes28 x_bytes28 = bytes28(x); + // bytes27 x_bytes27 = bytes27(x); + // bytes26 x_bytes26 = bytes26(x); + // bytes25 x_bytes25 = bytes25(x); + // bytes24 x_bytes24 = bytes24(x); + // bytes23 x_bytes23 = bytes23(x); + // bytes22 x_bytes22 = bytes22(x); + // bytes21 x_bytes21 = bytes21(x); + // bytes20 x_bytes20 = bytes20(x); + // bytes19 x_bytes19 = bytes19(x); + // bytes18 x_bytes18 = bytes18(x); + // bytes17 x_bytes17 = bytes17(x); + // bytes16 x_bytes16 = bytes16(x); + // bytes15 x_bytes15 = bytes15(x); + // bytes14 x_bytes14 = bytes14(x); + // bytes13 x_bytes13 = bytes13(x); + // bytes12 x_bytes12 = bytes12(x); + // bytes11 x_bytes11 = bytes11(x); + // bytes10 x_bytes10 = bytes10(x); + // bytes9 x_bytes9 = bytes9(x); + // bytes8 x_bytes8 = bytes8(x); + // bytes7 x_bytes7 = bytes7(x); + // bytes6 x_bytes6 = bytes6(x); + // bytes5 x_bytes5 = bytes5(x); + // bytes4 x_bytes4 = bytes4(x); + // bytes3 x_bytes3 = bytes3(x); + // bytes2 x_bytes2 = bytes2(x); + // bytes1 x_bytes1 = bytes1(x); + // bytes32 x_bytes32 = bytes32(x_bytes1); + // x_bytes31; + // x_bytes30; + // x_bytes29; + // x_bytes28; + // x_bytes27; + // x_bytes26; + // x_bytes25; + // x_bytes24; + // x_bytes23; + // x_bytes22; + // x_bytes21; + // x_bytes20; + // x_bytes19; + // x_bytes18; + // x_bytes17; + // x_bytes16; + // x_bytes15; + // x_bytes14; + // x_bytes13; + // x_bytes12; + // x_bytes11; + // x_bytes10; + // x_bytes9; + // x_bytes8; + // x_bytes7; + // x_bytes6; + // x_bytes5; + // x_bytes4; + // x_bytes3; + // x_bytes2; + // x_bytes1; + // x_bytes32; + // } function b_ytes_uint(bytes32 x) public pure returns (uint256) { uint256 x_uint256 = uint256(x); @@ -325,13 +325,13 @@ contract Cast { return x_bytes32; } - function u_int_addr(uint160 x) public pure returns (address) { - return address(x); - } + // function u_int_addr(uint160 x) public pure returns (address) { + // return address(x); + // } - function addr_uint(address x) public pure returns (uint160) { - return uint160(x); - } + // function addr_uint(address x) public pure returns (uint160) { + // return uint160(x); + // } function b_ytes_uint_conc() public pure returns (bytes32) { bytes32 round_trip = u_int_bytes(b_ytes_uint(hex"1337")); @@ -339,91 +339,91 @@ contract Cast { return round_trip; } - function addr_uint_conc() public pure returns (address) { - address round_trip = u_int_addr(addr_uint(address(1337))); - require(round_trip == address(1337)); - return round_trip; - } + // function addr_uint_conc() public pure returns (address) { + // address round_trip = u_int_addr(addr_uint(address(1337))); + // require(round_trip == address(1337)); + // return round_trip; + // } - function userStr() internal pure { - bytes32 x = bytes32("test"); - ShortString a = ShortString.wrap(x); - bytes32 b = ShortString.unwrap(a); - require(b == x); - } + // function userStr() internal pure { + // bytes32 x = bytes32("test"); + // ShortString a = ShortString.wrap(x); + // bytes32 b = ShortString.unwrap(a); + // require(b == x); + // } - function userUint() internal pure { - uint256 x = 100; - MyUint a = MyUint.wrap(x); - uint256 b = MyUint.unwrap(a); - require(b == x); - } + // function userUint() internal pure { + // uint256 x = 100; + // MyUint a = MyUint.wrap(x); + // uint256 b = MyUint.unwrap(a); + // require(b == x); + // } - function downcast_uint_conc() public pure returns (uint64) { - uint128 y = type(uint128).max; - y -= type(uint32).max; - return uint64(y); - } + // function downcast_uint_conc() public pure returns (uint64) { + // uint128 y = type(uint128).max; + // y -= type(uint32).max; + // return uint64(y); + // } - function downcast_int_conc() public pure returns (int64) { - int128 x = type(int128).max; - x -= type(int32).max; - return int64(x); - } + // function downcast_int_conc() public pure returns (int64) { + // int128 x = type(int128).max; + // x -= type(int32).max; + // return int64(x); + // } - function userInt() internal pure { - int256 x = -100; - MyInt a = MyInt.wrap(x); - int256 b = MyInt.unwrap(a); - require(b == x); - } + // function userInt() internal pure { + // int256 x = -100; + // MyInt a = MyInt.wrap(x); + // int256 b = MyInt.unwrap(a); + // require(b == x); + // } - function int_uint_int_conc() internal pure { - int256 a = -100; - uint256 b = uint(a); - int256 c = int(b); - require(-100 == c); - } + // function int_uint_int_conc() internal pure { + // int256 a = -100; + // uint256 b = uint(a); + // int256 c = int(b); + // require(-100 == c); + // } - function int_uint_int(int a) internal pure { - uint256 b = uint(a); - int256 c = int(b); - c; - } + // function int_uint_int(int a) internal pure { + // uint256 b = uint(a); + // int256 c = int(b); + // c; + // } } -contract FuncCast { - function a(bytes32 vs) public pure { - b(vs); - } +// contract FuncCast { +// function a(bytes32 vs) public pure { +// b(vs); +// } - function b(bytes32 vs) public pure returns (bool) { - bytes32 s = vs & - bytes32( - 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ); - // uint8 v = uint8((uint256(vs) >> 255) + 27); - return c(s); - } +// function b(bytes32 vs) public pure returns (bool) { +// bytes32 s = vs & +// bytes32( +// 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// ); +// // uint8 v = uint8((uint256(vs) >> 255) + 27); +// return c(s); +// } - function c(bytes32 s) public pure returns (bool) { - if ( - uint256(s) > - 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 - ) { - return true; - } else { - return false; - } - } +// function c(bytes32 s) public pure returns (bool) { +// if ( +// uint256(s) > +// 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 +// ) { +// return true; +// } else { +// return false; +// } +// } - function foo() public { - address ad = address(0); - (bool r, bytes memory k) = ad.call(hex""); - (r, k) = ad.delegatecall(hex""); - (r, k) = ad.delegatecall(msg.data); +// function foo() public { +// address ad = address(0); +// (bool r, bytes memory k) = ad.call(hex""); +// (r, k) = ad.delegatecall(hex""); +// (r, k) = ad.delegatecall(msg.data); - bytes memory data = hex"01234567"; - data; - } -} +// bytes memory data = hex"01234567"; +// data; +// } +// } diff --git a/crates/pyrometer/tests/test_data/function_calls.sol b/crates/pyrometer/tests/test_data/function_calls.sol index 36129bd4..fad4a930 100644 --- a/crates/pyrometer/tests/test_data/function_calls.sol +++ b/crates/pyrometer/tests/test_data/function_calls.sol @@ -28,6 +28,10 @@ contract B { a += x; } + function addTo(uint256 y) public { + a += y; + } + constructor(uint256 x) { a = x; } @@ -35,7 +39,7 @@ contract B { contract ExternalFuncCalls { function externalCall(uint256 x) public { - B(address(100)).addToA(x); + B(address(100)).addTo({y: x}); } function externalCall_conc() public { @@ -92,3 +96,55 @@ contract Disambiguation { function foo(address by, address from, address to, uint256 id) internal {} } + +contract S1 { + function a(uint x) internal pure virtual returns (uint) { + return 100; + } +} + +contract S2 { + function a(uint x) internal pure virtual returns (uint) { + return 10; + } + + function b(uint x) internal pure virtual returns (uint) { + return 10; + } +} + +contract C is S1, S2 { + function supers(uint128 x) public pure returns (uint) { + uint local_a = a(1); + uint super_a = super.a(1); + require(local_a == 50); + require(super_a == 10); + + uint local_super_b = b(x); + uint super_b = super.b(x); + require(local_super_b == super_b); + return 0; + } + + function a(uint256 x) internal pure override(A, B) returns (uint) { + return 50; + } +} + +contract D is S2, S1 { + function supers(uint128 x) public pure returns (uint) { + uint local_a = a(1); + uint super_a = super.a(1); + require(local_a == 50); + require(super_a == 100); + + uint local_super_b = b(x); + uint super_b = super.b(x); + require(local_super_b == super_b); + return 0; + } + + function a(uint256 x) internal pure override(A, B) returns (uint) { + return 50; + } +} diff --git a/crates/solc-expressions/src/context_builder/flattened.rs b/crates/solc-expressions/src/context_builder/flattened.rs index bf4369a0..f198bfd5 100644 --- a/crates/solc-expressions/src/context_builder/flattened.rs +++ b/crates/solc-expressions/src/context_builder/flattened.rs @@ -21,7 +21,7 @@ use shared::{ GraphError, IfElseChain, IntoExprErr, RangeArena, USE_DEBUG_SITE, }; use solang_parser::pt::{ - CodeLocation, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, + CodeLocation, ContractTy, Expression, Identifier, Loc, Statement, YulExpression, YulStatement, }; impl Flatten for T where @@ -922,6 +922,8 @@ pub trait Flatten: ctx.peek_expr_flag(self) ); + // ctx.debug_expr_stack(self); + match next { Todo(loc, err_str) => Err(ExprErr::Todo(loc, err_str.to_string())), // Flag expressions @@ -1013,7 +1015,7 @@ pub trait Flatten: | MoreEqual(loc) | And(loc) | Or(loc) => self.interp_cmp(arena, ctx, loc, next), // Function calling - MemberAccess(..) => self.interp_member_access(arena, ctx, next), + MemberAccess(..) => self.interp_member_access(arena, ctx, stack, next), FunctionCall(..) => self.interp_func_call(arena, ctx, next, None), FunctionCallBlock(_) => todo!(), NamedArgument(..) => Ok(()), @@ -1154,28 +1156,47 @@ pub trait Flatten: &mut self, arena: &mut RangeArena>, ctx: ContextNode, + stack: &mut Vec, next: FlatExpr, ) -> Result<(), ExprErr> { let FlatExpr::MemberAccess(loc, name) = next else { unreachable!() }; - let keep_member_on_stack = - matches!(ctx.peek_expr_flag(self), Some(ExprFlag::FunctionName(..))); - let member = ctx .pop_n_latest_exprs(1, loc, self) .into_expr_err(loc)? .swap_remove(0); - if keep_member_on_stack { - self.member_access(arena, ctx, member.clone(), name, loc)?; - // rearrange member to be in correct location relative to access + // If the member access points to a library function, we need to keep the + // member on the stack + let was_library_function = self.member_access(arena, ctx, member.clone(), name, loc)?; + if !was_library_function { + // It was unclear before knowing the node type of the member as to if + // the member function call took `self` or not (via a library function) + // + // Now that we know it was *not* a library function, we know it does not take self + // and therefore we *do* need to adjust the function call input length and + // *not* keep the member on the stack + match stack.get_mut(ctx.parse_idx(self)) { + Some(FlatExpr::FunctionCall(_, ref mut n)) => { + *n -= 1; + } + Some(FlatExpr::NamedFunctionCall(_, ref mut n)) => { + *n -= 1; + } + Some(_) | None => { + // it wasn't a function call at all, remove the member + } + } + Ok(()) + } else { + // it was a library function that by definition takes self. + // Pop off the access (for now), add the member back onto the stack + // and readd the access let access = ctx.pop_expr_latest(loc, self).unwrap().unwrap(); ctx.push_expr(member, self).into_expr_err(loc)?; ctx.push_expr(access, self).into_expr_err(loc) - } else { - self.member_access(arena, ctx, member.clone(), name, loc) } } @@ -1630,15 +1651,28 @@ pub trait Flatten: } else { None }; - let maybe_fn = if super_call { - self.find_super_func(arena, ctx, name.to_string(), n, maybe_names) - .into_expr_err(loc)? - } else { - self.find_func(arena, ctx, name.to_string(), n, maybe_names) - .into_expr_err(loc)? - }; - if let Some(fn_node) = maybe_fn { + if let Some((fn_node, input_reordering)) = self + .find_func(arena, ctx, name, n, &maybe_names, super_call) + .into_expr_err(loc)? + { + // reorder the inputs now that we have the function + let inputs = ctx.pop_n_latest_exprs(n, loc, self).into_expr_err(loc)?; + let mut tmp_inputs = vec![]; + tmp_inputs.resize(n, ExprRet::Null); + inputs.into_iter().enumerate().for_each(|(i, ret)| { + let target_idx = input_reordering[&i]; + tmp_inputs[target_idx] = ret; + }); + + // we reverse it because of how they are popped off the stack in the actual + // function call + tmp_inputs + .into_iter() + .rev() + .try_for_each(|i| ctx.push_expr(i, self)) + .into_expr_err(loc)?; + let as_var = ContextVar::maybe_from_user_ty(self, loc, fn_node.into()).unwrap(); let fn_var = ContextVarNode::from(self.add_node(as_var)); ctx.add_var(fn_var, self).into_expr_err(loc)?; @@ -1756,6 +1790,11 @@ pub trait Flatten: .pop_n_latest_exprs(n + 1, loc, self) .into_expr_err(loc)?; + println!( + "function call: {}", + ExprRet::Multi(func_and_inputs.clone()).debug_str(self) + ); + let func = func_and_inputs .first() .unwrap() @@ -1826,6 +1865,8 @@ pub trait Flatten: let inputs = ExprRet::Multi(inputs); + println!("inputs: {}", inputs.debug_str(self)); + if is_new_call { return self.new_call_inner(arena, ctx, func, inputs, loc); } @@ -1860,6 +1901,13 @@ pub trait Flatten: }; self.cast_inner(arena, ctx, ty, &builtin, inputs, loc) } + VarType::User(TypeNode::Unresolved(idx), _) => Err(ExprErr::ParseError( + loc, + format!( + "Could not call function: {:?}. The inputs may be wrong, or the this is a bug.", + self.node(idx) + ), + )), e => todo!("Unhandled ty: {e:?}"), } } diff --git a/crates/solc-expressions/src/func_call/func_caller.rs b/crates/solc-expressions/src/func_call/func_caller.rs index ce2fde4b..9a375bd7 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/internal_call.rs b/crates/solc-expressions/src/func_call/internal_call.rs index 3fcde89d..a548760e 100644 --- a/crates/solc-expressions/src/func_call/internal_call.rs +++ b/crates/solc-expressions/src/func_call/internal_call.rs @@ -4,13 +4,18 @@ use crate::helper::CallerHelper; use graph::{ elem::Elem, - nodes::{Builtin, Concrete, ContextNode, ExprRet, FunctionNode}, - AnalyzerBackend, GraphBackend, Node, VarType, + nodes::{ + Builtin, Concrete, ContextNode, ContextVarNode, ContractNode, ExprRet, FunctionNode, + StructNode, + }, + AnalyzerBackend, GraphBackend, Node, TypeNode, VarType, }; -use shared::{ExprErr, GraphError, RangeArena}; +use shared::{ExprErr, GraphError, NodeIdx, RangeArena}; use solang_parser::pt::Expression; +use std::collections::BTreeMap; + impl InternalFuncCaller for T where T: AnalyzerBackend + Sized + GraphBackend + CallerHelper { @@ -19,61 +24,85 @@ impl InternalFuncCaller for T where pub trait InternalFuncCaller: AnalyzerBackend + Sized + GraphBackend + CallerHelper { - fn find_super_func( + fn ordered_fn_inputs(&self, func: NodeIdx) -> Option> { + match self.node(func) { + Node::ContextVar(..) => match ContextVarNode::from(func).ty(self).unwrap() { + VarType::User(TypeNode::Func(func), _) => Some(func.ordered_param_names(self)), + VarType::User(TypeNode::Struct(strukt), _) => { + Some(strukt.ordered_new_param_names(self)) + } + VarType::User(TypeNode::Contract(con), _) => { + Some(con.ordered_new_param_names(self)) + } + other => None, + }, + Node::Function(..) => Some(FunctionNode::from(func).ordered_param_names(self)), + Node::Struct(..) => Some(StructNode::from(func).ordered_new_param_names(self)), + Node::Contract(..) => Some(ContractNode::from(func).ordered_new_param_names(self)), + other => None, + } + } + + fn construct_func_input_strings( &mut self, arena: &mut RangeArena>, ctx: ContextNode, - name: String, num_inputs: usize, - maybe_named: Option>, - ) -> Result, GraphError> { - if let Some(contract) = ctx.maybe_associated_contract(self)? { - let supers = contract.super_contracts(self); - let possible_funcs: Vec<_> = supers - .iter() - .filter_map(|con_node| { - con_node - .linearized_functions(self) - .ok()? - .into_iter() - .find(|(func_name, func_node)| { - if !func_name.starts_with(&name) { - return false; - } - - let params = func_node.params(self); - - if params.len() != num_inputs { - return false; - } - - if let Some(ref named) = maybe_named { - params - .iter() - .all(|param| named.contains(&&*param.name(self).unwrap())) - } else { - true - } - }) - .map(|(_, node)| node) - }) - .collect(); - self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs, maybe_named) - } else { - Ok(None) - } + ) -> Result>, GraphError> { + let stack = &ctx.underlying(self)?.expr_ret_stack; + let len = stack.len(); + let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); + inputs.reverse(); + Ok(inputs + .iter() + .map(|input| input.expect_single().ok()) + .map(|idx| { + let Some(idx) = idx else { + return vec![]; + }; + match VarType::try_from_idx(self, idx) { + Some(VarType::BuiltIn(bn, _)) => match self.node(bn) { + Node::Builtin(inner) => inner + .possible_upcast_builtins() + .into_iter() + .map(|b| b.basic_as_string()) + .collect(), + _ => { + unreachable!() + } + }, + Some(VarType::Concrete(cn)) => match self.node(cn) { + Node::Concrete(c) => c + .as_builtin() + .possible_upcast_builtins() + .into_iter() + .map(|b| b.basic_as_string()) + .collect(), + _ => { + unreachable!() + } + }, + Some(ty) => { + let Ok(ty_str) = ty.as_string(self) else { + return vec![]; + }; + vec![ty_str] + } + _ => vec![], + } + }) + .collect()) } - fn find_func( + fn potential_funcs( &mut self, - arena: &mut RangeArena>, ctx: ContextNode, - name: String, + name: &str, num_inputs: usize, - maybe_named: Option>, - ) -> Result, GraphError> { + maybe_named: &Option>, + ) -> Result, GraphError> { let funcs = ctx.visible_funcs(self)?; - let possible_funcs = funcs + let mut possible_funcs = funcs .iter() .filter(|func| func.name(self).unwrap().starts_with(&format!("{name}("))) .filter(|func| { @@ -91,65 +120,128 @@ pub trait InternalFuncCaller: }) .copied() .collect::>(); - self.find_func_inner(arena, ctx, name, num_inputs, possible_funcs, maybe_named) + possible_funcs.sort(); + possible_funcs.dedup(); + Ok(possible_funcs) } - fn find_func_inner( + fn potential_super_funcs( &mut self, arena: &mut RangeArena>, ctx: ContextNode, - name: String, + name: &str, num_inputs: usize, - possible_funcs: Vec, - maybe_named: Option>, - ) -> Result, GraphError> { - match possible_funcs.len() { - 0 => Ok(None), - 1 => Ok(Some(possible_funcs[0])), - _ => { - let stack = &ctx.underlying(self)?.expr_ret_stack; - let len = stack.len(); - let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); - inputs.reverse(); - let resizeables: Vec<_> = inputs - .iter() - .map(|input| input.expect_single().ok()) - .map(|idx| { - let Some(idx) = idx else { - return false; + maybe_named: &Option>, + ) -> Result, GraphError> { + if let Some(contract) = ctx.maybe_associated_contract(self)? { + let mut possible_funcs: Vec<_> = contract + .linearized_functions(self, true)? + .into_iter() + .filter(|(func_name, func_node)| { + if !func_name.starts_with(name) { + return false; + } + + let params = func_node.params(self); + + if params.len() != num_inputs { + return false; + } + + if let Some(ref named) = maybe_named { + params + .iter() + .all(|param| named.contains(&&*param.name(self).unwrap())) + } else { + true + } + }) + .map(|(_, node)| node) + .collect(); + possible_funcs.sort(); + possible_funcs.dedup(); + Ok(possible_funcs) + } else { + Ok(vec![]) + } + } + + fn find_func( + &mut self, + arena: &mut RangeArena>, + ctx: ContextNode, + name: &str, + num_inputs: usize, + maybe_named: &Option>, + is_super: bool, + ) -> Result)>, GraphError> { + let possible_funcs = if is_super { + self.potential_super_funcs(arena, ctx, name, num_inputs, maybe_named)? + } else { + self.potential_funcs(ctx, name, num_inputs, maybe_named)? + }; + + println!( + "potential funcs: {:?}", + possible_funcs + .iter() + .map(|i| i.name(self).unwrap()) + .collect::>() + ); + + let stack = &ctx.underlying(self)?.expr_ret_stack; + let len = stack.len(); + let mut inputs = stack[len.saturating_sub(num_inputs)..].to_vec(); + inputs.reverse(); + + let mut matched_funcs = possible_funcs + .iter() + .filter_map(|func| { + let ordered_pos_to_input_pos: BTreeMap<_, _> = + if let Some(input_names) = &maybe_named { + let Some(ordered_names) = self.ordered_fn_inputs(func.0.into()) else { + return None; }; - match VarType::try_from_idx(self, idx) { - Some(VarType::BuiltIn(bn, _)) => { - matches!( - self.node(bn), - Node::Builtin(Builtin::Uint(_)) - | Node::Builtin(Builtin::Int(_)) - | Node::Builtin(Builtin::Bytes(_)) - ) - } - Some(VarType::Concrete(c)) => { - matches!( - self.node(c), - Node::Concrete(Concrete::Uint(_, _)) - | Node::Concrete(Concrete::Int(_, _)) - | Node::Concrete(Concrete::Bytes(_, _)) - ) - } - _ => false, + + if ordered_names[..] != input_names[..] { + ordered_names + .iter() + .enumerate() + .filter_map(|(i, n)| { + Some((i, input_names.iter().position(|k| k == n)?)) + }) + .collect() + } else { + (0..input_names.len()).map(|i| (i, i)).collect() } - }) - .collect(); - - let inputs = ExprRet::Multi(inputs); - Ok(self.disambiguate_fn_call( - arena, - &name, - resizeables, - &inputs, - &possible_funcs, - maybe_named, - )) - } + } else { + (0..num_inputs).map(|i| (i, i)).collect() + }; + let tys = func + .params(self) + .iter() + .map(|param| VarType::try_from_idx(self, param.ty(self).unwrap()).unwrap()) + .collect::>(); + + let all = tys.iter().enumerate().all(|(true_idx, ty)| { + let input_pos = ordered_pos_to_input_pos.get(&true_idx).unwrap(); + inputs[*input_pos] + .implicitly_castable_to(self, ty) + .unwrap_or(false) + }); + + if all { + Some((*func, ordered_pos_to_input_pos)) + } else { + None + } + }) + .collect::>(); + + if matched_funcs.len() == 1 { + Ok(Some(matched_funcs.swap_remove(0))) + } else { + Ok(None) } } } 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 cf712062..53b4dca0 100644 --- a/crates/solc-expressions/src/func_call/intrinsic_call/address.rs +++ b/crates/solc-expressions/src/func_call/intrinsic_call/address.rs @@ -22,8 +22,17 @@ pub trait AddressCaller: AnalyzerBackend + 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)?; - Ok(()) + .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 @@ -33,8 +42,16 @@ pub trait AddressCaller: AnalyzerBackend + 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)?; - Ok(()) + .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)?; + 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) } _ => Err(ExprErr::FunctionNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/builtin_access.rs b/crates/solc-expressions/src/member_access/builtin_access.rs index c537cb3a..1831fd03 100644 --- a/crates/solc-expressions/src/member_access/builtin_access.rs +++ b/crates/solc-expressions/src/member_access/builtin_access.rs @@ -26,66 +26,22 @@ pub trait BuiltinAccess: name: &str, is_storage: bool, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { tracing::trace!("Looking for builtin member function"); if let Some(ret) = self.library_func_search(ctx, node.0.into(), name) { - Ok(ret) + Ok((ret, true)) } else { match node.underlying(self).into_expr_err(loc)?.clone() { Builtin::Address | Builtin::AddressPayable | Builtin::Payable => { match name { - "delegatecall" - | "call" - | "staticcall" - | "delegatecall(address, bytes)" - | "call(address, bytes)" - | "staticcall(address, bytes)" => { + "delegatecall" | "call" | "staticcall" | "code" | "codehash" + | "balance" | "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)) + Ok((ExprRet::Single(func_node), true)) } - "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)) - } - "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)) - } - "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)) - } - _ if name.starts_with("send") => { - let bn = self.builtin_or_add(Builtin::Bool); - 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)) - } - _ if name.starts_with("transfer") => Ok(ExprRet::Multi(vec![])), _ => Err(ExprErr::MemberAccessNotFound( loc, format!( @@ -105,7 +61,7 @@ pub trait BuiltinAccess: Builtin::String => match name.split('(').collect::>()[0] { "concat" => { let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), false)) } _ => Err(ExprErr::MemberAccessNotFound( loc, @@ -129,7 +85,7 @@ pub trait BuiltinAccess: Builtin::DynamicBytes => match name.split('(').collect::>()[0] { "concat" => { let fn_node = self.builtin_fn_or_maybe_add("concat").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), false)) } _ => Err(ExprErr::MemberAccessNotFound( loc, @@ -143,7 +99,7 @@ pub trait BuiltinAccess: if name.starts_with("push") { if is_storage { let fn_node = self.builtin_fn_or_maybe_add("push").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), true)) } else { Err(ExprErr::NonStoragePush( loc, @@ -153,7 +109,7 @@ pub trait BuiltinAccess: } else if name.starts_with("pop") { if is_storage { let fn_node = self.builtin_fn_or_maybe_add("pop").unwrap(); - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), true)) } else { Err(ExprErr::NonStoragePush( loc, @@ -210,7 +166,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } "min" => { let min = max * I256::from(-1i32) - I256::from(1i32); @@ -225,7 +181,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } e => Err(ExprErr::MemberAccessNotFound( loc, @@ -254,7 +210,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } "min" => { let min = U256::zero(); @@ -269,12 +225,7 @@ pub trait BuiltinAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) - } - "call" | "delegatecall" | "staticcall" if size == 160 => { - let builtin_name = name.split('(').collect::>()[0]; - let func_node = self.builtin_fn_or_maybe_add(builtin_name).unwrap(); - Ok(ExprRet::Single(func_node)) + Ok((ExprRet::Single(cvar), false)) } e => Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/contract_access.rs b/crates/solc-expressions/src/member_access/contract_access.rs index e963dbac..4be8268c 100644 --- a/crates/solc-expressions/src/member_access/contract_access.rs +++ b/crates/solc-expressions/src/member_access/contract_access.rs @@ -1,3 +1,4 @@ +use crate::member_access::library_access::LibraryAccess; use graph::{ nodes::{Builtin, Concrete, ContextNode, ContextVar, ContextVarNode, ContractNode, ExprRet}, AnalyzerBackend, ContextEdge, Edge, @@ -18,7 +19,7 @@ pub trait ContractAccess: AnalyzerBackend con_node: ContractNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { tracing::trace!( "Contract member access: {}.{}", con_node @@ -28,10 +29,11 @@ pub trait ContractAccess: AnalyzerBackend name ); - if let Some(func) = con_node - .funcs(self) + if let Some((_, func)) = con_node + .linearized_functions(self, false) + .into_expr_err(loc)? .into_iter() - .find(|func_node| func_node.name(self).unwrap() == name) + .find(|(func_name, func_node)| func_name == name) { if let Some(func_cvar) = ContextVar::maybe_from_user_ty(self, loc, func.0.into()) { let fn_node = self.add_node(func_cvar); @@ -39,7 +41,7 @@ pub trait ContractAccess: AnalyzerBackend if let Some(parent) = maybe_parent { self.add_edge(fn_node, parent, Edge::Context(ContextEdge::FuncAccess)); } - Ok(ExprRet::Single(fn_node)) + Ok((ExprRet::Single(fn_node), false)) } else { Err(ExprErr::MemberAccessNotFound( loc, @@ -49,6 +51,8 @@ pub trait ContractAccess: AnalyzerBackend ), )) } + } else if let Some(ret) = self.library_func_search(ctx, con_node.0.into(), name) { + Ok((ret, true)) } else if let Some(func) = con_node .structs(self) .into_iter() @@ -64,7 +68,7 @@ pub trait ContractAccess: AnalyzerBackend Edge::Context(ContextEdge::StructAccess), ); } - return Ok(ExprRet::Single(struct_node)); + return Ok((ExprRet::Single(struct_node), false)); } else { return Err(ExprErr::MemberAccessNotFound( loc, @@ -75,7 +79,7 @@ pub trait ContractAccess: AnalyzerBackend )); } } else { - match name { + let res = match name { "name" => { let c = Concrete::from(con_node.name(self).unwrap()); let cnode = self.add_node(c); @@ -84,7 +88,7 @@ pub trait ContractAccess: AnalyzerBackend 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)); - return Ok(ExprRet::Single(node)); + Ok(ExprRet::Single(node)) } "creationCode" | "runtimeCode" => { let bn = self.builtin_or_add(Builtin::DynamicBytes); @@ -93,7 +97,7 @@ pub trait ContractAccess: AnalyzerBackend 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)); - return Ok(ExprRet::Single(node)); + Ok(ExprRet::Single(node)) } "interfaceId" => { // TODO: actually calculate this @@ -103,7 +107,7 @@ pub trait ContractAccess: AnalyzerBackend 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)); - return Ok(ExprRet::Single(node)); + Ok(ExprRet::Single(node)) } _ => { // try to match just prefix @@ -137,7 +141,7 @@ pub trait ContractAccess: AnalyzerBackend )) } } else { - return Err(ExprErr::ContractFunctionNotFound( + Err(ExprErr::ContractFunctionNotFound( loc, format!( "No function or struct with name \"{name}\" in contract: {:?}. Functions: {:#?}", @@ -148,10 +152,11 @@ pub trait ContractAccess: AnalyzerBackend .map(|func| func.name(self).unwrap()) .collect::>() ), - )); + )) } } - } + }; + Ok((res?, false)) } } } diff --git a/crates/solc-expressions/src/member_access/enum_access.rs b/crates/solc-expressions/src/member_access/enum_access.rs index 22131e96..d7cafc63 100644 --- a/crates/solc-expressions/src/member_access/enum_access.rs +++ b/crates/solc-expressions/src/member_access/enum_access.rs @@ -24,7 +24,7 @@ pub trait EnumAccess: enum_node: EnumNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { tracing::trace!("Enum member access: {}", name); if let Some(variant) = enum_node @@ -39,9 +39,9 @@ pub trait EnumAccess: let cvar = self.add_node(var); ctx.add_var(cvar.into(), self).into_expr_err(loc)?; self.add_edge(cvar, ctx, Edge::Context(ContextEdge::Variable)); - Ok(ExprRet::Single(cvar)) + Ok((ExprRet::Single(cvar), false)) } else if let Some(ret) = self.library_func_search(ctx, enum_node.0.into(), name) { - Ok(ret) + Ok((ret, true)) } else { Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/library_access.rs b/crates/solc-expressions/src/member_access/library_access.rs index 3c3c9fc3..643dbd1c 100644 --- a/crates/solc-expressions/src/member_access/library_access.rs +++ b/crates/solc-expressions/src/member_access/library_access.rs @@ -20,9 +20,11 @@ pub trait LibraryAccess: AnalyzerBackend + ty: NodeIdx, func_name: &str, ) -> Option { + println!("searching for {func_name}"); self.possible_library_funcs(ctx, ty) .iter() .filter_map(|func| { + println!("func: {:?}", func.name(self).unwrap()); if let Ok(name) = func.name(self) { Some((name, func)) } else { diff --git a/crates/solc-expressions/src/member_access/member_trait.rs b/crates/solc-expressions/src/member_access/member_trait.rs index ae35cb1d..46571c79 100644 --- a/crates/solc-expressions/src/member_access/member_trait.rs +++ b/crates/solc-expressions/src/member_access/member_trait.rs @@ -41,10 +41,11 @@ pub trait MemberAccess: member: ExprRet, name: &str, loc: Loc, - ) -> Result<(), ExprErr> { + ) -> Result { // TODO: this is wrong as it overwrites a function call of the form elem.length(...) i believe if name == "length" { - return self.length(arena, ctx, member, loc); + self.length(arena, ctx, member, loc)?; + return Ok(false); } self.match_member(ctx, member, name, loc) @@ -57,18 +58,25 @@ pub trait MemberAccess: member: ExprRet, name: &str, loc: Loc, - ) -> Result<(), ExprErr> { + ) -> Result { match member { ExprRet::Single(idx) | ExprRet::SingleLiteral(idx) => { - ctx.push_expr(self.member_access_inner(ctx, idx, name, loc)?, self) - .into_expr_err(loc)?; - Ok(()) + let (inner, was_lib_func) = self.member_access_inner(ctx, idx, name, loc)?; + ctx.push_expr(inner, self).into_expr_err(loc)?; + Ok(was_lib_func) } - ExprRet::Multi(inner) => inner - .into_iter() - .try_for_each(|member| self.match_member(ctx, member, name, loc)), - ExprRet::CtxKilled(kind) => ctx.kill(self, loc, kind).into_expr_err(loc), - ExprRet::Null => Ok(()), + ExprRet::Multi(inner) => { + inner.into_iter().try_for_each(|member| { + let _ = self.match_member(ctx, member, name, loc)?; + Ok(()) + })?; + Ok(false) + } + ExprRet::CtxKilled(kind) => { + ctx.kill(self, loc, kind).into_expr_err(loc)?; + Ok(false) + } + ExprRet::Null => Ok(false), } } @@ -79,7 +87,7 @@ pub trait MemberAccess: member_idx: NodeIdx, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { match self.node(member_idx) { Node::ContextVar(_) => { self.member_access_var(ctx, ContextVarNode::from(member_idx), name, loc) @@ -100,8 +108,14 @@ pub trait MemberAccess: } Node::Enum(_c) => self.enum_member_access(ctx, EnumNode::from(member_idx), name, loc), Node::Ty(_ty) => self.ty_member_access(ctx, TyNode::from(member_idx), name, loc), - Node::Msg(_msg) => self.msg_access(ctx, name, loc), - Node::Block(_b) => self.block_access(ctx, name, loc), + Node::Msg(_msg) => { + let res = self.msg_access(ctx, name, loc)?; + Ok((res, false)) + } + Node::Block(_b) => { + let res = self.block_access(ctx, name, loc)?; + Ok((res, false)) + } Node::Builtin(ref _b) => { self.builtin_member_access(ctx, BuiltInNode::from(member_idx), name, false, loc) } @@ -126,58 +140,58 @@ pub trait MemberAccess: cvar.ty ); match &cvar.ty { - VarType::User(TypeNode::Contract(con_node), _) => { - let cnode = *con_node; - let mut funcs = cnode.linearized_functions(self).into_expr_err(loc)?; - self - .possible_library_funcs(ctx, cnode.0.into()) - .into_iter() - .for_each(|func| { - let name = func.name(self).unwrap(); - funcs.entry(name).or_insert(func); - }); - funcs.values().copied().collect() - }, - VarType::BuiltIn(bn, _) => self - .possible_library_funcs(ctx, bn.0.into()) - .into_iter() - .collect::>(), - VarType::Concrete(cnode) => { - let b = cnode.underlying(self).unwrap().as_builtin(); - let bn = self.builtin_or_add(b); - self.possible_library_funcs(ctx, bn) + VarType::User(TypeNode::Contract(con_node), _) => { + let cnode = *con_node; + let mut funcs = cnode.linearized_functions(self, false).into_expr_err(loc)?; + self + .possible_library_funcs(ctx, cnode.0.into()) .into_iter() - .collect::>() - } - VarType::User(TypeNode::Struct(sn), _) => self - .possible_library_funcs(ctx, sn.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Enum(en), _) => self - .possible_library_funcs(ctx, en.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Ty(ty), _) => self - .possible_library_funcs(ctx, ty.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Error(err), _) => self - .possible_library_funcs(ctx, err.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Func(func_node), _) => self - .possible_library_funcs(ctx, func_node.0.into()) - .into_iter() - .collect::>(), - VarType::User(TypeNode::Unresolved(n), _) => { - match self.node(*n) { - Node::Unresolved(ident) => { - return Err(ExprErr::Unresolved(loc, format!("The type \"{}\" is currently unresolved but should have been resolved by now. This is a bug.", ident.name))) + .for_each(|func| { + let name = func.name(self).unwrap(); + funcs.entry(name).or_insert(func); + }); + funcs.values().copied().collect() + }, + VarType::BuiltIn(bn, _) => self + .possible_library_funcs(ctx, bn.0.into()) + .into_iter() + .collect::>(), + VarType::Concrete(cnode) => { + let b = cnode.underlying(self).unwrap().as_builtin(); + let bn = self.builtin_or_add(b); + self.possible_library_funcs(ctx, bn) + .into_iter() + .collect::>() + } + VarType::User(TypeNode::Struct(sn), _) => self + .possible_library_funcs(ctx, sn.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Enum(en), _) => self + .possible_library_funcs(ctx, en.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Ty(ty), _) => self + .possible_library_funcs(ctx, ty.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Error(err), _) => self + .possible_library_funcs(ctx, err.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Func(func_node), _) => self + .possible_library_funcs(ctx, func_node.0.into()) + .into_iter() + .collect::>(), + VarType::User(TypeNode::Unresolved(n), _) => { + match self.node(*n) { + Node::Unresolved(ident) => { + return Err(ExprErr::Unresolved(loc, format!("The type \"{}\" is currently unresolved but should have been resolved by now. This is a bug.", ident.name))) + } + _ => unreachable!() } - _ => unreachable!() } } - } } Node::Contract(_) => ContractNode::from(member_idx).funcs(self), Node::Concrete(_) @@ -206,7 +220,7 @@ pub trait MemberAccess: cvar: ContextVarNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { match cvar.ty(self).into_expr_err(loc)? { VarType::User(TypeNode::Struct(struct_node), _) => { self.struct_var_member_access(ctx, cvar, *struct_node, name, loc) @@ -215,7 +229,7 @@ pub trait MemberAccess: self.enum_member_access(ctx, *enum_node, name, loc) } VarType::User(TypeNode::Func(func_node), _) => { - self.func_member_access(ctx, *func_node, name, loc) + Ok((self.func_member_access(ctx, *func_node, name, loc)?, false)) } VarType::User(TypeNode::Ty(ty_node), _) => { self.ty_member_access(ctx, *ty_node, name, loc) @@ -255,12 +269,12 @@ pub trait MemberAccess: ty_node: TyNode, name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { let name = name.split('(').collect::>()[0]; if let Some(func) = self.library_func_search(ctx, ty_node.0.into(), name) { - Ok(func) + Ok((func, true)) } else if let Some(func) = self.builtin_fn_or_maybe_add(name) { - Ok(ExprRet::Single(func)) + Ok((ExprRet::Single(func), false)) } else { Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/member_access/struct_access.rs b/crates/solc-expressions/src/member_access/struct_access.rs index d535e5bf..2a88fc2d 100644 --- a/crates/solc-expressions/src/member_access/struct_access.rs +++ b/crates/solc-expressions/src/member_access/struct_access.rs @@ -24,13 +24,13 @@ pub trait StructAccess: struct_node: StructNode, field_name: &str, loc: Loc, - ) -> Result { + ) -> Result<(ExprRet, bool), ExprErr> { let cvar_name = cvar.name(self).unwrap(); let name = format!("{cvar_name}.{field_name}"); tracing::trace!("Struct member access: {cvar_name}.{field_name}"); if let Some(field) = cvar.field_of_struct(field_name, self).into_expr_err(loc)? { - return Ok(ExprRet::Single(field.into())); + return Ok((ExprRet::Single(field.into()), false)); } if let Some(field) = struct_node.find_field(self, field_name) { @@ -46,12 +46,12 @@ pub trait StructAccess: cvar.first_version(self), Edge::Context(ContextEdge::AttrAccess("field")), ); - Ok(ExprRet::Single(fc_node)) + Ok((ExprRet::Single(fc_node), false)) } else { panic!("Couldn't create field variable"); } } else if let Some(func) = self.library_func_search(ctx, struct_node.0.into(), &name) { - Ok(func) + Ok((func, true)) } else { Err(ExprErr::MemberAccessNotFound( loc, diff --git a/crates/solc-expressions/src/require.rs b/crates/solc-expressions/src/require.rs index 984416ba..7e82c489 100644 --- a/crates/solc-expressions/src/require.rs +++ b/crates/solc-expressions/src/require.rs @@ -969,7 +969,9 @@ pub trait Require: AnalyzerBackend + Variable + BinOp + Sized { } RangeOp::Neq => { // check if contains - let mut elem = Elem::from(const_var.latest_version_or_inherited_in_ctx(ctx, self)); + let mut elem = Elem::from(const_var.latest_version_or_inherited_in_ctx(ctx, self)) + .minimize(self, arena) + .unwrap(); // potentially add the const var as a range exclusion if let Some(Ordering::Equal) = nonconst_range