swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.3k stars 10.34k forks source link

Malformed code appears to throw the compiler into an infinite loop #73885

Open gereons opened 3 months ago

gereons commented 3 months ago

Description

Compiling the attached (malformed) code appears to throw the compiler into an infinite loop, no apparent progress is made and no diagnostics are produced. This happens both when using swift build from terminal or within Xcode. I've waited for several minutes on an M1 iMac before hitting Ctrl-C / Cmd-Period while encountering this during a refactoring.

This problem is triggered in Event.test1 when too many properties of the dictionary are used. When the dict has fewer "bad" entries, the compiler immediately emits a helpful diagnostic, otherwise, at least one core is 100% pegged by swift-frontend for about 4 minutes before emitting the the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions diagnostic.

My real production code had 4 or 5 methods that all suffered from this problem, so I would have had to wait for about 20 minutes (with blowing fans) before seeing the diagnostic; this kind of waiting time is unreasonable IMHO, particularly since no apparent progress is visible.

Reproduction

main.swift:

enum Dimension: String {
    case dim1
    case dim2
    case dim3
    case dim4
    case dim5
    case dim6
    case dim7
    case dim8
    case dim9
    case dim10
}

struct Event {
    let properties: [Dimension: String]
    let foo: Int
    let bar: String

    init(properties: [Dimension: String] = [:], foo: Int? = nil) {
        self.init(properties: properties, foo: foo, bar: "bar")
    }

    init(properties: [Dimension: String], foo: Int?, bar: String) {
        self.properties = properties
        self.foo = foo ?? 0
        self.bar = bar
    }
}

struct Foo {
    let bar: Bar?
    let baz: Int?
}

struct Bar {
    let str: String
    let bar: String?
    let baz: Int
    let bad: Double?
    let isBar: Bool?
}

extension Event {
    static func test1(foo: Foo) -> Event {
        .init(properties: [
            .dim1: foo.bar?.bar,
            .dim2: foo.bar?.baz,
            .dim3: foo.bar?.bad,
            .dim4: foo.bar?.isBar,
            .dim5: foo.bar?.bar,
            .dim6: foo.bar?.baz,
            .dim7: foo.bar?.bad,
            // comment out the next 3 lines to get a reasonable compiler error
            .dim8: foo.bar?.isBar,
            .dim9: foo.bar?.bar,
            .dim10: foo.bar.str
            // leaving these lines in appears to throw the compiler into an infinite loop,
            // at least one core is 100% pegged by swift-frontend and no apparent progress
            // is made
        ].compactMapValues { $0 })
    }

    // for funsies, duplicate the test1 method to get even longer compile times.
}

Package.swift:

// swift-tools-version: 5.10
import PackageDescription

let package = Package(
    name: "SlowCompile",
    platforms: [.macOS(.v14)],
    targets: [
        .executableTarget(
            name: "SlowCompile"),
    ]
)

Build either in Xcode or using swift build

Expected behavior

I expected to see the diagnostics regardless of the number of dict entries, and (more importantly) within a reasonable time.

Environment

$ swiftc -version swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4) Target: arm64-apple-macosx14.0 // as installed by Xcode 15.4 (15F31d)

Additional information

No response

tbkka commented 3 months ago

CC: @xedin @hborla