Procházet zdrojové kódy

Fix recursive struct issue (#1522)

Lucas Steuernagel před 2 roky
rodič
revize
c2d0fd8d3b

+ 41 - 13
src/sema/types.rs

@@ -20,9 +20,11 @@ use num_traits::{One, Zero};
 use petgraph::algo::{all_simple_paths, tarjan_scc};
 use petgraph::stable_graph::IndexType;
 use petgraph::Directed;
+use phf::{phf_set, Set};
 use solang_parser::diagnostics::Note;
 use solang_parser::{doccomment::DocComment, pt, pt::CodeLocation};
 use std::collections::HashSet;
+use std::ops::MulAssign;
 use std::{fmt::Write, ops::Mul};
 
 type Graph = petgraph::Graph<(), usize, Directed, usize>;
@@ -288,7 +290,8 @@ fn check_infinite_struct_size(graph: &Graph, nodes: Vec<usize>, ns: &mut Namespa
         let mut infinite_edge = false;
         for edge in graph.edges_connecting(a.into(), b.into()) {
             match &ns.structs[a].fields[*edge.weight()].ty {
-                Type::Array(_, dims) if dims.first() != Some(&ArrayLength::Dynamic) => {}
+                Type::Array(_, dims) if dims.contains(&ArrayLength::Dynamic) => continue,
+                Type::Array(_, _) => {}
                 Type::Struct(StructType::UserDefined(_)) => {}
                 _ => continue,
             }
@@ -367,6 +370,7 @@ fn find_struct_recursion(ns: &mut Namespace) {
             check_recursive_struct_field(n.index(), &graph, ns);
         }
     }
+
     for n in 0..ns.structs.len() {
         let mut notes = vec![];
         for field in ns.structs[n].fields.iter().filter(|f| f.infinite_size) {
@@ -1579,7 +1583,11 @@ impl Type {
     /// which is not accounted for.
     pub fn solana_storage_size(&self, ns: &Namespace) -> BigInt {
         match self {
-            Type::Array(_, dims) if dims.first() == Some(&ArrayLength::Dynamic) => 4.into(),
+            Type::Array(_, dims) if dims.contains(&ArrayLength::Dynamic) => {
+                let size = dynamic_array_size(dims);
+                // A pointer is four bytes on Solana
+                size * 4
+            }
             Type::Array(ty, dims) => ty.solana_storage_size(ns).mul(
                 dims.iter()
                     .map(|d| match d {
@@ -1707,8 +1715,10 @@ impl Type {
                 Type::Value => BigInt::from(ns.value_length),
                 Type::Uint(n) | Type::Int(n) => BigInt::from(n / 8),
                 Type::Rational => unreachable!(),
-                Type::Array(_, dims) if dims.first() == Some(&ArrayLength::Dynamic) => {
-                    BigInt::from(4)
+                Type::Array(_, dims) if dims.contains(&ArrayLength::Dynamic) => {
+                    let size = dynamic_array_size(dims);
+                    // A pointer is four bytes on Solana
+                    size * 4
                 }
                 Type::Array(ty, dims) => {
                     let pointer_size = BigInt::from(4);
@@ -1756,7 +1766,9 @@ impl Type {
                     .filter(|f| !f.infinite_size)
                     .map(|f| f.ty.storage_slots(ns))
                     .sum(),
-                Type::Array(_, dims) if dims.first() == Some(&ArrayLength::Dynamic) => 1.into(),
+                Type::Array(_, dims) if dims.contains(&ArrayLength::Dynamic) => {
+                    dynamic_array_size(dims)
+                }
                 Type::Array(ty, dims) => {
                     let one = 1.into();
                     ty.storage_slots(ns)
@@ -1787,9 +1799,7 @@ impl Type {
                 Type::Value => BigInt::from(ns.value_length),
                 Type::Uint(n) | Type::Int(n) => BigInt::from(n / 8),
                 Type::Rational => unreachable!(),
-                Type::Array(_, dims) if dims.first() == Some(&ArrayLength::Dynamic) => {
-                    BigInt::from(4)
-                }
+                Type::Array(_, dims) if dims.contains(&ArrayLength::Dynamic) => BigInt::from(4),
                 Type::Array(ty, _) => {
                     if self.is_sparse_solana(ns) {
                         BigInt::from(4)
@@ -2174,10 +2184,28 @@ impl Type {
 
 /// These names cannot be used on Windows, even with an extension.
 /// shamelessly stolen from cargo
+static WINDOWS_RESERVED: Set<&'static str> = phf_set! {
+     "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8",
+         "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
+};
 fn is_windows_reserved(name: &str) -> bool {
-    [
-        "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8",
-        "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
-    ]
-    .contains(&name.to_ascii_lowercase().as_str())
+    WINDOWS_RESERVED.contains(name.to_ascii_lowercase().as_str())
+}
+
+/// This function calculates the size of a dynamic array.
+/// The reasoning is the following:
+/// An array `uint [2][][3][1]` is a `void * foo[3][1]`-like in C, so its size
+/// in storage is 3*1*ptr_size. Each pointer points to a `uint[2]` so whatever comes before the
+/// ultimate empty square bracket does not matter.
+fn dynamic_array_size(dims: &[ArrayLength]) -> BigInt {
+    let mut result = BigInt::one();
+    for dim in dims.iter().rev() {
+        match dim {
+            ArrayLength::Fixed(num) => result.mul_assign(num),
+            ArrayLength::Dynamic => break,
+            ArrayLength::AnyFixed => unreachable!("unknown dimension"),
+        }
+    }
+
+    result
 }

+ 29 - 0
tests/contract_testcases/solana/structs/parse_structs.sol

@@ -0,0 +1,29 @@
+contract hatchling {
+    struct A {
+        A [][2][1] b;
+    }
+    struct B {
+        B [2][][1] b;
+    }
+    struct C {
+        C [2][1][] b;
+    }
+
+    A private n1;
+    B private n2;
+    C private n3;
+
+    constructor() {}
+
+    function foo(uint a, uint b) public {
+
+    }
+}
+
+// ---- Expect: diagnostics ----
+// warning: 12:5-17: storage variable 'n1' has never been used
+// warning: 13:5-17: storage variable 'n2' has never been used
+// warning: 14:5-17: storage variable 'n3' has never been used
+// warning: 18:5-40: function can be declared 'pure'
+// warning: 18:23-24: function parameter 'a' is unused
+// warning: 18:31-32: function parameter 'b' is unused