-
Notifications
You must be signed in to change notification settings - Fork 188
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 basic support for frozen structs projections #2560
Add basic support for frozen structs projections #2560
Conversation
This commit introduces Swift metadata types and implements basic support for frozen structs.
This commit refactors the tooling, including the type database and declarations, to manage recursive dependencies. It introduces a type registrar system for mapping between Swift and C#. Additionally, it updates the parser and emitter to interact with the type database and registrar, and implement basic type filtering. Tests are added for non-generic nested frozen structs with primitive properties.
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.
One thing you're going to run into in your code is that you don't have a distinction between a type declaration and a type specification. This will cause you problems later on.
Here's what the distinction is:
a type declaration is a nominal type in swift. It is anything that has a name and its public facing API. This includes top-level functions, structs, enums, classes, and actors.
A type specification is the annotation that is used to specify the type of a parameter, an associated type, a type alias target, or the return type of a method. In swift, a type specification can include nominal types (fully or partially specified), tuples, closure types, and protocol list types. In addition, parts of these types may include syntactic sugars such as option, implicitly unwrapped options, and meta information such as attributes. All of those elements need to be handled correctly.
Indeed, the representation of these types is, unto itself, a little language.
I strongly recommend that you take TypeSpecParser
, TypeSpecTokenizer
, TypeSpecToken
, and `TypeSpec (and its subclasses). This represents a very compact recursive descent parser than can handle whatever the swift compiler can generate.
I've converted this PR to draft as we plan to have additional iterations before another review. @stephen-hawley thank you for your contribution. Here are some thoughts:
Let's integrate your changes and add comments, documentation, and more test cases. |
Let's add them in a follow-up PR as this one is already ~+5k loc. What is the difference between |
This commit introduces the Conductor class, which is responsible for finding appropriate declaration handler factory. If a handler can handle a declaration, the factory creates a handler. Handlers implement the IHandler interface, which defines two methods: marshal and emit. The marshal method should collect all data required for emitting and return an environment, which is used for emitting. The emit function generates code based on the environment.
/// <param name="decl">The base declaration.</param> | ||
public bool Handles(BaseDecl decl) | ||
{ | ||
return decl is TypeDecl && decl is ClassDecl; |
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.
decl is TypeDecl &&
isn't needed. If the type is a ClassDecl
it is always a TypeDecl
/// <param name="decl">The base declaration.</param> | ||
public bool Handles(BaseDecl decl) | ||
{ | ||
return decl is TypeDecl && decl is StructDecl; |
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.
decl is TypeDecl &&
isn't needed.
} | ||
writer.WriteLine(); | ||
|
||
foreach (BaseDecl baseDecl in structDecl.Declarations) |
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.
This should be done through the conductor, otherwise we will have identical code for classes, structs, enums, and actors when it could be in one place.
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 created BaseHandler
that implements HandleBaseDecl
which calls into Conductor
. Do you have any better ideas 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.
How about putting an EmitInnerTypes
method into BaseHandler
which looks like:
protected EmitInnerTypes(IEnumerable<TypeDecl> types, IndentedTextWriter writer, Conductor conductor, TypeDatabase typeDatabase) {
writer.Indent++;
foreach (var type in types) {
HandleBaseDecl(writer, baseDecl, conductor, typeDatabase);
}
writer.Indent--;
}
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.
Moved the loop to the HandleBaseDecl
. I didn't change the name of the method as it handles other declarations as well. We can rename it later.
Merging this PR. Any additional feedback will be addressed in follow-up work. |
Description
This PR implements basic support for frozen structs. It refactors the tooling, including the type database and declarations, to manage recursive dependencies. It introduces Swift metadata projections and type registrar system for mapping between Swift and C#. Additionally, it updates the parser and emitter to interact with the type database and registrar, and implement basic type filtering. Tests are added for non-generic nested frozen structs with primitive properties.