From 9bebce16aad60cd2594b8191034af3f156e006fb Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 19:03:33 +0530 Subject: [PATCH 01/12] feat: skeleton --- crates/sema/src/typeck/mod.rs | 87 ++++++++++++++++++++++ tests/ui/resolve/contract_storage_size.sol | 17 +++++ 2 files changed, 104 insertions(+) create mode 100644 tests/ui/resolve/contract_storage_size.sol diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index c2bde199..26608672 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -1,8 +1,10 @@ use crate::{ ast_lowering::resolve::{Declaration, Declarations}, + eval::ConstantEvaluator, hir::{self, Res}, ty::{Gcx, Ty}, }; +use alloy_primitives::U256; use rayon::prelude::*; use solar_data_structures::{map::FxHashSet, parallel}; @@ -15,9 +17,94 @@ pub(crate) fn check(gcx: Gcx<'_>) { gcx.hir.par_source_ids().for_each(|id| { check_duplicate_definitions(gcx, &gcx.symbol_resolver.source_scopes[id]); }), + gcx.hir.par_contract_ids().for_each(|id| { + check_storage_size_upper_bound(gcx, id); + }), ); } +/// Checks for violation of maximum storage size to ensure slot allocation algorithms works. +fn check_storage_size_upper_bound(gcx: Gcx<'_>, id: hir::ContractId) -> U256 { + let contract_items = gcx.hir.contract_items(id); + let mut total_size = U256::ZERO; + for item in contract_items { + if let hir::Item::Variable(variable) = item { + // Skip constant and immutable variables + if variable.mutability.is_none() { + let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx); + let Some(sz) = total_size.checked_add(size_contribution) else { + gcx.dcx().err("overflowed storage slots").emit(); + return U256::ZERO; + }; + total_size += sz; + } + } + } + total_size +} + +fn item_ty_upper_bound_size<'a, 'hir>(item: &hir::Item<'a, 'hir>, gcx: Gcx<'_>) -> U256 { + match item { + hir::Item::Function(_) | hir::Item::Contract(_) => U256::from(1), + hir::Item::Variable(variable) => variable_ty_upper_bound_size(&variable.ty.kind, gcx), + hir::Item::Udvt(udvt) => variable_ty_upper_bound_size(&udvt.ty.kind, gcx), + hir::Item::Struct(strukt) => { + let mut total_size = U256::from(1); + + for field_id in strukt.fields { + let variable = gcx.hir.variable(*field_id); + let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx); + let Some(sz) = total_size.checked_add(size_contribution) else { + gcx.dcx().err("overflowed storage slots").emit(); + return U256::ZERO; + }; + total_size = sz; + } + total_size + } + hir::Item::Enum(_) | hir::Item::Event(_) | hir::Item::Error(_) => { + // Enum and events cannot be types of storage variables + unreachable!("illegal values") + } + } +} + +fn variable_ty_upper_bound_size(var_ty: &hir::TypeKind<'_>, gcx: Gcx<'_>) -> U256 { + match &var_ty { + hir::TypeKind::Elementary(_) | hir::TypeKind::Function(_) | hir::TypeKind::Mapping(_) => { + U256::from(1) + } + hir::TypeKind::Array(array) => { + // For fixed size arrays the formula is length * size of base item + if let Some(len_expr) = array.size { + // Evaluate the length expression in array declaration + let mut e = ConstantEvaluator::new(gcx); + let arr_len = e.eval(len_expr).unwrap().data; // `.eval()` emits errors beforehand + + // Estimate the upper bound size of each individual element + let elem_size = variable_ty_upper_bound_size(&array.element.kind, gcx); + + let Some(size_contribution) = arr_len.checked_mul(elem_size) else { + gcx.dcx().err("overflowed storage slots").emit(); + return U256::ZERO; + }; + + size_contribution + } else { + // For dynamic size arrays + U256::from(1) + } + } + hir::TypeKind::Custom(item_id) => { + let item = gcx.hir.item(*item_id); + item_ty_upper_bound_size(&item, gcx) + } + hir::TypeKind::Err(_) => { + unreachable!() + } + } +} + /// Checks for definitions that have the same name and parameter types in the given scope. fn check_duplicate_definitions(gcx: Gcx<'_>, scope: &Declarations) { let is_duplicate = |a: Declaration, b: Declaration| -> bool { diff --git a/tests/ui/resolve/contract_storage_size.sol b/tests/ui/resolve/contract_storage_size.sol new file mode 100644 index 00000000..b71f4dc6 --- /dev/null +++ b/tests/ui/resolve/contract_storage_size.sol @@ -0,0 +1,17 @@ +contract B { + uint b; +} + +contract A { + struct Person { + string first; + string middle; + string last; + } + + Person my; + mapping(string => uint256) public a; + Person[] public b; + bool c; + B d; +} From 16e28d4b3a21e0e87294899c7c48e13d493dcb37 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 19:04:22 +0530 Subject: [PATCH 02/12] clippy --- crates/sema/src/typeck/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 26608672..6dad442a 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -43,7 +43,7 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, id: hir::ContractId) -> U256 { total_size } -fn item_ty_upper_bound_size<'a, 'hir>(item: &hir::Item<'a, 'hir>, gcx: Gcx<'_>) -> U256 { +fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> U256 { match item { hir::Item::Function(_) | hir::Item::Contract(_) => U256::from(1), hir::Item::Variable(variable) => variable_ty_upper_bound_size(&variable.ty.kind, gcx), From 0ba59889266d245edb85344bb29f40443daed777 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 19:05:27 +0530 Subject: [PATCH 03/12] fix --- crates/sema/src/typeck/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 6dad442a..61fdc29c 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -24,7 +24,7 @@ pub(crate) fn check(gcx: Gcx<'_>) { } /// Checks for violation of maximum storage size to ensure slot allocation algorithms works. -fn check_storage_size_upper_bound(gcx: Gcx<'_>, id: hir::ContractId) -> U256 { +fn check_storage_size_upper_bound(gcx: Gcx<'_>, id: hir::ContractId) { let contract_items = gcx.hir.contract_items(id); let mut total_size = U256::ZERO; for item in contract_items { @@ -34,13 +34,12 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, id: hir::ContractId) -> U256 { let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx); let Some(sz) = total_size.checked_add(size_contribution) else { gcx.dcx().err("overflowed storage slots").emit(); - return U256::ZERO; + return; }; total_size += sz; } } } - total_size } fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> U256 { From 23122d5d8b9fe5160f4d1913696e0aef0f15645a Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 19:54:29 +0530 Subject: [PATCH 04/12] feat --- crates/sema/src/typeck/mod.rs | 41 +++++++++++-------- tests/ui/resolve/contract_storage_size.sol | 17 -------- .../ui/typeck/contract_storage_size_check.sol | 39 ++++++++++++++++++ 3 files changed, 63 insertions(+), 34 deletions(-) delete mode 100644 tests/ui/resolve/contract_storage_size.sol create mode 100644 tests/ui/typeck/contract_storage_size_check.sol diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 61fdc29c..3ad05169 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -24,42 +24,49 @@ pub(crate) fn check(gcx: Gcx<'_>) { } /// Checks for violation of maximum storage size to ensure slot allocation algorithms works. -fn check_storage_size_upper_bound(gcx: Gcx<'_>, id: hir::ContractId) { - let contract_items = gcx.hir.contract_items(id); +/// Reference: https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/analysis/ContractLevelChecker.cpp#L556C1-L570C2 +fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { + let contract_items = gcx.hir.contract_items(contract_id); let mut total_size = U256::ZERO; for item in contract_items { if let hir::Item::Variable(variable) = item { // Skip constant and immutable variables if variable.mutability.is_none() { - let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx); + let Some(size_contribution) = variable_ty_upper_bound_size(&variable.ty.kind, gcx) + else { + gcx.dcx().err("overflowed storage slots").emit(); + return; + }; let Some(sz) = total_size.checked_add(size_contribution) else { gcx.dcx().err("overflowed storage slots").emit(); return; }; - total_size += sz; + total_size = sz; } } } + //let c = gcx.hir.contract(contract_id).name; + //println!("{total_size} - {c}"); } -fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> U256 { +fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> Option { match item { - hir::Item::Function(_) | hir::Item::Contract(_) => U256::from(1), + hir::Item::Function(_) | hir::Item::Contract(_) => Some(U256::from(1)), hir::Item::Variable(variable) => variable_ty_upper_bound_size(&variable.ty.kind, gcx), hir::Item::Udvt(udvt) => variable_ty_upper_bound_size(&udvt.ty.kind, gcx), hir::Item::Struct(strukt) => { + // Reference https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/ast/Types.cpp#L2303C1-L2309C2 let mut total_size = U256::from(1); - for field_id in strukt.fields { let variable = gcx.hir.variable(*field_id); - let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx); + let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx)?; let Some(sz) = total_size.checked_add(size_contribution) else { gcx.dcx().err("overflowed storage slots").emit(); - return U256::ZERO; + return None; }; total_size = sz; } - total_size + Some(total_size) } hir::Item::Enum(_) | hir::Item::Event(_) | hir::Item::Error(_) => { // Enum and events cannot be types of storage variables @@ -68,30 +75,30 @@ fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> U256 { } } -fn variable_ty_upper_bound_size(var_ty: &hir::TypeKind<'_>, gcx: Gcx<'_>) -> U256 { +fn variable_ty_upper_bound_size(var_ty: &hir::TypeKind<'_>, gcx: Gcx<'_>) -> Option { match &var_ty { hir::TypeKind::Elementary(_) | hir::TypeKind::Function(_) | hir::TypeKind::Mapping(_) => { - U256::from(1) + Some(U256::from(1)) } hir::TypeKind::Array(array) => { - // For fixed size arrays the formula is length * size of base item + // Reference: https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/ast/Types.cpp#L1800C1-L1806C2 if let Some(len_expr) = array.size { // Evaluate the length expression in array declaration let mut e = ConstantEvaluator::new(gcx); let arr_len = e.eval(len_expr).unwrap().data; // `.eval()` emits errors beforehand // Estimate the upper bound size of each individual element - let elem_size = variable_ty_upper_bound_size(&array.element.kind, gcx); + let elem_size = variable_ty_upper_bound_size(&array.element.kind, gcx)?; let Some(size_contribution) = arr_len.checked_mul(elem_size) else { gcx.dcx().err("overflowed storage slots").emit(); - return U256::ZERO; + return None; }; - size_contribution + Some(size_contribution) } else { // For dynamic size arrays - U256::from(1) + Some(U256::from(1)) } } hir::TypeKind::Custom(item_id) => { diff --git a/tests/ui/resolve/contract_storage_size.sol b/tests/ui/resolve/contract_storage_size.sol deleted file mode 100644 index b71f4dc6..00000000 --- a/tests/ui/resolve/contract_storage_size.sol +++ /dev/null @@ -1,17 +0,0 @@ -contract B { - uint b; -} - -contract A { - struct Person { - string first; - string middle; - string last; - } - - Person my; - mapping(string => uint256) public a; - Person[] public b; - bool c; - B d; -} diff --git a/tests/ui/typeck/contract_storage_size_check.sol b/tests/ui/typeck/contract_storage_size_check.sol new file mode 100644 index 00000000..e32b0816 --- /dev/null +++ b/tests/ui/typeck/contract_storage_size_check.sol @@ -0,0 +1,39 @@ +struct Person { + string name; + uint age; +} + +contract B { + uint c; + bool x; +} + +// Total - 8 + +contract A { + uint256 a; // 1 + bool b; // 1 + B c; // 1 + Person e; // 1 + 2 fields = 3 + int128 f; // 1 + Person[] g; // 1 + Person[23] h; // 23 * 3 = 69 +} + +// Total = 1 + 1 + 1 + 3 + 1 + 1 + 69 = 77 + +contract M { + struct P1 { + string first; + string middle; + string last; + } + + P1 my; // 4 + mapping(string => uint256) public a; // 1 + P1[] public b; // 1 + bool c; // 1 + B d; // 1 +} + +// Total = 8 From 6e6c78c12a6e57e794e1ff20d1d3aa1cb1bf9f41 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 19:58:53 +0530 Subject: [PATCH 05/12] fix --- tests/ui/typeck/contract_storage_size_check.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/typeck/contract_storage_size_check.sol b/tests/ui/typeck/contract_storage_size_check.sol index e32b0816..e6a54cf3 100644 --- a/tests/ui/typeck/contract_storage_size_check.sol +++ b/tests/ui/typeck/contract_storage_size_check.sol @@ -8,7 +8,7 @@ contract B { bool x; } -// Total - 8 +// Total = 1 + 1 = 2 contract A { uint256 a; // 1 @@ -36,4 +36,4 @@ contract M { B d; // 1 } -// Total = 8 +// Total = 4 + 1 + 1 + 1 + 1 = 8 From 73580a65735a482ebd47b57a7aed74f02903fd1e Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 20:01:51 +0530 Subject: [PATCH 06/12] chore: remove old code --- crates/sema/src/typeck/mod.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 3ad05169..bb7c11a6 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -60,11 +60,7 @@ fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> Option, gcx: Gcx<'_>) -> Opt // Estimate the upper bound size of each individual element let elem_size = variable_ty_upper_bound_size(&array.element.kind, gcx)?; - - let Some(size_contribution) = arr_len.checked_mul(elem_size) else { - gcx.dcx().err("overflowed storage slots").emit(); - return None; - }; - - Some(size_contribution) + arr_len.checked_mul(elem_size) } else { // For dynamic size arrays Some(U256::from(1)) From 273ef23137a919ffe267ba4c705689e56fc8e5a5 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 20:33:10 +0530 Subject: [PATCH 07/12] feat --- crates/sema/src/typeck/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index bb7c11a6..6c9cbfd5 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -26,6 +26,7 @@ pub(crate) fn check(gcx: Gcx<'_>) { /// Checks for violation of maximum storage size to ensure slot allocation algorithms works. /// Reference: https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/analysis/ContractLevelChecker.cpp#L556C1-L570C2 fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { + let contract_span = gcx.hir.contract(contract_id).span; let contract_items = gcx.hir.contract_items(contract_id); let mut total_size = U256::ZERO; for item in contract_items { @@ -34,11 +35,11 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { if variable.mutability.is_none() { let Some(size_contribution) = variable_ty_upper_bound_size(&variable.ty.kind, gcx) else { - gcx.dcx().err("overflowed storage slots").emit(); + gcx.dcx().err("contract requires too much storage").span(contract_span).emit(); return; }; let Some(sz) = total_size.checked_add(size_contribution) else { - gcx.dcx().err("overflowed storage slots").emit(); + gcx.dcx().err("contract requires too much storage").span(contract_span).emit(); return; }; total_size = sz; From 1de1e60d58f11754783c539ce180d774c7dac58f Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 20:46:24 +0530 Subject: [PATCH 08/12] feat: use debug assertions to uibless to stderrr --- crates/sema/src/typeck/mod.rs | 8 ++++++-- tests/ui/abi/basic.stderr | 2 ++ tests/ui/abi/contract_special_functions.stderr | 6 ++++++ tests/ui/abi/getters.stderr | 1 + tests/ui/abi/mapping_getters.stderr | 1 + tests/ui/parser/custom_error.stderr | 2 ++ tests/ui/parser/dollar_identifiers.stderr | 1 + tests/ui/resolve/emit_overloaded_event.stderr | 2 ++ tests/ui/resolve/getters.stderr | 1 + tests/ui/resolve/getters_complex.stderr | 1 + tests/ui/resolve/import_self.stderr | 1 + tests/ui/resolve/import_twice.stderr | 2 ++ tests/ui/resolve/named_import.stderr | 1 + tests/ui/resolve/overrides.stderr | 3 +++ tests/ui/resolve/this.stderr | 2 ++ tests/ui/stats/ast.stderr | 1 + tests/ui/typeck/contract_storage_size_check.stderr | 3 +++ tests/ui/typeck/duplicate_overloaded_items.stderr | 3 +++ tests/ui/typeck/issue_128_library_mapping.stderr | 1 + tests/ui/typeck/issue_129_nested_struct.stderr | 1 + 20 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/ui/abi/basic.stderr create mode 100644 tests/ui/abi/contract_special_functions.stderr create mode 100644 tests/ui/abi/getters.stderr create mode 100644 tests/ui/abi/mapping_getters.stderr create mode 100644 tests/ui/parser/custom_error.stderr create mode 100644 tests/ui/parser/dollar_identifiers.stderr create mode 100644 tests/ui/resolve/emit_overloaded_event.stderr create mode 100644 tests/ui/resolve/getters.stderr create mode 100644 tests/ui/resolve/getters_complex.stderr create mode 100644 tests/ui/resolve/import_self.stderr create mode 100644 tests/ui/resolve/import_twice.stderr create mode 100644 tests/ui/resolve/named_import.stderr create mode 100644 tests/ui/resolve/overrides.stderr create mode 100644 tests/ui/resolve/this.stderr create mode 100644 tests/ui/typeck/contract_storage_size_check.stderr create mode 100644 tests/ui/typeck/issue_128_library_mapping.stderr create mode 100644 tests/ui/typeck/issue_129_nested_struct.stderr diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 6c9cbfd5..919fbffd 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -46,8 +46,12 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { } } } - //let c = gcx.hir.contract(contract_id).name; - //println!("{total_size} - {c}"); + if cfg!(debug_assertions) { + eprintln!( + "{} requires {total_size} storage", + gcx.contract_fully_qualified_name(contract_id) + ); + } } fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> Option { diff --git a/tests/ui/abi/basic.stderr b/tests/ui/abi/basic.stderr new file mode 100644 index 00000000..11c04acb --- /dev/null +++ b/tests/ui/abi/basic.stderr @@ -0,0 +1,2 @@ +ROOT/tests/ui/abi/basic.sol:C requires 0 storage +ROOT/tests/ui/abi/basic.sol:D requires 0 storage diff --git a/tests/ui/abi/contract_special_functions.stderr b/tests/ui/abi/contract_special_functions.stderr new file mode 100644 index 00000000..a0ecdaa2 --- /dev/null +++ b/tests/ui/abi/contract_special_functions.stderr @@ -0,0 +1,6 @@ +ROOT/tests/ui/abi/contract_special_functions.sol:A requires 0 storage +ROOT/tests/ui/abi/contract_special_functions.sol:B requires 0 storage +ROOT/tests/ui/abi/contract_special_functions.sol:C requires 0 storage +ROOT/tests/ui/abi/contract_special_functions.sol:D requires 0 storage +ROOT/tests/ui/abi/contract_special_functions.sol:E requires 0 storage +ROOT/tests/ui/abi/contract_special_functions.sol:F requires 0 storage diff --git a/tests/ui/abi/getters.stderr b/tests/ui/abi/getters.stderr new file mode 100644 index 00000000..32ed3656 --- /dev/null +++ b/tests/ui/abi/getters.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/abi/getters.sol:C requires 26 storage diff --git a/tests/ui/abi/mapping_getters.stderr b/tests/ui/abi/mapping_getters.stderr new file mode 100644 index 00000000..6bd48153 --- /dev/null +++ b/tests/ui/abi/mapping_getters.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/abi/mapping_getters.sol:C requires 3 storage diff --git a/tests/ui/parser/custom_error.stderr b/tests/ui/parser/custom_error.stderr new file mode 100644 index 00000000..faf8d594 --- /dev/null +++ b/tests/ui/parser/custom_error.stderr @@ -0,0 +1,2 @@ +ROOT/tests/ui/parser/custom_error.sol:C requires 0 storage +ROOT/tests/ui/parser/custom_error.sol:D requires 1 storage diff --git a/tests/ui/parser/dollar_identifiers.stderr b/tests/ui/parser/dollar_identifiers.stderr new file mode 100644 index 00000000..689616c9 --- /dev/null +++ b/tests/ui/parser/dollar_identifiers.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/parser/dollar_identifiers.sol:DollarIdentifiers requires 2 storage diff --git a/tests/ui/resolve/emit_overloaded_event.stderr b/tests/ui/resolve/emit_overloaded_event.stderr new file mode 100644 index 00000000..f816e8ac --- /dev/null +++ b/tests/ui/resolve/emit_overloaded_event.stderr @@ -0,0 +1,2 @@ +ROOT/tests/ui/resolve/emit_overloaded_event.sol:StdAssertions requires 0 storage +ROOT/tests/ui/resolve/emit_overloaded_event.sol:Test requires 0 storage diff --git a/tests/ui/resolve/getters.stderr b/tests/ui/resolve/getters.stderr new file mode 100644 index 00000000..3b2540fb --- /dev/null +++ b/tests/ui/resolve/getters.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/resolve/getters.sol:C requires 8 storage diff --git a/tests/ui/resolve/getters_complex.stderr b/tests/ui/resolve/getters_complex.stderr new file mode 100644 index 00000000..e80a89c8 --- /dev/null +++ b/tests/ui/resolve/getters_complex.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/resolve/getters_complex.sol:Complex requires 2 storage diff --git a/tests/ui/resolve/import_self.stderr b/tests/ui/resolve/import_self.stderr new file mode 100644 index 00000000..e41e02ba --- /dev/null +++ b/tests/ui/resolve/import_self.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/resolve/import_self.sol:C requires 0 storage diff --git a/tests/ui/resolve/import_twice.stderr b/tests/ui/resolve/import_twice.stderr new file mode 100644 index 00000000..8bcf643f --- /dev/null +++ b/tests/ui/resolve/import_twice.stderr @@ -0,0 +1,2 @@ +ROOT/tests/ui/resolve/import_twice.sol:C requires 0 storage +ROOT/tests/ui/resolve/import_twice.sol:D requires 0 storage diff --git a/tests/ui/resolve/named_import.stderr b/tests/ui/resolve/named_import.stderr new file mode 100644 index 00000000..5a467e37 --- /dev/null +++ b/tests/ui/resolve/named_import.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/resolve/named_import.sol:C requires 0 storage diff --git a/tests/ui/resolve/overrides.stderr b/tests/ui/resolve/overrides.stderr new file mode 100644 index 00000000..bdfc91e1 --- /dev/null +++ b/tests/ui/resolve/overrides.stderr @@ -0,0 +1,3 @@ +ROOT/tests/ui/resolve/overrides.sol:MyAbstractTest requires 0 storage +ROOT/tests/ui/resolve/overrides.sol:MyTestSuite requires 0 storage +ROOT/tests/ui/resolve/overrides.sol:MyTest requires 0 storage diff --git a/tests/ui/resolve/this.stderr b/tests/ui/resolve/this.stderr new file mode 100644 index 00000000..2eee586f --- /dev/null +++ b/tests/ui/resolve/this.stderr @@ -0,0 +1,2 @@ +ROOT/tests/ui/resolve/this.sol:C requires 1 storage +ROOT/tests/ui/resolve/this.sol:D requires 1 storage diff --git a/tests/ui/stats/ast.stderr b/tests/ui/stats/ast.stderr index d13f817b..2a940e87 100644 --- a/tests/ui/stats/ast.stderr +++ b/tests/ui/stats/ast.stderr @@ -24,3 +24,4 @@ ast-stats - Function 272 (15.3%) 2 ast-stats ---------------------------------------------------------------- ast-stats Total 1_772 ast-stats +ROOT/tests/ui/stats/ast.sol:Counter requires 1 storage diff --git a/tests/ui/typeck/contract_storage_size_check.stderr b/tests/ui/typeck/contract_storage_size_check.stderr new file mode 100644 index 00000000..e12ef4b6 --- /dev/null +++ b/tests/ui/typeck/contract_storage_size_check.stderr @@ -0,0 +1,3 @@ +ROOT/tests/ui/typeck/contract_storage_size_check.sol:B requires 2 storage +ROOT/tests/ui/typeck/contract_storage_size_check.sol:A requires 77 storage +ROOT/tests/ui/typeck/contract_storage_size_check.sol:M requires 8 storage diff --git a/tests/ui/typeck/duplicate_overloaded_items.stderr b/tests/ui/typeck/duplicate_overloaded_items.stderr index b003d6b6..744c4e54 100644 --- a/tests/ui/typeck/duplicate_overloaded_items.stderr +++ b/tests/ui/typeck/duplicate_overloaded_items.stderr @@ -81,6 +81,9 @@ LL | function f6(string calldata) {} | -- note: other declaration | +ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:C requires 0 storage +ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:C2 requires 0 storage +ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:D requires 0 storage error: event with same name and parameter types declared twice --> ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:LL:CC | diff --git a/tests/ui/typeck/issue_128_library_mapping.stderr b/tests/ui/typeck/issue_128_library_mapping.stderr new file mode 100644 index 00000000..7984e544 --- /dev/null +++ b/tests/ui/typeck/issue_128_library_mapping.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/typeck/issue_128_library_mapping.sol:TestLibrary requires 0 storage diff --git a/tests/ui/typeck/issue_129_nested_struct.stderr b/tests/ui/typeck/issue_129_nested_struct.stderr new file mode 100644 index 00000000..c529e84a --- /dev/null +++ b/tests/ui/typeck/issue_129_nested_struct.stderr @@ -0,0 +1 @@ +ROOT/tests/ui/typeck/issue_129_nested_struct.sol:TestContract requires 0 storage From 3f85d3b687a1a7b097b7dc0e049fb00688686d7e Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 20:50:13 +0530 Subject: [PATCH 09/12] fix: remove stderr for non test files --- crates/sema/src/typeck/mod.rs | 11 +++++++---- tests/ui/abi/basic.stderr | 2 -- tests/ui/abi/contract_special_functions.stderr | 6 ------ tests/ui/abi/getters.stderr | 1 - tests/ui/abi/mapping_getters.stderr | 1 - tests/ui/parser/custom_error.stderr | 2 -- tests/ui/parser/dollar_identifiers.stderr | 1 - tests/ui/resolve/emit_overloaded_event.stderr | 2 -- tests/ui/resolve/getters.stderr | 1 - tests/ui/resolve/getters_complex.stderr | 1 - tests/ui/resolve/import_self.stderr | 1 - tests/ui/resolve/import_twice.stderr | 2 -- tests/ui/resolve/named_import.stderr | 1 - tests/ui/resolve/overrides.stderr | 3 --- tests/ui/resolve/this.stderr | 2 -- tests/ui/stats/ast.stderr | 1 - tests/ui/typeck/contract_storage_size_check.stderr | 3 --- tests/ui/typeck/duplicate_overloaded_items.stderr | 3 --- tests/ui/typeck/issue_128_library_mapping.stderr | 1 - tests/ui/typeck/issue_129_nested_struct.stderr | 1 - 20 files changed, 7 insertions(+), 39 deletions(-) delete mode 100644 tests/ui/abi/basic.stderr delete mode 100644 tests/ui/abi/contract_special_functions.stderr delete mode 100644 tests/ui/abi/getters.stderr delete mode 100644 tests/ui/abi/mapping_getters.stderr delete mode 100644 tests/ui/parser/custom_error.stderr delete mode 100644 tests/ui/parser/dollar_identifiers.stderr delete mode 100644 tests/ui/resolve/emit_overloaded_event.stderr delete mode 100644 tests/ui/resolve/getters.stderr delete mode 100644 tests/ui/resolve/getters_complex.stderr delete mode 100644 tests/ui/resolve/import_self.stderr delete mode 100644 tests/ui/resolve/import_twice.stderr delete mode 100644 tests/ui/resolve/named_import.stderr delete mode 100644 tests/ui/resolve/overrides.stderr delete mode 100644 tests/ui/resolve/this.stderr delete mode 100644 tests/ui/typeck/contract_storage_size_check.stderr delete mode 100644 tests/ui/typeck/issue_128_library_mapping.stderr delete mode 100644 tests/ui/typeck/issue_129_nested_struct.stderr diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 919fbffd..45c9a260 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -47,10 +47,13 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { } } if cfg!(debug_assertions) { - eprintln!( - "{} requires {total_size} storage", - gcx.contract_fully_qualified_name(contract_id) - ); + let full_contract_name = format!("{}", gcx.contract_fully_qualified_name(contract_id)); + if full_contract_name.starts_with("ROOT/tests/ui/typeck/contract_storage_size_check.sol") { + eprintln!( + "{} requires {total_size} maximum storage", + gcx.contract_fully_qualified_name(contract_id) + ); + } } } diff --git a/tests/ui/abi/basic.stderr b/tests/ui/abi/basic.stderr deleted file mode 100644 index 11c04acb..00000000 --- a/tests/ui/abi/basic.stderr +++ /dev/null @@ -1,2 +0,0 @@ -ROOT/tests/ui/abi/basic.sol:C requires 0 storage -ROOT/tests/ui/abi/basic.sol:D requires 0 storage diff --git a/tests/ui/abi/contract_special_functions.stderr b/tests/ui/abi/contract_special_functions.stderr deleted file mode 100644 index a0ecdaa2..00000000 --- a/tests/ui/abi/contract_special_functions.stderr +++ /dev/null @@ -1,6 +0,0 @@ -ROOT/tests/ui/abi/contract_special_functions.sol:A requires 0 storage -ROOT/tests/ui/abi/contract_special_functions.sol:B requires 0 storage -ROOT/tests/ui/abi/contract_special_functions.sol:C requires 0 storage -ROOT/tests/ui/abi/contract_special_functions.sol:D requires 0 storage -ROOT/tests/ui/abi/contract_special_functions.sol:E requires 0 storage -ROOT/tests/ui/abi/contract_special_functions.sol:F requires 0 storage diff --git a/tests/ui/abi/getters.stderr b/tests/ui/abi/getters.stderr deleted file mode 100644 index 32ed3656..00000000 --- a/tests/ui/abi/getters.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/abi/getters.sol:C requires 26 storage diff --git a/tests/ui/abi/mapping_getters.stderr b/tests/ui/abi/mapping_getters.stderr deleted file mode 100644 index 6bd48153..00000000 --- a/tests/ui/abi/mapping_getters.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/abi/mapping_getters.sol:C requires 3 storage diff --git a/tests/ui/parser/custom_error.stderr b/tests/ui/parser/custom_error.stderr deleted file mode 100644 index faf8d594..00000000 --- a/tests/ui/parser/custom_error.stderr +++ /dev/null @@ -1,2 +0,0 @@ -ROOT/tests/ui/parser/custom_error.sol:C requires 0 storage -ROOT/tests/ui/parser/custom_error.sol:D requires 1 storage diff --git a/tests/ui/parser/dollar_identifiers.stderr b/tests/ui/parser/dollar_identifiers.stderr deleted file mode 100644 index 689616c9..00000000 --- a/tests/ui/parser/dollar_identifiers.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/parser/dollar_identifiers.sol:DollarIdentifiers requires 2 storage diff --git a/tests/ui/resolve/emit_overloaded_event.stderr b/tests/ui/resolve/emit_overloaded_event.stderr deleted file mode 100644 index f816e8ac..00000000 --- a/tests/ui/resolve/emit_overloaded_event.stderr +++ /dev/null @@ -1,2 +0,0 @@ -ROOT/tests/ui/resolve/emit_overloaded_event.sol:StdAssertions requires 0 storage -ROOT/tests/ui/resolve/emit_overloaded_event.sol:Test requires 0 storage diff --git a/tests/ui/resolve/getters.stderr b/tests/ui/resolve/getters.stderr deleted file mode 100644 index 3b2540fb..00000000 --- a/tests/ui/resolve/getters.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/resolve/getters.sol:C requires 8 storage diff --git a/tests/ui/resolve/getters_complex.stderr b/tests/ui/resolve/getters_complex.stderr deleted file mode 100644 index e80a89c8..00000000 --- a/tests/ui/resolve/getters_complex.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/resolve/getters_complex.sol:Complex requires 2 storage diff --git a/tests/ui/resolve/import_self.stderr b/tests/ui/resolve/import_self.stderr deleted file mode 100644 index e41e02ba..00000000 --- a/tests/ui/resolve/import_self.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/resolve/import_self.sol:C requires 0 storage diff --git a/tests/ui/resolve/import_twice.stderr b/tests/ui/resolve/import_twice.stderr deleted file mode 100644 index 8bcf643f..00000000 --- a/tests/ui/resolve/import_twice.stderr +++ /dev/null @@ -1,2 +0,0 @@ -ROOT/tests/ui/resolve/import_twice.sol:C requires 0 storage -ROOT/tests/ui/resolve/import_twice.sol:D requires 0 storage diff --git a/tests/ui/resolve/named_import.stderr b/tests/ui/resolve/named_import.stderr deleted file mode 100644 index 5a467e37..00000000 --- a/tests/ui/resolve/named_import.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/resolve/named_import.sol:C requires 0 storage diff --git a/tests/ui/resolve/overrides.stderr b/tests/ui/resolve/overrides.stderr deleted file mode 100644 index bdfc91e1..00000000 --- a/tests/ui/resolve/overrides.stderr +++ /dev/null @@ -1,3 +0,0 @@ -ROOT/tests/ui/resolve/overrides.sol:MyAbstractTest requires 0 storage -ROOT/tests/ui/resolve/overrides.sol:MyTestSuite requires 0 storage -ROOT/tests/ui/resolve/overrides.sol:MyTest requires 0 storage diff --git a/tests/ui/resolve/this.stderr b/tests/ui/resolve/this.stderr deleted file mode 100644 index 2eee586f..00000000 --- a/tests/ui/resolve/this.stderr +++ /dev/null @@ -1,2 +0,0 @@ -ROOT/tests/ui/resolve/this.sol:C requires 1 storage -ROOT/tests/ui/resolve/this.sol:D requires 1 storage diff --git a/tests/ui/stats/ast.stderr b/tests/ui/stats/ast.stderr index 2a940e87..d13f817b 100644 --- a/tests/ui/stats/ast.stderr +++ b/tests/ui/stats/ast.stderr @@ -24,4 +24,3 @@ ast-stats - Function 272 (15.3%) 2 ast-stats ---------------------------------------------------------------- ast-stats Total 1_772 ast-stats -ROOT/tests/ui/stats/ast.sol:Counter requires 1 storage diff --git a/tests/ui/typeck/contract_storage_size_check.stderr b/tests/ui/typeck/contract_storage_size_check.stderr deleted file mode 100644 index e12ef4b6..00000000 --- a/tests/ui/typeck/contract_storage_size_check.stderr +++ /dev/null @@ -1,3 +0,0 @@ -ROOT/tests/ui/typeck/contract_storage_size_check.sol:B requires 2 storage -ROOT/tests/ui/typeck/contract_storage_size_check.sol:A requires 77 storage -ROOT/tests/ui/typeck/contract_storage_size_check.sol:M requires 8 storage diff --git a/tests/ui/typeck/duplicate_overloaded_items.stderr b/tests/ui/typeck/duplicate_overloaded_items.stderr index 744c4e54..b003d6b6 100644 --- a/tests/ui/typeck/duplicate_overloaded_items.stderr +++ b/tests/ui/typeck/duplicate_overloaded_items.stderr @@ -81,9 +81,6 @@ LL | function f6(string calldata) {} | -- note: other declaration | -ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:C requires 0 storage -ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:C2 requires 0 storage -ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:D requires 0 storage error: event with same name and parameter types declared twice --> ROOT/tests/ui/typeck/duplicate_overloaded_items.sol:LL:CC | diff --git a/tests/ui/typeck/issue_128_library_mapping.stderr b/tests/ui/typeck/issue_128_library_mapping.stderr deleted file mode 100644 index 7984e544..00000000 --- a/tests/ui/typeck/issue_128_library_mapping.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/typeck/issue_128_library_mapping.sol:TestLibrary requires 0 storage diff --git a/tests/ui/typeck/issue_129_nested_struct.stderr b/tests/ui/typeck/issue_129_nested_struct.stderr deleted file mode 100644 index c529e84a..00000000 --- a/tests/ui/typeck/issue_129_nested_struct.stderr +++ /dev/null @@ -1 +0,0 @@ -ROOT/tests/ui/typeck/issue_129_nested_struct.sol:TestContract requires 0 storage From e83fc137619210230b1a28fd50096dc05c5d4870 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sat, 14 Dec 2024 20:53:06 +0530 Subject: [PATCH 10/12] final fix --- crates/sema/src/typeck/mod.rs | 7 ++----- tests/ui/typeck/contract_storage_size_check.stderr | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 tests/ui/typeck/contract_storage_size_check.stderr diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 45c9a260..68a624db 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -48,11 +48,8 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { } if cfg!(debug_assertions) { let full_contract_name = format!("{}", gcx.contract_fully_qualified_name(contract_id)); - if full_contract_name.starts_with("ROOT/tests/ui/typeck/contract_storage_size_check.sol") { - eprintln!( - "{} requires {total_size} maximum storage", - gcx.contract_fully_qualified_name(contract_id) - ); + if full_contract_name.contains("contract_storage_size_check") { + eprintln!("{full_contract_name} requires {total_size} maximum storage"); } } } diff --git a/tests/ui/typeck/contract_storage_size_check.stderr b/tests/ui/typeck/contract_storage_size_check.stderr new file mode 100644 index 00000000..4a6e2dd3 --- /dev/null +++ b/tests/ui/typeck/contract_storage_size_check.stderr @@ -0,0 +1,3 @@ +ROOT/tests/ui/typeck/contract_storage_size_check.sol:B requires 2 maximum storage +ROOT/tests/ui/typeck/contract_storage_size_check.sol:A requires 77 maximum storage +ROOT/tests/ui/typeck/contract_storage_size_check.sol:M requires 8 maximum storage From 05a395e3941cd698e1858625f635a573b4126d65 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sun, 15 Dec 2024 14:17:20 +0530 Subject: [PATCH 11/12] refactor --- crates/sema/src/typeck/mod.rs | 104 ++++++++++++++++------------------ 1 file changed, 48 insertions(+), 56 deletions(-) diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 68a624db..0eccff76 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -1,8 +1,7 @@ use crate::{ ast_lowering::resolve::{Declaration, Declarations}, - eval::ConstantEvaluator, hir::{self, Res}, - ty::{Gcx, Ty}, + ty::{Gcx, Ty, TyKind}, }; use alloy_primitives::U256; use rayon::prelude::*; @@ -13,13 +12,11 @@ pub(crate) fn check(gcx: Gcx<'_>) { gcx.sess, gcx.hir.par_contract_ids().for_each(|id| { check_duplicate_definitions(gcx, &gcx.symbol_resolver.contract_scopes[id]); + check_storage_size_upper_bound(gcx, id); }), gcx.hir.par_source_ids().for_each(|id| { check_duplicate_definitions(gcx, &gcx.symbol_resolver.source_scopes[id]); }), - gcx.hir.par_contract_ids().for_each(|id| { - check_storage_size_upper_bound(gcx, id); - }), ); } @@ -33,16 +30,21 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { if let hir::Item::Variable(variable) = item { // Skip constant and immutable variables if variable.mutability.is_none() { - let Some(size_contribution) = variable_ty_upper_bound_size(&variable.ty.kind, gcx) - else { - gcx.dcx().err("contract requires too much storage").span(contract_span).emit(); - return; - }; - let Some(sz) = total_size.checked_add(size_contribution) else { - gcx.dcx().err("contract requires too much storage").span(contract_span).emit(); - return; - }; - total_size = sz; + let t = gcx.type_of_hir_ty(&variable.ty); + match ty_upper_bound_storage_var_size(t, gcx) + .and_then(|size_contribution| total_size.checked_add(size_contribution)) + { + Some(sz) => { + total_size = sz; + } + None => { + gcx.dcx() + .err("contract requires too much storage") + .span(contract_span) + .emit(); + return; + } + } } } } @@ -54,55 +56,45 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { } } -fn item_ty_upper_bound_size(item: &hir::Item<'_, '_>, gcx: Gcx<'_>) -> Option { - match item { - hir::Item::Function(_) | hir::Item::Contract(_) => Some(U256::from(1)), - hir::Item::Variable(variable) => variable_ty_upper_bound_size(&variable.ty.kind, gcx), - hir::Item::Udvt(udvt) => variable_ty_upper_bound_size(&udvt.ty.kind, gcx), - hir::Item::Struct(strukt) => { +fn ty_upper_bound_storage_var_size(ty: Ty<'_>, gcx: Gcx<'_>) -> Option { + match ty.kind { + TyKind::Elementary(..) + | TyKind::StringLiteral(..) + | TyKind::IntLiteral(..) + | TyKind::Mapping(..) + | TyKind::Contract(..) + | TyKind::Udvt(..) + | TyKind::Enum(..) + | TyKind::DynArray(..) => Some(U256::from(1)), + TyKind::Ref(..) + | TyKind::Tuple(..) + | TyKind::FnPtr(..) + | TyKind::Module(..) + | TyKind::BuiltinModule(..) + | TyKind::Event(..) + | TyKind::Meta(..) + | TyKind::Err(..) + | TyKind::Error(..) => { + unreachable!() + } + TyKind::Array(ty, uint) => { + // Reference: https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/ast/Types.cpp#L1800C1-L1806C2 + let elem_size = ty_upper_bound_storage_var_size(ty, gcx)?; + uint.checked_mul(elem_size) + } + TyKind::Struct(struct_id) => { + let strukt = gcx.hir.strukt(struct_id); // Reference https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/ast/Types.cpp#L2303C1-L2309C2 let mut total_size = U256::from(1); for field_id in strukt.fields { let variable = gcx.hir.variable(*field_id); - let size_contribution = variable_ty_upper_bound_size(&variable.ty.kind, gcx)?; + let t = gcx.type_of_hir_ty(&variable.ty); + let size_contribution = ty_upper_bound_storage_var_size(t, gcx)?; total_size = total_size.checked_add(size_contribution)?; } Some(total_size) } - hir::Item::Enum(_) | hir::Item::Event(_) | hir::Item::Error(_) => { - // Enum and events cannot be types of storage variables - unreachable!("illegal values") - } - } -} - -fn variable_ty_upper_bound_size(var_ty: &hir::TypeKind<'_>, gcx: Gcx<'_>) -> Option { - match &var_ty { - hir::TypeKind::Elementary(_) | hir::TypeKind::Function(_) | hir::TypeKind::Mapping(_) => { - Some(U256::from(1)) - } - hir::TypeKind::Array(array) => { - // Reference: https://github.com/ethereum/solidity/blob/03e2739809769ae0c8d236a883aadc900da60536/libsolidity/ast/Types.cpp#L1800C1-L1806C2 - if let Some(len_expr) = array.size { - // Evaluate the length expression in array declaration - let mut e = ConstantEvaluator::new(gcx); - let arr_len = e.eval(len_expr).unwrap().data; // `.eval()` emits errors beforehand - - // Estimate the upper bound size of each individual element - let elem_size = variable_ty_upper_bound_size(&array.element.kind, gcx)?; - arr_len.checked_mul(elem_size) - } else { - // For dynamic size arrays - Some(U256::from(1)) - } - } - hir::TypeKind::Custom(item_id) => { - let item = gcx.hir.item(*item_id); - item_ty_upper_bound_size(&item, gcx) - } - hir::TypeKind::Err(_) => { - unreachable!() - } + TyKind::Type(ty) => ty_upper_bound_storage_var_size(ty, gcx), } } From b44c589be0bf1f39ce6c8e43c366b4575f1d3937 Mon Sep 17 00:00:00 2001 From: TilakMaddy Date: Sun, 15 Dec 2024 15:00:56 +0530 Subject: [PATCH 12/12] feat: add unstable flag --- crates/cli/src/cli.rs | 3 +++ crates/cli/src/lib.rs | 1 + crates/interface/src/session.rs | 3 +++ crates/sema/src/typeck/mod.rs | 7 +++---- tests/ui/typeck/contract_storage_size_check.sol | 2 ++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index f5534871..311b6b9c 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -138,6 +138,9 @@ pub struct UnstableFeatures { #[cfg(test)] #[arg(long)] test_value: Option, + + #[arg(long)] + pub storage_sz_ub: bool, } /// How errors and other messages are produced. diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index f9ad5a96..6ce0cf9e 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -138,6 +138,7 @@ fn run_compiler_with(args: Args, f: impl FnOnce(&Compiler) -> Result + Send) -> sess.stop_after = args.stop_after; sess.dump = args.unstable.dump.clone(); sess.ast_stats = args.unstable.ast_stats; + sess.storage_sz_ub = args.unstable.storage_sz_ub; sess.jobs = NonZeroUsize::new(args.threads) .unwrap_or_else(|| std::thread::available_parallelism().unwrap_or(NonZeroUsize::MIN)); if !args.input.is_empty() diff --git a/crates/interface/src/session.rs b/crates/interface/src/session.rs index faa2576c..b5ab5529 100644 --- a/crates/interface/src/session.rs +++ b/crates/interface/src/session.rs @@ -45,6 +45,9 @@ pub struct Session { /// Whether to emit AST stats. #[builder(default)] pub ast_stats: bool, + /// Whether to emit contract's storage size upper bound + #[builder(default)] + pub storage_sz_ub: bool, } impl SessionBuilder { diff --git a/crates/sema/src/typeck/mod.rs b/crates/sema/src/typeck/mod.rs index 0eccff76..a33f8201 100644 --- a/crates/sema/src/typeck/mod.rs +++ b/crates/sema/src/typeck/mod.rs @@ -48,11 +48,10 @@ fn check_storage_size_upper_bound(gcx: Gcx<'_>, contract_id: hir::ContractId) { } } } - if cfg!(debug_assertions) { + + if gcx.sess.storage_sz_ub { let full_contract_name = format!("{}", gcx.contract_fully_qualified_name(contract_id)); - if full_contract_name.contains("contract_storage_size_check") { - eprintln!("{full_contract_name} requires {total_size} maximum storage"); - } + eprintln!("{full_contract_name} requires {total_size} maximum storage"); } } diff --git a/tests/ui/typeck/contract_storage_size_check.sol b/tests/ui/typeck/contract_storage_size_check.sol index e6a54cf3..9a3a6d58 100644 --- a/tests/ui/typeck/contract_storage_size_check.sol +++ b/tests/ui/typeck/contract_storage_size_check.sol @@ -1,3 +1,5 @@ +//@ compile-flags: -Zstorage-sz-ub + struct Person { string name; uint age;