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

Incorrect location generated when desugaring arithmetic assign ops (e.g., a += b) #1521

Open
BenTheKush opened this issue Sep 13, 2023 · 2 comments

Comments

@BenTheKush
Copy link
Contributor

BenTheKush commented Sep 13, 2023

Describe the bug
An expression a += b is parsed as a pt::Expression::AssignAdd{left, right}. When this is translated from a pt to an ast it is desugared into an Assign { ..., Expression::Add { left, right}}. When left is a Storage access, left's location is wrong when it gets desugared: it ends up spanning the entire AssignAdd expression instead of just the left expression.

To Reproduce
I have a repository that reproduces the bug.

Hyperledger Solang version

solang = { git = "https://github.com/hyperledger/solang.git", rev = "70af554c42748009e414e6263dd4607fb380e5dc", default-features = false }
solang-parser = { git = "https://github.com/hyperledger/solang.git", rev = "70af554c42748009e414e6263dd4607fb380e5dc" }

Include the complete solidity source code

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >0.7.0;
pragma experimental ABIEncoderV2;

contract foo {
    uint256 private _totalSupply;

    function updateStorage() public {
        _totalSupply += 1;
        _totalSupply *= 1;
        _totalSupply /= 1;
        _totalSupply -= 1;
        _totalSupply &= 1;
        _totalSupply |= 1;
    }

    function updateVariable() public pure {
        uint256 x;
        x += 1;
        x *= 1;
        x /= 1;
        x -= 1;
        x &= 1;
        x |= 1;
    }
}

Additional context
Running my code visits each binary expression (after desugaring), and checks to ensure that the left.loc().end() < right.loc.start(), and if not, prints an error. This is the output of my test on the above program:

$ cargo run                      
   === Function: updateStorage() ===

Error: start: 213 >= end 212 in expression _totalSupply += 1
    left: _totalSupply += 1
    right: 1

Error: start: 240 >= end 239 in expression _totalSupply *= 1
    left: _totalSupply *= 1
    right: 1

Error: start: 267 >= end 266 in expression _totalSupply /= 1
    left: _totalSupply /= 1
    right: 1

Error: start: 294 >= end 293 in expression _totalSupply -= 1
    left: _totalSupply -= 1
    right: 1

Error: start: 321 >= end 320 in expression _totalSupply &= 1
    left: _totalSupply &= 1
    right: 1

Error: start: 348 >= end 347 in expression _totalSupply |= 1
    left: _totalSupply |= 1
    right: 1

   === Function: updateVariable() ===

Success

Success

Success

Success

Success

Success  

It's clear that the bug exists only when the LHS of an op-assign (+=, |=) is a storage access. This does not occur for local variables.

@BenTheKush
Copy link
Contributor Author

For what it's worth, from a client perspective I think there is strong value added for an AST library to not do this desugaring of a += b -> a = a + b. I may want to treat these two operations differently, and it's possible that I will try to access a source location that doesn't exist.

For instance, there is no way for me to access the location of the + in add expression aside from looking between the left and right expressions in the original source. But now, I need to double check that I don't end up with a += instead of a +...it's weird that we aren't guaranteed that Add{left, right} corresponds to a + operator in the original code.

Anyway, not the point of this issue, just my two cents

@seanyoung
Copy link
Contributor

You're right, we should have an Expression::AddAssign in the AST. There are a few other things we de-sugar which we should not, like type() functions.

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

No branches or pull requests

3 participants
@seanyoung @BenTheKush and others