Sitelet https://web.archive.org/web/20201113050245/https://github.com/ethereum/solidity/pull/8952
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

[Sol->Yul] Simplify end visit for internal calls (refactor) #8952

Merged

Conversation

@cameel
Copy link
Member

@cameel cameel commented May 14, 2020 •

Based on #8951 which needs to be merged first. Could also be rebased on #8949 which is immediately below but this requires resolving a minor conflict so I left them one on the other. It's based on develop now.

Refactoring changes extracted from #8943 (and before that from #8797). Does not affect functionality.
Related to #6788 and #8485.

@cameel cameel requested a review from chriseth May 14, 2020
@cameel cameel self-assigned this May 14, 2020
@cameel cameel added this to Review in progress in Solidity May 14, 2020
@cameel cameel force-pushed the sol-yul-refactor-simplify-end-visit-for-internal-calls branch from 663d40f to daf82a3 May 14, 2020
@cameel cameel force-pushed the sol-yul-refactor-simplify-end-visit-for-internal-calls branch 2 times, most recently from a2db99a to bd6845d May 15, 2020
@cameel cameel force-pushed the sol-yul-refactor-split-internal-dispatch branch from 75efd4f to 9209810 May 19, 2020
@cameel cameel force-pushed the sol-yul-refactor-simplify-end-visit-for-internal-calls branch from bd6845d to 4c847cc May 19, 2020
@cameel cameel changed the base branch from sol-yul-refactor-split-internal-dispatch to develop May 19, 2020
@cameel
Copy link
Member Author

@cameel cameel commented May 19, 2020

I have changed the base branch to develop so this can now be reviewed independently. I just realized that #8797 does not really require this refactor.

@cameel
Copy link
Member Author

@cameel cameel commented May 19, 2020 •

Well, CI just proved me wrong :) While removing this PR from under #8797 did not cause conflicts, the functions from here are still used there. I'll just keep a copy of the commit in both branches. I think this will get merged quickly and the commit will simply disappear from #8797 after rebase.

else if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
solAssert(!functionType->bound(), "");
solUnimplementedAssert(!memberAccess || !functionType->bound(), "");

This comment has been minimized.

@chriseth

chriseth May 19, 2020
Contributor

These assertions are all rather difficult to read. Would it be better to use something like

if (a)
  solAssert(!b);

?

This comment has been minimized.

@cameel

cameel May 20, 2020 •
Author Member

On the other hand I like that each one is now self-contained instead of having multiple nesting levels. That's a bit less of a problem now that I introduced referencedFunctionDeclaration() but before that I was getting a bit lost in it because the three main branches (identifier vs member vs something else) had overlapping but not exactly identical checks.

Also, they're just extra sanity checks, not a part of the logic and if they're not compact they obscure the more important bits of code in my opinion.

I agree that they're harder to read though. Before I go on with this change, here's a side-by side comparison with the best nested version I could come up with:

solUnimplementedAssert(!memberAccess || !functionType->bound(), "");
solAssert(memberAccess || !functionType->bound(), "");

solAssert(!memberAccess || !unresolvedFunctionDef || functionType->declaration() == *unresolvedFunctionDef, "");
solAssert(unresolvedFunctionDef || !functionType->hasDeclaration(), "");

// functionDef = ...

solAssert((functionDef == nullptr) == (unresolvedFunctionDef == nullptr), "");
solAssert(!functionDef || functionDef->isImplemented(), "");
if (memberAccess)
	solUnimplementedAssert(!functionType->bound(), "");
else
	solAssert(!functionType->bound(), "");

if (unresolvedFunctionDef && memberAccess)
	solAssert(functionType->declaration() == *unresolvedFunctionDef, "");
else if (!unresolvedFunctionDef)
	solAssert(!functionType->hasDeclaration(), "");

// functionDef = ...

if (functionDef)
{
	solAssert(unresolvedFunctionDef, "");
	solAssert(functionDef->isImplemented(), "");
}
else
	solAssert(!unresolvedFunctionDef, "");

Should I change it?

This comment has been minimized.

@cameel

cameel May 20, 2020
Author Member

I changed it after all. The version with ifs is not that bad when it's not mixed with logic and the compact one is indeed harder to read. I just noticed that the condition for unresolvedFunctionDef && memberAccess should really have been just unresolvedFunctionDef.

This comment has been minimized.

@chriseth

chriseth May 20, 2020
Contributor

Not so sure about either :)

This comment has been minimized.

@cameel

cameel May 20, 2020
Author Member

I tweaked it a bit more. Here's how it looks like now:

if (functionDef)
{
	solAssert(memberAccess || identifier, "");
	solAssert(functionType->declaration() == *functionDef, "");

	if (identifier)
		functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract());

	solAssert(functionDef->isImplemented(), "");
}
else
	solAssert(!functionType->hasDeclaration(), "");

if (memberAccess)
	solUnimplementedAssert(!functionType->bound(), "");
else
	solAssert(!functionType->bound(), "");
FunctionDefinition const* functionDef = (!unresolvedFunctionDef ?
nullptr :
(memberAccess ?
unresolvedFunctionDef :
&unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract())
)
);
Comment on lines 653 to 659

This comment has been minimized.

@chriseth

chriseth May 19, 2020
Contributor

Isn't the following easier to understand? Yes, the variable is reassigned, but that's what we have a multi-paradigm language for, don't we? I would actually even go so far and use the same variable for unresolvedFunctionDef and functionDef. After all, what does "unresolved" mean in case we do not use a virtual function call?

Suggested change
FunctionDefinition const* functionDef = (!unresolvedFunctionDef ?
nullptr :
(memberAccess ?
unresolvedFunctionDef :
&unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract())
)
);
FunctionDefinition const* functionDef = unresolvedFunctionDef;
if (dynamic_cast<Identifier const*>(&_functionCall.expression()))
functionDef = unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract());

This comment has been minimized.

@chriseth

chriseth May 19, 2020
Contributor

Also note that you are implictly assuming here that "no member access" and "definition found" means that we have a direct identifier.

This comment has been minimized.

@cameel

cameel May 20, 2020
Author Member

Also note that you are implictly assuming here that "no member access" and "definition found" means that we have a direct identifier.

I've been hoping to keep casts to Identifer and MemberAccess hidden neatly inside referencedFunctionDeclaration(). Not very successfully. I'll add it back and add an assert for that case.

Isn't the following easier to understand? Yes, the variable is reassigned, but that's what we have a multi-paradigm language for, don't we?

ok, it's indeed simpler. I shouldn't have tried so hard to get rid of Identifier.

I would actually even go so far and use the same variable for unresolvedFunctionDef and functionDef. After all, what does "unresolved" mean in case we do not use a virtual function call?

In this particular case maybe reusing the variable is not that bad but in general I think it adds some unnecessary mental overhead to track what it refers to. Having separate names for things is better in my opinion. Of course it requires coming up with good names which can be hard at times.

This comment has been minimized.

@cameel

cameel May 20, 2020
Author Member

Done. I changed it according to your suggestions - added assertion for identifier, changed to reassignment and made the code reuse functionDef variable.

I still think that separate names would be clearer. Using the same name in assertions might give a false impression that they all still hold after the reassignment. This is not true for example for functionType->declaration() == *functionDef.

@cameel cameel force-pushed the sol-yul-refactor-simplify-end-visit-for-internal-calls branch 3 times, most recently from 0d9a5eb to b7ee796 May 20, 2020
- Define IRHelpers::referencedFunctionDeclaration() to avoid repeating the same dynamic_casts over and over again.
@cameel cameel force-pushed the sol-yul-refactor-simplify-end-visit-for-internal-calls branch from b7ee796 to 0943333 May 20, 2020
@chriseth chriseth merged commit 042dc96 into develop May 25, 2020
36 checks passed
36 checks passed
ci/circleci: b_archlinux Your tests passed on CircleCI!
Details
ci/circleci: b_docs Your tests passed on CircleCI!
Details
ci/circleci: b_ems Your tests passed on CircleCI!
Details
ci/circleci: b_osx Your tests passed on CircleCI!
Details
ci/circleci: b_ubu Your tests passed on CircleCI!
Details
ci/circleci: b_ubu18 Your tests passed on CircleCI!
Details
ci/circleci: b_ubu_asan Your tests passed on CircleCI!
Details
ci/circleci: b_ubu_asan_clang Your tests passed on CircleCI!
Details
ci/circleci: b_ubu_clang Your tests passed on CircleCI!
Details
ci/circleci: b_ubu_cxx20 Your tests passed on CircleCI!
Details
ci/circleci: b_ubu_ossfuzz Your tests passed on CircleCI!
Details
ci/circleci: b_ubu_release Your tests passed on CircleCI!
Details
ci/circleci: chk_antlr_grammar Your tests passed on CircleCI!
Details
ci/circleci: chk_buglist Your tests passed on CircleCI!
Details
ci/circleci: chk_coding_style Your tests passed on CircleCI!
Details
ci/circleci: chk_docs_pragma_min_version Your tests passed on CircleCI!
Details
ci/circleci: chk_proofs Your tests passed on CircleCI!
Details
ci/circleci: chk_pylint Your tests passed on CircleCI!
Details
ci/circleci: chk_spelling Your tests passed on CircleCI!
Details
ci/circleci: t_ems_compile_ext_colony Your tests passed on CircleCI!
Details
ci/circleci: t_ems_compile_ext_gnosis Your tests passed on CircleCI!
Details
ci/circleci: t_ems_compile_ext_zeppelin Your tests passed on CircleCI!
Details
ci/circleci: t_ems_solcjs Your tests passed on CircleCI!
Details
ci/circleci: t_osx_cli Your tests passed on CircleCI!
Details
ci/circleci: t_osx_soltest Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_asan_cli Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_asan_constantinople Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_asan_constantinople_clang Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_clang_soltest Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_cli Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_release_cli Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_release_soltest Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_soltest Your tests passed on CircleCI!
Details
ci/circleci: t_ubu_soltest_enforce_yul Your tests passed on CircleCI!
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
Solidity automation moved this from Review in progress to Done May 25, 2020
@chriseth chriseth deleted the sol-yul-refactor-simplify-end-visit-for-internal-calls branch May 25, 2020
@cameel cameel added this to In progress in Sol -> Yul CodeGen via automation Jun 3, 2020
@cameel cameel moved this from In progress to Done in Sol -> Yul CodeGen Jun 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Solidity
  
Done
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.