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

fix: concretize Self references in method signatures #1001

Merged
merged 9 commits into from
Jan 23, 2023

Conversation

miraclx
Copy link
Contributor

@miraclx miraclx commented Jan 21, 2023

Resolves: #972, #1000.

This patch replaces any Self references in method signatures with the concrete type of the impl block itself.

Example:

#[near_bindgen]
#[derive(Serialize, Deserialize)]
struct MyType {
    data: u8,
}

#[near_bindgen]
impl MyType {
    fn set_state(&mut self, new_state: Self) -> Self {
        std::mem::replace(self, new_state)
    }
}

the impl block is transformed into:

impl MyType {
    fn set_state(&mut self, new_state: MyType) -> MyType {
        std::mem::replace(self, new_state)
    }
}

this transformation is maintained regardless of type complexity or nesting level.

Worth noting, this does not transform Self references in generic bounds. But they're going to be prohibited following #980 anyway. So I left it out.

@miraclx miraclx changed the base branch from miraclx/fix-abi-embed-compile-error to master January 21, 2023 15:07
@willemneal
Copy link
Contributor

Shouldn't this be fixed in the abi? Seems like it should be handled there and not banned. Witgen handles it.

@miraclx
Copy link
Contributor Author

miraclx commented Jan 21, 2023

Shouldn't this be fixed in the abi? Seems like it should be handled there and not banned. Witgen handles it.

Well, the issue here is mostly that the intermediate code generated that eventually generates the ABI derives the schema for a type from a method call that is generic over the type. Specifically a line that does:

gen.subschema_for::<T>();

and in the case where T is Self, Self will be undefined because the generated code lives inside an extern "C" block which is unbound to an impl block.

At the moment, the example code just fails on master (See #972). This patch fixes that to make sure it both compiles and has ABI generated for the appropriate type.

An alternative as mentioned in #972 would just be to warn / fail with a useful error nudging users to use concrete types, but I wanted to submit this first so we know it's possible and not just jump head-first into disallowing it.

Copy link
Contributor

@itegulov itegulov left a comment

Choose a reason for hiding this comment

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

LGTM, just a couple of nitpicks. This is a great fix for the current major version of SDK, but I wonder if we should still explore whether banning Self makes sense in 5.0. Not because this PR does an unsatisfactory job but rather because it seems like a potentially harmful practice as was pointed out here. I can create an issue if you agree that it is worth exploring.

near-sdk-macros/src/core_impl/abi/abi_generator.rs Outdated Show resolved Hide resolved
near-sdk-macros/src/core_impl/utils/mod.rs Outdated Show resolved Hide resolved
}

pub fn sanitize_self(typ: &Type, replace_with: &TokenStream2) -> syn::Result<Type> {
syn::parse2(_sanitize_self(quote! { #typ }, replace_with)).map_err(|original| {
Copy link
Contributor

Choose a reason for hiding this comment

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

Sanitizing TokenStream instead of Type is a nice trick. I remember trying to sanitize Self in the past, and it required so much boilerplate to traverse all Type variants.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, nightmarish 😨

@miraclx
Copy link
Contributor Author

miraclx commented Jan 23, 2023

it seems like a potentially harmful practice as was pointed out #972 (comment). I can create an issue if you agree that it is worth exploring.

Yup, Sounds good to me!

@miraclx miraclx merged commit 9aef9fd into master Jan 23, 2023
@miraclx miraclx deleted the miraclx/fix-self-references branch January 23, 2023 07:18
@itegulov
Copy link
Contributor

Done: #1005

@mitinarseny
Copy link
Contributor

Hey Team,
This fix makes it impossible to define a trait with #[init] method that returns Self:

#[ext_contract(ext_my_contract)]
pub trait MyContract {
    #[init]
    fn new() -> Self;
}

as it expands into

pub trait MyContract {
    fn new() -> MyContract;
}

pub mod ext_my_contract {
    // ...
}
error[E0782]: trait objects must include the `dyn` keyword
 --> src/lib.rs:2:16:
  |
6 |     fn new() -> Self;
  |                 ^^^^
  |
help: add `dyn` keyword before this trait
  |
6 |     fn new() -> dyn Self;
  |                 +++

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.

ABI generation fails with Self arg types ABI generation fails with Self return types
4 participants