swiftlang / swift

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

Compiler crash involving associated type loop #74306

Open bjhomer opened 3 weeks ago

bjhomer commented 3 weeks ago

Description

We have two protocols that describe two related types, which have associatedtype declarations that tie them to each other. In the Swift 6.0 compiler in Xcode 16.0 (b1), the compiler crashes when compiling this code. The Swift 5.10 compiler did not crash on this code.

Motivation: In our particular use case, this code is describing two related types: a type that represents the JSON structure of a particular object, and another type that represents the type used to represent this in our local database.

Reproduction

class C {}

public protocol P1 {
    associatedtype PetType: P2 where PetType.OwnerType == Self
}

public protocol P2: C {
    associatedtype OwnerType: P1 where OwnerType.PetType == Self
}

Removing the class constraint (P2: C) or either of the where clauses avoids the crash.

Stack dump

Stack dump:
0.  Program arguments: /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -emit-module -experimental-skip-non-inlinable-function-bodies-without-types /Users/bjhomer/Desktop/AssociatedTypeCrarsh/AssociatedTypeCrarsh/AssociatedTypes.swift /Users/bjhomer/Desktop/AssociatedTypeCrarsh/AssociatedTypeCrarsh/ContentView.swift /Users/bjhomer/Desktop/AssociatedTypeCrarsh/AssociatedTypeCrarsh/AssociatedTypeCrarshApp.swift /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/DerivedSources/GeneratedAssetSymbols.swift -target arm64-apple-ios18.0-simulator -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator18.0.sdk -I /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Products/Debug-iphonesimulator -F /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Products/Debug-iphonesimulator -no-color-diagnostics -enable-testing -g -debug-info-format=dwarf -dwarf-version=5 -module-cache-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -profile-generate -profile-coverage-mapping -swift-version 5 -enforce-exclusivity=checked -Onone -D DEBUG -serialize-debugging-options -enable-experimental-opaque-type-erasure -const-gather-protocols-file /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh_const_extract_protocols.json -enable-experimental-feature DebugDescriptionMacro -enable-bare-slash-regex -empty-abi-descriptor -validate-clang-modules-once -clang-build-session-file /Users/bjhomer/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -Xcc -working-directory -Xcc /Users/bjhomer/Desktop/AssociatedTypeCrarsh -resource-dir /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -file-compilation-dir /Users/bjhomer/Desktop/AssociatedTypeCrarsh -Xcc -ivfsstatcache -Xcc /Users/bjhomer/Library/Developer/Xcode/DerivedData/SDKStatCaches.noindex/iphonesimulator18.0-22A5282g-f46cc61c0d4ec2f57568566c87a94c0a.sdkstatcache -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/AssociatedTypeCrarsh-generated-files.hmap -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/AssociatedTypeCrarsh-own-target-headers.hmap -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/AssociatedTypeCrarsh-all-target-headers.hmap -Xcc -iquote -Xcc /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/AssociatedTypeCrarsh-project-headers.hmap -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Products/Debug-iphonesimulator/include -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/DerivedSources-normal/arm64 -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/DerivedSources/arm64 -Xcc -I/Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/DerivedSources -Xcc -DDEBUG=1 -module-name AssociatedTypeCrarsh -disable-clang-spi -target-sdk-version 18.0 -target-sdk-name iphonesimulator18.0 -external-plugin-path /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/swift-plugin-server -plugin-path /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode-16.0.0-Beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -emit-module-doc-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh.swiftdoc -emit-module-source-info-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh.swiftsourceinfo -emit-objc-header-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh-Swift.h -serialize-diagnostics-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh-master-emit-module.dia -emit-dependencies-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh-master-emit-module.d -o /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh.swiftmodule -emit-abi-descriptor-path /Users/bjhomer/Library/Developer/Xcode/DerivedData/AssociatedTypeCrarsh-ehmjdpwavshuuwdpabdrxvcathkk/Build/Intermediates.noindex/AssociatedTypeCrarsh.build/Debug-iphonesimulator/AssociatedTypeCrarsh.build/Objects-normal/arm64/AssociatedTypeCrarsh.abi.json
1.  Apple Swift version 6.0 (swiftlang-6.0.0.3.300 clang-1600.0.20.10)
2.  Compiling with effective version 5.10
3.  While evaluating request TypeCheckSourceFileRequest(source_file "/Users/bjhomer/Desktop/AssociatedTypeCrarsh/AssociatedTypeCrarsh/AssociatedTypes.swift")
4.  While type-checking 'P1' (at /Users/bjhomer/Desktop/AssociatedTypeCrarsh/AssociatedTypeCrarsh/AssociatedTypes.swift:12:8)
5.  While checking generic signature <τ_0_0 where τ_0_0 : P1>
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x0000000109a4ecd0 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x0000000109a4cf44 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x0000000109a4f2d8 SignalHandler(int) + 352
3  libsystem_platform.dylib 0x0000000194e93584 _sigtramp + 56
4  libsystem_pthread.dylib  0x0000000194e62c20 pthread_kill + 288
5  libsystem_c.dylib        0x0000000194d6fa30 abort + 180
6  swift-frontend           0x0000000105cd75d4 swift::GenericSignature::verify(llvm::ArrayRef<swift::Requirement>) const::$_0::operator()() const + 212
7  swift-frontend           0x0000000105cd7450 swift::GenericSignature::verify(llvm::ArrayRef<swift::Requirement>) const + 8420
8  swift-frontend           0x00000001057b6478 (anonymous namespace)::DeclChecker::visit(swift::Decl*) + 12996
9  swift-frontend           0x00000001057b31a4 swift::TypeChecker::typeCheckDecl(swift::Decl*) + 152
10 swift-frontend           0x00000001058979e4 swift::TypeCheckSourceFileRequest::evaluate(swift::Evaluator&, swift::SourceFile*) const + 660
11 swift-frontend           0x000000010589f098 swift::TypeCheckSourceFileRequest::OutputType swift::Evaluator::getResultUncached<swift::TypeCheckSourceFileRequest, swift::TypeCheckSourceFileRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckSourceFileRequest>(swift::Evaluator&, swift::TypeCheckSourceFileRequest, swift::TypeCheckSourceFileRequest::OutputType)::'lambda'()>(swift::TypeCheckSourceFileRequest const&, swift::TypeCheckSourceFileRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckSourceFileRequest>(swift::Evaluator&, swift::TypeCheckSourceFileRequest, swift::TypeCheckSourceFileRequest::OutputType)::'lambda'()) + 616
12 swift-frontend           0x0000000105897734 swift::performTypeChecking(swift::SourceFile&) + 328
13 swift-frontend           0x00000001047efca8 swift::CompilerInstance::performSema() + 332
14 swift-frontend           0x000000010441d3d0 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1520
15 swift-frontend           0x000000010441b438 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 4752
16 swift-frontend           0x00000001043a12d8 swift::mainEntry(int, char const**) + 2812
17 dyld                     0x0000000194ada0e0 start + 2360
Command SwiftEmitModule failed with a nonzero exit code

Expected behavior

The two protocols should be constrained so types can only conform if they come in pairs.

Environment

Apple Swift version 6.0 (swiftlang-6.0.0.3.300 clang-1600.0.20.10)

Additional information

No response

bjhomer commented 3 weeks ago

I found the following workaround:

public class C {}

public protocol P1 {
    associatedtype PetType: P2 where PetType.OwnerType == Self
}

// ⚠️ Protocol '_P2' should be declared to refine 'P2' due to a same-type constraint on 'Self'
public protocol _P2: AnyObject {
    associatedtype OwnerType: P1 where OwnerType.PetType == Self
}

public protocol P2: _P2, C {}

Declaring _P2 to refine "AnyObject" instead of "C" resolves the conflict. And then P2 refines _P2 by adding an additional constraint for the class we actually care about.

This does produce a warning, but the code appears to compile correctly.

xedin commented 3 weeks ago

cc @slavapestov

slavapestov commented 3 weeks ago
class C {}

public protocol P1 {
    associatedtype PetType: P2 where PetType.OwnerType == Self
}

public protocol P2: C {
    associatedtype OwnerType: P1 where OwnerType.PetType == Self
}

This is actually invalid because it forces P2's Self to be exactly C, and not just a subclass, via the same-type requirement.

However, it shouldn't crash!

slavapestov commented 3 weeks ago

FWIW, in 5.10, this diagnoses:

class C {}

protocol P1 {
    associatedtype PetType: P2 where PetType.OwnerType == Self
}

protocol P2: C {
    associatedtype OwnerType: P1 where OwnerType.PetType == Self
}

class D: C, P2 { // warning: non-final class 'D' cannot safely conform to protocol 'P2', which requires that 'Self.OwnerType.PetType' is exactly equal to 'Self'; this is an error in Swift 6
  typealias OwnerType = O
}

struct O: P1 {
  typealias PetType = D
}
bjhomer commented 3 weeks ago

Thanks for the update.

This is actually invalid because it forces P2's Self to be exactly C, and not just a subclass, via the same-type requirement.

Is it that it's exactly equal to C? Or just that it must a final class to rule out subclasses? I'm not following how the constraints would require it to be exactly C. In your last example, if class D were final, does it still diagnose?