-
Notifications
You must be signed in to change notification settings - Fork 216
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
Represent contracts by their program id #1474
Conversation
151504a
to
4c8695b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a complete review yet, sorry. I'll get back to this later today.
contract foo {
function meh() external {
for (uint64 i = 0; i < 64; i++) {
X a = new X();
}
}
}
@program_id("Foo5mMfYo5RhRcWa4NZ2bwFn4Kdhe8rNK5jchxsKrivA")
contract X {}
Results in:
thread 'main' panicked at 'assertion failed: contract_args.accounts.is_some()', src/emit/solana/target.rs:1268:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
src/sema/mutability.rs
Outdated
state.read(loc) | ||
state.data_account |= DataAccountUsage::READ; | ||
state.read(loc); | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why stop here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we continue recursing, there would be two mutability errors. One for the subscript and other for the storage variable nested within the subscript expression. Perhaps the best solution here is just to track the variable access instead of the subscript.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps the best solution here is just to track the variable access instead of the subscript.
There is a test case that fails if I do that. I need to keep the expressions separated and not recurse on on them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's fundamentally broken.
contract C {
bool x;
int[4] a;
function test() public view returns (int) {
return foo()[1];
}
function foo() internal returns (int[4] storage) {
x = true;
return a;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, do not say it is broken without testing. The IDL generated for this contract is correct:
{
"name": "test",
"accounts": [
{
"name": "dataAccount",
"isMut": true,
"isSigner": false,
"isOptional": false
}
],
"args": [],
"returns": "i256"
}
The collection of accounts between function calls happens in codegen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My goal here was not to make mutability check perfect, but make it work for the tests we have while collecting the data account. Not continuing the traversal was an existing problem and its solution is not simple to include in this PR. I am happy to work on this separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point is this: you can't just not traverse parts of the ast when looking for mutability.
Right now, the main branch stops traversing when we match one of the cases in
fn read_expression
.
I just replicated the main branch's behavior by stopping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to work on this on separate PR, sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to work on this on separate PR, sure.
Thanks! I should make a start on this today or tomorrow. As the problem also exists in the main branch, I believe it deserves a separate piece of work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll track this issue at #1493.
I've been thinking a lot of about this change. The thing that bother is that contract variables (i.e. First of all, we could allow calling contracts without a contract variable: contract A {
function test() public {
int value = B.foo();
}
}
contract B {
int a;
function foo() public returns (int) { return a; }
} There is a slight complication there: B could be a base contract of A: contract A is B {
function test() public {
int value = B.foo();
}
}
contract B {
int a;
function foo() public returns (int) { return a; }
} This would default to a call to base contract unless accounts are specified. Secondly, the contract variable types make no sense. They do not hold useful information. I think they should be disallowed completely, as they are not intuitive on Solana. |
We still allow contracts to be declared without a program id. In your example, for instance, calling Also, what would be the syntax for the constructor? |
Good point.
You can still do |
Perhaps, we should get rid of the account management at this point and explicitly require the accounts to be passed when instantiating a contract. Even if |
a427b0d
to
307ecd8
Compare
966a62b
to
7a9b94f
Compare
This PR is already large enough (it touches 110 files). I decided to merge this first and open another PR for the new contracts syntax (there will be another one to refactor the mutability check as well). Please, review it when possible @seanyoung and @xermicus. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, just some nitpicks
cfg_no: usize, | ||
ast_no: usize, | ||
) { | ||
if contracts[contract_no].cfg[cfg_no].blocks.is_empty() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm aware that this is already in main
, but it lets me wondering whether it should be an assert!
instead. Unless being able to add empty CFGs has a fair reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we have a modifier, an empty CFG is generated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I guess an empty function body not returning anything might also add an empty CFG. But this could be detected optimized away.
src/sema/expression/mod.rs
Outdated
|
||
impl ExprContext { | ||
pub fn enter_loop(&mut self) { | ||
self.loop_counter += 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use checked arithmetic and error out if too many loops. Also could indicate a compiler bug if that happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the variable is of type usize
, Rust will already warn of an overflow. This won't be, however, an error for the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
This PR changes the inner representation of Solana contracts from their data account to their program id.
All the changes are listed here: #1430 (except the last item, which will have a dedicated PR).
Constructors will still require an account to be initialized, even if the contract has no storage variables and no function requires a data account. This setting does not influence the overall functionality of Solang, but it is something we must solve as I have described in #1480 .
There are small changes in Polkadot tests, because I found the mutability check to be incomplete. We were not recursing down all expressions, so reads and writes from the state went unnoticed. I fixed this as well.