Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
Checked arithmetic by default. #9465
Conversation
There was an error when running
Please check that your changes are working as intended. |
There was an error when running
Please check that your changes are working as intended. |
There was an error when running
Please check that your changes are working as intended. |
Rebase problems? |
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. |
There was an error when running
Please check that your changes are working as intended. |
Rebased and changed target to |
There was an error when running
Please check that your changes are working as intended. |
38be2ad
to
e7e6212
8450cc3
to
2b3900d
709edb0
to
a991b2f
block: | ||
LBrace ( statement | uncheckedBlock )* RBrace; | ||
|
||
uncheckedBlock: Unchecked block; |
ekpyron
Oct 15, 2020
Member
Is if (<sth>) unchecked { x; }
invalid? (I'll probably know when I'm done reviewing...)
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>. |
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 |
leonardoalt
Oct 14, 2020
Member
using the <unchecked> block, resulting in wrapping arithmetic. More details | |
using the <unchecked> block, resulting in modular arithmetic. More details |
chriseth
Oct 15, 2020
Author
Contributor
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; |
leonardoalt
Oct 14, 2020
Member
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?
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... |
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. |
ekpyron
Oct 15, 2020
Member
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; |
ekpyron
Oct 15, 2020
Member
Is if (<sth>) unchecked { x; }
invalid? (I'll probably know when I'm done reviewing...)
block: | ||
LBrace ( statement | uncheckedBlock )* RBrace; | ||
|
||
uncheckedBlock: Unchecked block; |
@@ -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); |
ekpyron
Oct 15, 2020
Member
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?
|
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! |
A few small comments. It would be nice to also have a few more corner case tests:
|
@@ -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); |
ekpyron
Oct 16, 2020
Member
Same as #9465 (comment) - since the arithmetic functions in yul already do the cleanup, we should probably not clean up before them.
break; | ||
case Token::Mod: | ||
fun = m_utils.checkedIntModFunction(*type); | ||
fun = m_utils.intModFunction(*type); |
chriseth
Oct 19, 2020
Author
Contributor
It branches out earlier in visit(BinaryOperation const& _binOp)
BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Var)); | ||
BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Reference)); |
ekpyron
Oct 16, 2020
Member
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...
Actually some commits need to be squashed. |
Looks good. What about |
Will be done in #10013 |
Depends on #9479.
TODO:
-x
for unsignedx
outside of "unchecked" - or do we want to disallow it in general?0-x
is still OK, I would say.