Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix struct member read access (take 2) #1563

Merged

Conversation

seanyoung
Copy link
Contributor

@seanyoung seanyoung commented Oct 12, 2023

Fixes a bug: StorageLoad after a struct member access is a storage read, this case was not considered in the mutability check.

The regression in the EVM test is fine; it's caused by a problem in solc

@@ -358,29 +335,23 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec

fn read_expression(expr: &Expression, state: &mut StateCheck) -> bool {
match expr {
Expression::StorageLoad { loc, .. } => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I initially proposed this solution in the standup. I was pointed out that #1544 removed it because it leads to duplicate error messages. And I should try a different solution. But I agree that a StorageLoad must always require view. So I guess without changing the structure of the tree there is no other solution than tracking the diagnostics. 🤷

self.contents.push(diag);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this solution of removing duplicate diagnostics based on their location is a working solution, I wonder whether this code is really necessary. In codegen, we have explicitly Isntr::StorageStore and Instr::LoadStorage that can be used to detect storage mutability. In fact, moving the mutability check to codegen was my original idea for #1544, but I found a quick fix for the problem (which led to other unforeseen errors, unfortunately).

I know this would require a lot more work than this PR, so we might solve this later and I would be happy to implement this. Please, just share your thoughts about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work in codegen, if we want to be compatible with solc. Take this example:

contract C {
	int[] arr;

	function foo() internal pure returns (int[] storage y) {
		y = arr;
	}
}

solc says:

Error: Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view".

codegen:

# function C::C::function::foo public:false selector:8ae62232ad41e1fb nonpayable:true
# params: 
# returns: int256[] storage y
block0: # entry
	ty:int256[] storage %y = uint32 16
	return %y

(There is no Instr::LoadStorage)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some more fixes, I think the fn remove_overlapping() is entirely unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is interesting that solc considers that a view function, even if we do not read anything from storage and only assign a constant value to the return variable.

After some more fixes, I think the fn remove_overlapping() is entirely unnecessary.

Great! That makes your code much cleaner!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is interesting that solc considers that a view function, even if we do not read anything from storage and only assign a constant value to the return variable.

Yes, I don't understand this either. I guess it's not just any old constant value, it's a storage key.

@seanyoung seanyoung force-pushed the bugfix-struct-member-read-access branch from 95d12dc to f42cc59 Compare October 17, 2023 11:56
Signed-off-by: Sean Young <[email protected]>
@seanyoung seanyoung force-pushed the bugfix-struct-member-read-access branch from f42cc59 to b8fe6a4 Compare October 17, 2023 11:56
@LucasSte
Copy link
Contributor

In this version, this code generates multiple errors for the same read/write violations:

contract Test {
    struct CC {
        bytes name;
        uint32 age;
    }

    CC[] vari;
    uint32[] cc;

    function read() public pure returns (uint32) {
        return vari[1].age + cc[2];
    }
}
error: function declared 'pure' but this expression reads from state
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:11:16
   │
11 │         return vari[1].age + cc[2];
   │                ^^^^

error: function declared 'pure' but this expression reads from state
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:11:16
   │
11 │         return vari[1].age + cc[2];
   │                ^^^^^^^^^^^

error: function declared 'pure' but this expression reads from state
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:11:30
   │
11 │         return vari[1].age + cc[2];
   │                              ^^

error: function declared 'pure' but this expression reads from state
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:11:30
   │
11 │         return vari[1].age + cc[2];
   │                              ^^^^^

In one hand, you are not wrong as solc generates the same four errors. On the other hand, they clutter the diagnostics, don't they?

@seanyoung
Copy link
Contributor Author

In one hand, you are not wrong as solc generates the same four errors. On the other hand, they clutter the diagnostics, don't they?

Thanks for finding those, I was thinking this could happen in some situations.

  • Generating the same errors as solc is nice in its own way
  • The code which removes overlapping errors works, but it is not the most elegant solution.
  • I don't think those extra errors are too ugly (that's an opinion of course)

@xermicus
Copy link
Contributor

In one hand, you are not wrong as solc generates the same four errors. On the other hand, they clutter the diagnostics, don't they?

Thanks for finding those, I was thinking this could happen in some situations.

* Generating the same errors as solc is nice in its own way

* The code which removes overlapping errors works, but it is not the most elegant solution.

* I don't think those extra errors are too ugly (that's an opinion of course)

I'm on the same page. Not ideal to have those errors, but I'm also not sure if it's worth the effort. What about fixing those cluttering messages when users complain, WDYT @LucasSte ?

@seanyoung
Copy link
Contributor Author

I think what we want is some marker you can set which says "I've already warned for read on this expression, don't generate any more when recursing the current expression. I am not entirely sure how would you do that with current code though.

@LucasSte
Copy link
Contributor

This is a working solution that copies solc behavior, so it is valid for now.

@xermicus
Copy link
Contributor

I think what we want is some marker you can set which says "I've already warned for read on this expression, don't generate any more when recursing the current expression. I am not entirely sure how would you do that with current code though.

Hmm yeah, I've tried that out in my previous attempt, I agree it's not easily possible with the current implementation

@codecov
Copy link

codecov bot commented Oct 18, 2023

Codecov Report

Merging #1563 (51d273f) into main (2c29fd7) will increase coverage by 0.02%.
Report is 1 commits behind head on main.
The diff coverage is 98.46%.

@@            Coverage Diff             @@
##             main    #1563      +/-   ##
==========================================
+ Coverage   87.33%   87.36%   +0.02%     
==========================================
  Files         133      133              
  Lines       64143    64126      -17     
==========================================
+ Hits        56021    56022       +1     
+ Misses       8122     8104      -18     
Files Coverage Δ
src/sema/statements.rs 93.80% <100.00%> (+0.04%) ⬆️
src/sema/mutability.rs 95.38% <97.87%> (+0.10%) ⬆️

... and 5 files with indirect coverage changes

@seanyoung seanyoung merged commit a9e20d3 into hyperledger-solang:main Oct 18, 2023
19 checks passed
@seanyoung seanyoung deleted the bugfix-struct-member-read-access branch October 18, 2023 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants