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

Implement type normalization #7

Open
6 tasks
stackotter opened this issue Oct 10, 2023 · 2 comments · May be fixed by #15
Open
6 tasks

Implement type normalization #7

stackotter opened this issue Oct 10, 2023 · 2 comments · May be fixed by #15
Labels
enhancement New feature or request hacktoberfest

Comments

@stackotter
Copy link
Owner

stackotter commented Oct 10, 2023

A major pain point for many Macro developers is handling all of the different ways that users can write types. For example, Void? is equivalent to Optional<Void> which is equivalent to (((((()))?))); what a nightmare!

If you're going to complete this issue you may need a good understanding of Swift itself, but the issue should still be relatively approachable for anyone new to Swift Macros.

This task contains four main steps; I've tried to give enough detail for someone to jump in and complete this without looking around the codebase for too long (specifically for Hacktoberfest!)

  • Create a new NormalizedType enum with the smallest possible number of cases to describe all types in Swift. See below for a rough idea of what this might look like.
  • Create some new structs to go along with the new NormalizedType enum: e.g. NormalizedTupleType should be a copy of TupleType except with the element types being NormalizedType instead of Type. This will create some duplication, but I think it's probably the best solution in the long run (as it provides a compile-time guarantee that a type and all subtypes within it have been normalized).
  • Add a func normalized() -> NormalizedType method to Type (and implement accordingly)
  • And to top things off, update Type's normalizedDescription computed property to use Type's normalized method under the hood instead of the hacky normalization it currently performs.

The following steps would likely improve Quality of Life (but could be completed separately);

  • Add a func normalized() -> NormalizedType method in an extension of TypeProtocol which first converts the specific type to a Type and then normalizes it using the previously implemented Type.normalized method.
  • Create a NormalizedTypeProtocol (by copying TypeProtocol and modifying where applicable) with an additional method or computed property to convert a normalized type back to an instance of the Type enum.

NormalizedType (a rough plan)

enum NormalizedType {
    /// A composition of two types (e.g. `Encodable & Decodable`). Used to
    /// combine protocol requirements.
    case composition(NormalizedCompositionType)
    /// A some or any protocol type (e.g. `any T` or `some T`).
    case someOrAny(NormalizedSomeOrAnyType)
    /// A function type (e.g. `() -> ()`).
    case function(NormalizedFunctionType)
    /// An implicitly unwrapped optional type (e.g. `Int!`).
    case implicitlyUnwrappedOptional(NormalizedImplicitlyUnwrappedOptionalType)
    /// A member type (e.g. `Array<Int>.Element`).
    case member(NormalizedMemberType)
    /// A pack expansion type (e.g. `repeat each V`).
    case packExpansion(NormalizedPackExpansionType)
    /// A pack reference type (e.g. `each V`).
    case packReference(NormalizedPackReferenceType)
    /// A simple type (e.g. `Int` or `Box<Int>`).
    case simple(NormalizedSimpleType)
    /// A suppressed type in a conformance position (e.g. `~Copyable`).
    case suppressed(NormalizedSuppressedType)
    //// A tuple type (e.g. `(Int, String)`).
    case tuple(NormalizedTupleType)
}

Converting Type to NormalizedType as simply as possible will tackle most forms of normalization. However, there are another two normalization patterns that need to be considered and aren't encoded in this new type: () gets replaced by Void (a NormalizedSimpleType), and (_) gets replaced by _ (where _ is some arbitrary type).

@stackotter stackotter added hacktoberfest enhancement New feature or request labels Oct 10, 2023
@alessionossa
Copy link
Contributor

I'm trying to implement this, with good results already, however I am not sure what classRestriction, metatype and missing cases of Type should be mapped to in NormalizedType.

@stackotter
Copy link
Owner Author

Awesome! I reckon classRestriction should map to the nominal type AnyObject (iirc it’s equivalent to that but I may be mistaken), metatype should be mapped to the member type X.Type (where X is the type that the metatype is of), and missing is a bit annoying. For missing it may turn out that macros are never invoked on malformed syntax (the only way that missing can appear in the AST afaik), in which case we could potentially fatalError on missing earlier on (when initialising a Type) since it’s meant to be unreachable; but for now maybe just add a missing case to NormalizedType and I’ll deal with that issue later.

@alessionossa alessionossa linked a pull request Apr 7, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request hacktoberfest
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants