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

Solidity binding fixes driven by Sanctuary #1149

Open
wants to merge 68 commits into
base: main
Choose a base branch
from

Conversation

ggiraldez
Copy link
Contributor

@ggiraldez ggiraldez commented Nov 15, 2024

This PR contains fixes to bindings for issues that were found by trying to resolve all references in Sanctuary, as well as test cases to verify them. It also improves documentation of the rules and improvements (renames and rearrangements) for readability. The built-in types are also standardized with a PascalCase naming convention.

Most notably, it introduces extension scopes to support resolving attached function set via using directives. These extension scopes need to be accessible at any point during the resolution of a symbol stack if the originating reference is located in a lexical scope which is affected by a using directive. This forced a refactor of the lexical scope node structure, introducing a new .extended_scope available in nodes where references may need to resolve to attached functions (ie. function bodies, constant initialization expressions, etc.) Also, for Solidity < 0.7.0 using directives are inherited, so we need to propagate the new extension scopes for all sub-contracts relative to where the directive is located.

The extension scope mechanism is implemented using the jump to scope and scope stack features of the stack graphs. The extended scope would optionally push the extension scope for the current contract and then continue resolution through the normal lexical scope. This effectively doubles the search space in the graph when performing a resolution, and this happens every time we inject a new extension scope to the scope stack. This has the potential to exponentially increase the running times and memory requirements. This is a known caveat which will be addressed in a future PR.

- `string` and `bytes` are exported as built-in variables resolving to types
that provide a `concat` function
- `address` can be used as a function to cast a parameter to `address` to eg.
retrieve the balance
This makes resolving to attached functions on types even when the reference of
those types happen in a different lexical scope.
And also provide alternative paths with and without propagating the dynamic
scope. Otherwise, since scope accumulate on the stack it's possible we'll need
to resolve an attached function with the wrong dynamic scope at the top of the
scope stack.
Both contracts and libraries can optionally push the dynamics scope when
traversing to the parent lexical scope (ie. the source unit). But for libraries,
they can also optionally push their name to correctly bind internal types which
were extended (with `using`) by their qualified name (ie. `Lib.Type`).
Applying a function call with a type will always return a value of that type, so
a symbol stack `type,()` is equivalent to `type,@typeof`. Reflect on the binding
entry point of the using clause.
These are parsed as modifiers, and they need a similar treatment as parent
constructor calls in new constructor definitions.
A public getter will never return a complex data type, and a special getter
function is generated for arrays, mappings and structs. Arrays and mappings we
were already handling, but this commit makes that more explicit. For structs, we
bind the type of the first field. There is a caveat here: if the first field is
not a type that can be flattened and returned in a public getter we're still
binding it, instead of the first field that can be returned. But I don't see a
way to be 100% correct here.
@ggiraldez
Copy link
Contributor Author

Re-running the tests against Sanctuary I noticed some things broke with the last few changes. Expect a couple more commits added to this PR.

@ggiraldez ggiraldez marked this pull request as draft November 21, 2024 21:22
We are usually binding `this` to the enclosing contract type, but it can also be
used in library code. For this special ocasion, bind it to a built-in. This will
need to be resolved later to the actual contract instance by the backend.
@ggiraldez ggiraldez marked this pull request as ready for review November 22, 2024 18:57
ggiraldez added a commit to manastech/slang that referenced this pull request Nov 25, 2024
This happens when there is special member access in some Yul identifier (like
`x.slot` or `x.offset`). I think this issue was introduced when unreserving the
`address` keyword since that changed the structure of `YulPath`. There is a
proper test case in NomicFoundation#1149 already, but without this fix running Sanctuary with
existing rules crashes.
Copy link
Collaborator

@OmarTawfik OmarTawfik left a comment

Choose a reason for hiding this comment

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

LGTM.
For the unresolved comments, I'm happy to follow up on it later.

github-merge-queue bot pushed a commit that referenced this pull request Nov 28, 2024
This PR adds a step to Sanctuary testing to exercise bindings. We now
also consider a Sanctuary test to fail if any of the references found in
it cannot be resolved to one (or possibly more) definitions.

This also includes a small binding rules fix to avoid crashing if we
find a Solidity source file with an assembly block that has a `YulPath`
with more than one identifier (eg. a member access like `x.slot`). This
is properly tested in #1149, but without it running Sanctuary tests will
crash since there are contracts with the aforementioned contents.
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