Skip to content

Fix output type mismatch with RegexBuilder #626

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

Merged
merged 10 commits into from
Feb 9, 2023
6 changes: 5 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ let availabilityDefinition = PackageDescription.SwiftSetting.unsafeFlags([
"-define-availability",
"-Xfrontend",
"SwiftStdlib 5.7:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999",
"-Xfrontend",
"-define-availability",
"-Xfrontend",
"SwiftStdlib 5.8:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999",
])

/// Swift settings for building a private stdlib-like module that is to be used
Expand Down Expand Up @@ -87,7 +91,7 @@ let package = Package(
name: "RegexBuilderTests",
dependencies: ["_StringProcessing", "RegexBuilder", "TestSupport"],
swiftSettings: [
.unsafeFlags(["-Xfrontend", "-disable-availability-checking"])
availabilityDefinition
]),
.testTarget(
name: "DocumentationTests",
Expand Down
62 changes: 62 additions & 0 deletions Sources/RegexBuilder/DSL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,65 @@ extension Regex.Match {
internal func makeFactory() -> _RegexFactory {
_RegexFactory()
}

/// These are special `accumulate` methods that wrap one or both components in
/// a node that indicates that that their output types shouldn't be included in
/// the resulting strongly-typed output type. This is required from a
/// `buildPartialBlock` call where a component's output type is either ignored
/// or not included in the resulting type. For example:
///
/// static func buildPartialBlock<W0, W1, C1, R0: RegexComponent, R1: RegexComponent>(
/// accumulated: R0, next: R1
/// ) -> Regex<(Substring, C1)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1)
///
/// In this `buildPartialBlock` overload, `W0` isn't included in the
/// resulting output type, even though it can match any output type, including
/// a tuple. When `W0` matches a tuple type that doesn't match another overload
/// (because of arity or labels) we need this "ignoring" variant so that we
/// don't have a type mismatch when we ultimately cast the type-erased output
/// to the expected type.
@available(SwiftStdlib 5.7, *)
extension _RegexFactory {
/// Concatenates the `left` and `right` component, wrapping `right` to
/// indicate that its output type shouldn't be included in the resulting
/// strongly-typed output type.
@_alwaysEmitIntoClient
internal func accumulate<Output>(
_ left: some RegexComponent,
ignoringOutputTypeOf right: some RegexComponent
) -> Regex<Output> {
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
return accumulate(left, ignoreCapturesInTypedOutput(right))
}
return accumulate(left, right)
}

/// Concatenates the `left` and `right` component, wrapping `left` to
/// indicate that its output type shouldn't be included in the resulting
/// strongly-typed output type.
@_alwaysEmitIntoClient
internal func accumulate<Output>(
ignoringOutputTypeOf left: some RegexComponent,
_ right: some RegexComponent
) -> Regex<Output> {
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
return accumulate(ignoreCapturesInTypedOutput(left), right)
}
return accumulate(left, right)
}

/// Concatenates the `left` and `right` component, wrapping both sides to
/// indicate that their output types shouldn't be included in the resulting
/// strongly-typed output type.
@_alwaysEmitIntoClient
internal func accumulate<Output>(
ignoringOutputTypeOf left: some RegexComponent,
andAlso right: some RegexComponent
) -> Regex<Output> {
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
return accumulate(
ignoreCapturesInTypedOutput(left), ignoreCapturesInTypedOutput(right))
}
return accumulate(left, right)
}
}
59 changes: 22 additions & 37 deletions Sources/RegexBuilder/Variadics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
// Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand All @@ -20,7 +20,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -30,7 +30,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -40,7 +40,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -50,7 +50,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -60,7 +60,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4, C5)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4, C5) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -70,7 +70,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4, C5, C6)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4, C5, C6) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -80,7 +80,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4, C5, C6, C7)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4, C5, C6, C7) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -90,7 +90,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4, C5, C6, C7, C8)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4, C5, C6, C7, C8) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -100,7 +100,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4, C5, C6, C7, C8, C9) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand All @@ -110,7 +110,7 @@ extension RegexComponentBuilder {
accumulated: R0, next: R1
) -> Regex<(Substring, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10)> where R0.RegexOutput == W0, R1.RegexOutput == (W1, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)
}
}
@available(SwiftStdlib 5.7, *)
Expand Down Expand Up @@ -565,123 +565,112 @@ extension RegexComponentBuilder {
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<Substring> where R0.RegexOutput == W0 {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(ignoringOutputTypeOf: accumulated, andAlso: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0)> where R0.RegexOutput == (W0, C0) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1)> where R0.RegexOutput == (W0, C0, C1) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2)> where R0.RegexOutput == (W0, C0, C1, C2) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3)> where R0.RegexOutput == (W0, C0, C1, C2, C3) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, C4, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3, C4)> where R0.RegexOutput == (W0, C0, C1, C2, C3, C4) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, C4, C5, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3, C4, C5)> where R0.RegexOutput == (W0, C0, C1, C2, C3, C4, C5) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, C4, C5, C6, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3, C4, C5, C6)> where R0.RegexOutput == (W0, C0, C1, C2, C3, C4, C5, C6) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, C4, C5, C6, C7, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3, C4, C5, C6, C7)> where R0.RegexOutput == (W0, C0, C1, C2, C3, C4, C5, C6, C7) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, C4, C5, C6, C7, C8, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3, C4, C5, C6, C7, C8)> where R0.RegexOutput == (W0, C0, C1, C2, C3, C4, C5, C6, C7, C8) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}
@available(SwiftStdlib 5.7, *)
extension RegexComponentBuilder {
@available(SwiftStdlib 5.7, *)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, R0: RegexComponent, R1: RegexComponent>(
accumulated: R0, next: R1
) -> Regex<(Substring, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)> where R0.RegexOutput == (W0, C0, C1, C2, C3, C4, C5, C6, C7, C8, C9) {
let factory = makeFactory()
return factory.accumulate(accumulated, next)
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)
}
}

Expand Down Expand Up @@ -6884,7 +6873,3 @@ extension TryCapture {
self.init(factory.captureOptional(componentBuilder(), reference._raw, transform))
}
}



// END AUTO-GENERATED CONTENT
31 changes: 28 additions & 3 deletions Sources/VariadicsGenerator/VariadicsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ struct VariadicsGenerator: ParsableCommand {
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
// Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -262,7 +262,20 @@ struct VariadicsGenerator: ParsableCommand {
accumulated: R0, next: R1
) -> \(regexTypeName)<\(matchType)> \(whereClause) {
let factory = makeFactory()

""")
if leftArity == 0 {
output("""
return factory.accumulate(ignoringOutputTypeOf: accumulated, next)

""")
} else {
output("""
return factory.accumulate(accumulated, next)

""")
}
output("""
}
}

Expand All @@ -274,7 +287,6 @@ struct VariadicsGenerator: ParsableCommand {
output("""
\(defaultAvailableAttr)
extension \(concatBuilderName) {
\(defaultAvailableAttr)
@_alwaysEmitIntoClient
public static func buildPartialBlock<W0
""")
Expand Down Expand Up @@ -308,7 +320,20 @@ struct VariadicsGenerator: ParsableCommand {
output("""
{
let factory = makeFactory()
return factory.accumulate(accumulated, next)

""")
if leftArity == 0 {
output("""
return factory.accumulate(ignoringOutputTypeOf: accumulated, andAlso: next)

""")
} else {
output("""
return factory.accumulate(accumulated, ignoringOutputTypeOf: next)

""")
}
output("""
}
}

Expand Down
Loading