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

Checked arithmetic by default. #9465

Merged
merged 5 commits into from
Oct 19, 2020
Merged

Checked arithmetic by default. #9465

merged 5 commits into from
Oct 19, 2020

Conversation

chriseth
Copy link
Contributor

@chriseth chriseth commented Jul 22, 2020

Depends on #9479.

TODO:

  • documentation
  • document: division by zero is always checked
  • wrapping arithmetics for Sol -> Yul
  • disallow -x for unsigned x outside of "unchecked" - or do we want to disallow it in general? 0-x is still OK, I would say.
  • decide which error to report and change all errors.

libsolidity/codegen/CompilerContext.h Outdated Show resolved Hide resolved
libsolidity/codegen/ContractCompiler.cpp Outdated Show resolved Hide resolved
libsolidity/codegen/ContractCompiler.cpp Outdated Show resolved Hide resolved
libsolidity/ast/AST.h Outdated Show resolved Hide resolved
@stackenbotten
Copy link

There was an error when running chk_coding_style for commit d4651d289c3a790a5a8bb0d9b1818762e9a06788:

Error: Trailing whitespace found:
 libsolidity/ast/AST.h:1341: Statement(_id, _location, _docString), 

Please check that your changes are working as intended.

@stackenbotten
Copy link

There was an error when running chk_coding_style for commit 84ac4987282254e1782946313163dcf202c6d5e7:

Error: Trailing whitespace found:
 libsolidity/ast/AST.h:1341: Statement(_id, _location, _docString), 

Please check that your changes are working as intended.

@stackenbotten
Copy link

There was an error when running chk_coding_style for commit 44e18bc91be7005599bed9f3c05118fb37082fd2:

Error: Trailing whitespace found:
 libsolidity/ast/AST.h:1341: Statement(_id, _location, _docString), 

Please check that your changes are working as intended.

@leonardoalt
Copy link
Member

Rebase problems?

@axic
Copy link
Member

axic commented Aug 5, 2020

This still needs to be rebased and perhaps need to merge develop into breaking too. At the same time this seems to depend on #9479, so I'd suggest turning this to draft until that is merged.

@axic axic marked this pull request as draft August 5, 2020 22:00
@stackenbotten
Copy link

There was an error when running chk_coding_style for commit b3674152aa040dd440da1143a07ce8522eeda451:

Error: Trailing whitespace found:
 libsolidity/ast/AST.h:1341: Statement(_id, _location, _docString), 

Please check that your changes are working as intended.

@chriseth chriseth changed the base branch from breaking to breaking_8 August 6, 2020 14:58
@chriseth
Copy link
Contributor Author

chriseth commented Aug 6, 2020

Rebased and changed target to breaking_8 (@axic requested to have one breaking branch per release at some point)

@chriseth chriseth changed the base branch from breaking_8 to breaking September 14, 2020 18:46
@stackenbotten
Copy link

There was an error when running chk_coding_style for commit c24cc01f404d3796f672cdbcd89211dac987f18c:

Error: Trailing whitespace found:
 libsolidity/ast/AST.h:1347: Statement(_id, _location, _docString), 

Please check that your changes are working as intended.

@chriseth chriseth force-pushed the unchecked branch 8 times, most recently from 38be2ad to e7e6212 Compare September 16, 2020 15:27
@chriseth chriseth force-pushed the unchecked branch 5 times, most recently from 8450cc3 to 2b3900d Compare September 24, 2020 14:11
@chriseth chriseth force-pushed the unchecked branch 3 times, most recently from 709edb0 to a991b2f Compare October 14, 2020 15:52
block:
LBrace ( statement | uncheckedBlock )* RBrace;

uncheckedBlock: Unchecked block;
Copy link
Member

Choose a reason for hiding this comment

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

This allows for nested unchecked blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is not a parser but a syntax checker restriction.

Copy link
Member

Choose a reason for hiding this comment

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

Is if (<sth>) unchecked { x; } invalid? (I'll probably know when I'm done reviewing...)

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, apparently it isn't - not sure if it couldn't be valid, but fine!

There are two modes in which arithmetic is performed on these types: The "wrapping" or "unchecked" mode and the "checked" mode.
By default, arithmetic is always "checked", which mean that if the result of an operation falls outside the value range
of the type, the call is reverted through a :ref:`failing assertion<assert-and-require>`. You can switch to "unchecked" mode
using ``unchecked { ... }``. More details can be found in the section about <unchecked>.
Copy link
Member

Choose a reason for hiding this comment

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

Is <unchecked> at the end an actual link to the section?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope, it's not. Will fix.

modes in regard to over- and underflow:

By default, all arithmetic is checked for under- or overflow, but this can be disabled
using the <unchecked> block, resulting in wrapping arithmetic. More details
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
using the <unchecked> block, resulting in wrapping arithmetic. More details
using the <unchecked> block, resulting in modular arithmetic. More details

Copy link
Member

Choose a reason for hiding this comment

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

Is <unchecked> a link?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed the link. Rust also uses the term "wrapping": https://doc.rust-lang.org/std/primitive.u32.html#method.wrapping_add

Maybe "wrapping" is also less intimidating than "modular"?

@@ -380,6 +384,8 @@ class CompilerContext
std::map<Declaration const*, std::vector<unsigned>> m_localVariables;
/// The contract currently being compiled. Virtual function lookup starts from this contarct.
ContractDefinition const* m_mostDerivedContract = nullptr;
/// Whether to use checked arithmetic.
std::stack<Arithmetic> m_arithmetic;
Copy link
Member

Choose a reason for hiding this comment

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

It needs to be a stack because you can call a function inside an unchecked block and then open another unchecked block inside that function, but the rest of that function is checked. Correct?

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 actually does not need to be a stack anymore, I think.

Copy link
Member

@ekpyron ekpyron left a comment

Choose a reason for hiding this comment

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

I haven't seen anything that really bothered me in the first pass, but I wasn't extremely careful about it, so I'm a bit hesitant approving just yet...

Comment on lines 519 to 520
An overflow or underflow is the situation where the resulting value of an arithmetic operation,
when executed on an unrestricted integer, falls outside the range of the result type.
Copy link
Member

Choose a reason for hiding this comment

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

This seems a bit out of place to me here... this should probably be the second paragraph of the section at the very top?

block:
LBrace ( statement | uncheckedBlock )* RBrace;

uncheckedBlock: Unchecked block;
Copy link
Member

Choose a reason for hiding this comment

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

Is if (<sth>) unchecked { x; } invalid? (I'll probably know when I'm done reviewing...)

block:
LBrace ( statement | uncheckedBlock )* RBrace;

uncheckedBlock: Unchecked block;
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, apparently it isn't - not sure if it couldn't be valid, but fine!

@@ -460,7 +479,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
m_context << commonType->literalValue(nullptr);
else
{
bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op);
bool cleanupNeeded = m_context.arithmetic() == Arithmetic::Checked || cleanupNeededForOp(commonType->category(), c_op);
Copy link
Member

Choose a reason for hiding this comment

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

Do we actually need cleanup here or aren't we doing it twice then? The checked arithmetic yul functions do the cleanup themselves, don't they?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

true!

@chriseth
Copy link
Contributor Author

if (<sth>) unchecked { x; } is invalid currently. It was just looking weird to me. We anyway need a special case unless we also want
function f() public unchecked { x; } to be valid.

@ekpyron
Copy link
Member

ekpyron commented Oct 15, 2020

if (<sth>) unchecked { x; } is invalid currently. It was just looking weird to me. We anyway need a special case unless we also want
function f() public unchecked { x; } to be valid.

It's fine, I was just wondering when I saw the grammar - disallowing it now is definitely good - we can still allow it later anyways, while not the other way!

Copy link
Member

@leonardoalt leonardoalt left a comment

Choose a reason for hiding this comment

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

A few small comments.

It would be nice to also have a few more corner case tests:

  • unchecked function called by checked function called by unchecked function
  • checked function called by unchecked function
  • is it possible to have contract C { uint x = unchecked { ... }; } ?

@@ -275,7 +275,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
solAssert(*_assignment.annotation().type == leftType, "");
bool cleanupNeeded = false;
if (op != Token::Assign)
cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp);
cleanupNeeded = m_context.arithmetic() == Arithmetic::Checked || cleanupNeededForOp(leftType.category(), binOp);
Copy link
Member

Choose a reason for hiding this comment

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

Is this just extra safety?

Copy link
Member

Choose a reason for hiding this comment

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

Same as #9465 (comment) - since the arithmetic functions in yul already do the cleanup, we should probably not clean up before them.

});
}


Copy link
Member

Choose a reason for hiding this comment

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

Extra blank

break;
case Token::Mod:
fun = m_utils.checkedIntModFunction(*type);
fun = m_utils.intModFunction(*type);
Copy link
Member

Choose a reason for hiding this comment

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

What about exp?

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 branches out earlier in visit(BinaryOperation const& _binOp)

BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Var));
BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Reference));
Copy link
Member

Choose a reason for hiding this comment

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

Where did Reference come from?

Copy link
Member

Choose a reason for hiding this comment

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

IIUC the test verifies that isReservedKeyword is correctly true for the first and the last reserved keyword - the last one used to be unchecked, but then we made var reserved again and added it as new last one here, but just kept unchecked, so since then the idea is "check first and last reserved keyword and a random one in between" - the random one in between can't be unchecked anymore, so now it's reference...

@leonardoalt
Copy link
Member

Actually some commits need to be squashed.

@leonardoalt
Copy link
Member

Looks good. What about decide which error to report and change all errors.?

@chriseth
Copy link
Contributor Author

Will be done in #10013

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.

8 participants