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

Nested declaration macro diagnoses generic as incorrect type #69454

Open
stephencelis opened this issue Oct 27, 2023 · 0 comments
Open

Nested declaration macro diagnoses generic as incorrect type #69454

stephencelis opened this issue Oct 27, 2023 · 0 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels

Comments

@stephencelis
Copy link
Contributor

Description

I encountered very bizarre behavior when trying to create a declaration macro that takes a generic closure. The closure seems to be incorrectly identifying one generic type as another.

Steps to reproduce

Download the following project and run the DeclarationMacroBugClient target:

DeclarationMacroBug.zip

image

You'll see that the closure is evaluating a parameter as if it is the wrong generic type.

Source

Macro plugin source:

import SwiftCompilerPlugin
import SwiftDiagnostics
import SwiftOperators
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public enum ReduceMacro: DeclarationMacro {
  public static func expansion<N: FreestandingMacroExpansionSyntax, C: MacroExpansionContext>(
    of node: N,
    in context: C
  ) throws -> [DeclSyntax] {
    guard let trailingClosure = node.trailingClosure
    else {
      fatalError()
    }
    return [
      """
      let body = Reduce<State, Action> \(trailingClosure)
      """
    ]
  }
}

@main
struct MacrosPlugin: CompilerPlugin {
  let providingMacros: [Macro.Type] = [
    ReduceMacro.self,
  ]
}

Library source:

public struct Reduce<State, Action> {
  public let reducer: (inout State, Action) -> Effect<Action>

  public init(_ reducer: @escaping (inout State, Action) -> Effect<Action>) {
    self.reducer = reducer
  }
}

public struct Effect<Action> {
  public static var none: Self { Self() }
}

@freestanding(declaration, names: named(body))
public macro Reduce<State, Action>(
  _ reducer: (inout State, Action) -> Effect<Action>
) = #externalMacro(
  module: "DeclarationMacroBugMacros", type: "ReduceMacro"
)

Client source:

import DeclarationMacroBug

struct State {
  var count = 0
}

enum Action {
  case decrementButtonTapped
  case incrementButtonTapped
}

struct Feature {
  #Reduce<State, Action> { state, action in
    switch action {
    case .decrementButtonTapped:
      state.count -= 1
      return .none
    case .incrementButtonTapped:
      state.count += 1
      return .none
    }
  }
}

Expected behavior

I expect action to be treated as an Action, not State.

Environment

  • Swift compiler version info
    swift-driver version: 1.87.2 Apple Swift version 5.9 (swiftlang-5.9.2.1.6     clang-1500.1.0.1.1)
    Target: arm64-apple-macosx14.0
    
  • Xcode version info
    Xcode 15.1
    Build version 15C5028h
    
@stephencelis stephencelis added bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels labels Oct 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels
Projects
None yet
Development

No branches or pull requests

1 participant