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

add Compilation API #1194

Merged
merged 3 commits into from
Dec 17, 2024
Merged

add Compilation API #1194

merged 3 commits into from
Dec 17, 2024

Conversation

OmarTawfik
Copy link
Contributor

And along, a few fixes to the parser and binding APIs (more details in the added changesets).

Copy link

changeset-bot bot commented Dec 13, 2024

🦋 Changeset detected

Latest commit: 8d72d5d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@nomicfoundation/slang Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

BindingGraph, BindingLocation, BuiltInLocation, UserFileLocation,
};

/// TODO: This is a work-around for the fact that `metaslang_bindings` internals (handles, locators, etc...) are exposed.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ggiraldez these are the wrappers I mentioned, due to the fact that the original Definition and Reference types have handles in them.
They should be removed once the metaslang_bindings API is fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is the problem we don't want to expose those handles to Typescript? The handles themselves are usize values. Or is the pointer/reference to the bindings object that's the problem? I recall you mentioning that they should be copyable.

Since we're going to pre-resolve all references anyway, we may just get away with cursors in the end. But asking to understand and to be aware of what are the limitations.

Copy link
Contributor Author

@OmarTawfik OmarTawfik Dec 16, 2024

Choose a reason for hiding this comment

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

is the pointer/reference to the bindings object that's the problem?

Yes. For WIT resources, we need to track their lifetimes on the other side of the FFI boundary, and there isn't (currently) a straightforward way to do that with the handle references.
For the existing WIT resources, we either wrap them in Rc<> (define_rc_wrapper()) or RefCell<> (define_refcell_wrapper()).

@@ -267,7 +271,7 @@ impl<KT: KindTypes + 'static> Bindings<KT> {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ggiraldez unrelated to the PR, but I wonder if this TODO is already fixed?

// TODO: what should we do if the parent reference
// cannot be resolved at this point?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not, but I don't know if there's something meaningful we can do in this case. For now, if we cannot resolve a parent it's simply ignored.

This resolution method is used internally, while resolving other references. Eg. for determining which methods are reachable when resolving a virtual call; or to determine which extension scopes (ie. attached functions) apply in a particular context. Resolving parents in these circumstances should not be a problem unless the user code is invalid, or if there's a bug in the binding rules.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unless the user code is invalid

Which is a possible use case, right?

if we cannot resolve a parent it's simply ignored

Sounds good. I will use None in this case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it's certainly possible that the user code is invalid and that means we cannot resolve a parent reference. Eg. definining contract Test is Base {} without defining Base. This failure to resolve is currently ignored. I think my main concern for writing the TODO was that while this may impact resolved references, there's no feedback given to the user indicating that this may be a problem. In general, the current behaviour should be ok though.


const defs = reference.definitions();
assert.equal(defs.length, 0);
// TODO: there should be one built-in definition, right?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ggiraldez I'm getting this result, and I don't think it is correct.
IIUC, The commented out assertion should have succeeded, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Correct. This is probably because there's a missing step when constructing the BindingsGraph in CompilationUnit to add the built-ins. Since this is language dependant, I don't think it's possible to do in the runtime. As of today's base main this is also a bit cumbersome, but I streamlined the process in #1170. It may be worth it to wait for it to be merged (shouldn't be too far ahead) to find a good solution for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made the refactoring, and added a couple of TODOs that you might want to check for the remaining work. I didn't want to do a larger refactoring of bindings until you get a chance to review/rebase on top.

@OmarTawfik OmarTawfik marked this pull request as ready for review December 13, 2024 19:20
@OmarTawfik OmarTawfik requested review from a team as code owners December 13, 2024 19:20
@OmarTawfik OmarTawfik enabled auto-merge December 13, 2024 19:21
@OmarTawfik OmarTawfik disabled auto-merge December 13, 2024 19:21
Copy link
Contributor

@ggiraldez ggiraldez left a comment

Choose a reason for hiding this comment

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

The important missing thing is adding the built-ins somehow while constructing the CompilationUnit. Looks great other than that, although I left some minor questions/suggestions.

};

let mut binding_graph =
create_with_resolver(self.language_version.clone(), Rc::new(resolver));
Copy link
Contributor

Choose a reason for hiding this comment

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

As mentioned before, there's a missing step here, adding the built-ins to the bindings graph. There are some changes in #1170 to streamline this process, but it's still language dependant (ie. currently only available for Solidity), so it's not possible to introduce that code here. I'm not sure what's a good solution for this problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Will move to extensions.

@@ -0,0 +1,47 @@
// This file is generated automatically by infrastructure scripts. Please don't edit by hand.
Copy link
Contributor

Choose a reason for hiding this comment

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

This file is not automatically generated IIUC. Is this a leftover?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good catch!

@@ -0,0 +1,213 @@
// This file is generated automatically by infrastructure scripts. Please don't edit by hand.
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto, same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed from all files in this directory.

Comment on lines 105 to 107
path: [StringLiteral
@variant ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral])
]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we capturing the string literal variant instead of the StringLiteral? I suspect it's to return terminal nodes, but since we're using cursors it shouldn't make a big difference and I expect users to .unparse() the cursor anyways. Am I missing something?

Copy link
Contributor Author

@OmarTawfik OmarTawfik Dec 16, 2024

Choose a reason for hiding this comment

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

The difference is that unparsing a non-terminal can also return the trivia around it, so it will be confusing for users to deal with leading whitespace in ( "foo.sol").

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah! I see. Thanks for the explanation.

SourceFileNotFound(String),
}

fn extract_import_paths(cursor: Cursor) -> Result<Vec<Cursor>, String> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function's implementation is Solidity specific. Should we extract a SPI and move it to the slang_solidity crate instead of the generator? This is a similar conundrum we have with built-ins and I'd be interested a better solution for them as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved it.

use crate::cst::{Cursor, Query};
use crate::parser::{Parser, ParserInitializationError};

pub struct InternalCompilationBuilder {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be called CompilationBuilder? I assume the Internal prefix is because it's only used for the Typescript interface, but looks like a useful component for Rust users as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

After reading the whole PR I got to the actual CompilationBuilder. I still find the Internal prefix a bit confusing here, but it's more a nit than anything else.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking that we would then expose a similar callback-based API in Rust as well using a new CompilationBuilder that would wrap the InternalCompilationBuilder here. WDYT?
I'm happy to rename it as well if you have a better idea.

.map_err(AddFileError::Internal)?;

let file = File::new(id.clone(), parse_output.tree().clone());
self.files.insert(id, file);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we check if the file was already added?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. We do that check in TS, but wouldn't hurt to add it here as well.

Comment on lines 23 to 27
let built_ins_cursor = transform_built_ins_node(&built_ins_parse_output.tree())
.cursor_with_offset(TextIndex::ZERO);
let built_ins_cursor =
transform_built_ins_node(built_ins_parse_output.tree()).cursor_with_offset(TextIndex::ZERO);

bindings.add_system_file("built_ins.sol", built_ins_cursor);
Ok(bindings)
binding_graph.add_system_file("built_ins.sol", built_ins_cursor);
Ok(binding_graph)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the part that's missing for the Typescript interface or InternalCompilationBuilder. It's simplified in #1170 to just

add_built_ins(&mut bindings, language_version)?;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will move it to the extensions module.

Some(path_to_resolve.to_string())
impl PathResolver<KindTypes> for NoOpResolver {
fn resolve_path(&self, _context_path: &str, path_to_resolve: &Cursor) -> Option<String> {
Some(path_to_resolve.node().unparse())
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is important because the files being tested are unrelated, but for correctness this should remove the quotes surrounding the string literal received.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

And along, a few fixes to the parser and binding APIs (more details in the added changesets).
Copy link
Contributor

@ggiraldez ggiraldez left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for making some of the changes we've discussed in #1170. I'll rebase that once this is merged.

@@ -4,7 +4,7 @@ codegen_language_macros::compile!(Language(
name = Solidity,
documentation_dir = "crates/solidity/inputs/language/docs",
binding_rules_file = "crates/solidity/inputs/language/bindings/rules.msgb",
file_extension = ".sol",
file_extension = ".sol", // TODO: This should be moved to the Solidity-specific 'extensions' sub-module.
Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking it may make sense to put this in the new LanguageFacts struct. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mmm, What is the use case to expose this in the public API?

@OmarTawfik OmarTawfik added this pull request to the merge queue Dec 17, 2024
Merged via the queue into main with commit 7a25d63 Dec 17, 2024
2 checks passed
@OmarTawfik OmarTawfik deleted the compilation-api branch December 17, 2024 16:16
@github-actions github-actions bot mentioned this pull request Dec 17, 2024
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.

2 participants