swiftlang / swift

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

Calling NSDecimalString function crashes the Swift compiler (also FB14697842) #75752

Open flockoffiles opened 2 months ago

flockoffiles commented 2 months ago

Description

The following code that calls NSDecimalString crashes the compiler with the message:

SILFunction type mismatch for 'NSDecimalString': '$@convention(c) (UnsafePointer, Optional) -> @autoreleased Optional' != '$@convention(c) (UnsafePointer, Optional) -> @autoreleased NSString'

Reproduction


import Foundation
var tmpDecimal: Decimal = 5.5
let decimalString = NSDecimalString(&tmpDecimal, Locale.current)

Stack dump

Stack dump:
0.  Program arguments: /Applications/Xcode-beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -c /Users/gl68pl/Developer/Temp/DecimalStringBug/DecimalStringBug/ViewController.swift -primary-file /Users/gl68pl/Developer/Temp/DecimalStringBug/DecimalStringBug/AppDelegate.swift /Users/gl68pl/Developer/Temp/DecimalStringBug/DecimalStringBug/SceneDelegate.swift /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DerivedSources/GeneratedAssetSymbols.swift -emit-dependencies-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/AppDelegate.d -emit-const-values-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/AppDelegate.swiftconstvalues -emit-reference-dependencies-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/AppDelegate.swiftdeps -serialize-diagnostics-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/AppDelegate.dia -emit-localized-strings -emit-localized-strings-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64 -target arm64-apple-ios18.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -stack-check -sdk /Applications/Xcode-beta5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk -I /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Products/Debug-iphoneos -F /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Products/Debug-iphoneos -no-color-diagnostics -enable-testing -g -debug-info-format=dwarf -dwarf-version=5 -module-cache-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/ModuleCache.noindex -swift-version 5 -enforce-exclusivity=checked -Onone -D DEBUG -serialize-debugging-options -const-gather-protocols-file /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/DecimalStringBug_const_extract_protocols.json -enable-experimental-feature DebugDescriptionMacro -enable-experimental-feature OpaqueTypeErasure -enable-bare-slash-regex -empty-abi-descriptor -validate-clang-modules-once -clang-build-session-file /Users/gl68pl/Library/Developer/Xcode/DerivedData/ModuleCache.noindex/Session.modulevalidation -Xcc -working-directory -Xcc /Users/gl68pl/Developer/Temp/DecimalStringBug -resource-dir /Applications/Xcode-beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -enable-anonymous-context-mangled-names -file-compilation-dir /Users/gl68pl/Developer/Temp/DecimalStringBug -Xcc -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG -Xcc -ivfsstatcache -Xcc /Users/gl68pl/Library/Developer/Xcode/DerivedData/SDKStatCaches.noindex/iphoneos18.0-22A5326g-db8e1de5f26549a68b1cfeeb5c021498.sdkstatcache -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/swift-overrides.hmap -Xcc -iquote -Xcc /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DecimalStringBug-generated-files.hmap -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DecimalStringBug-own-target-headers.hmap -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DecimalStringBug-all-target-headers.hmap -Xcc -iquote -Xcc /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DecimalStringBug-project-headers.hmap -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Products/Debug-iphoneos/include -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DerivedSources-normal/arm64 -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DerivedSources/arm64 -Xcc -I/Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/DerivedSources -Xcc -DDEBUG=1 -module-name DecimalStringBug -frontend-parseable-output -disable-clang-spi -target-sdk-version 18.0 -target-sdk-name iphoneos18.0 -external-plugin-path /Applications/Xcode-beta5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode-beta5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode-beta5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode-beta5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/swift-plugin-server -plugin-path /Applications/Xcode-beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Applications/Xcode-beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/local/lib/swift/host/plugins -o /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Build/Intermediates.noindex/DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/AppDelegate.o -index-unit-output-path /DecimalStringBug.build/Debug-iphoneos/DecimalStringBug.build/Objects-normal/arm64/AppDelegate.o -index-store-path /Users/gl68pl/Library/Developer/Xcode/DerivedData/DecimalStringBug-aauwvbqieqteiaeknfmiqmgohuwq/Index.noindex/DataStore -index-system-modules
1.  Apple Swift version 6.0 (swiftlang-6.0.0.7.6 clang-1600.0.24.1)
2.  Compiling with effective version 5.10
3.  While evaluating request ExecuteSILPipelineRequest(Run pipelines { Mandatory Diagnostic Passes + Enabling Optimization Passes } on SIL for DecimalStringBug)
4.  While running pass #847 SILModuleTransform "MandatorySILLinker".
5.  While deserializing SIL function "NSDecimalString"
6.  *** DESERIALIZATION FAILURE ***
*** If any module named here was modified in the SDK, please delete the ***
*** new swiftmodule files from the SDK and keep only swiftinterfaces.   ***
module 'Foundation', builder version '6.0(5.10)/Apple Swift version 6.0 (swiftlang-6.0.0.7.6 clang-1600.0.24.1)', built from swiftinterface, resilient, loaded from '/Applications/Xcode-beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos/prebuilt-modules/18.0/Foundation.swiftmodule/arm64-apple-ios.swiftmodule'
SILFunction type mismatch for 'NSDecimalString': '$@convention(c) (UnsafePointer<Decimal>, Optional<AnyObject>) -> @autoreleased Optional<NSString>' != '$@convention(c) (UnsafePointer<Decimal>, Optional<AnyObject>) -> @autoreleased NSString'

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           0x0000000105b6d194 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x0000000105b6b3e8 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x0000000105b6d760 SignalHandler(int) + 292
3  libsystem_platform.dylib 0x000000018b612584 _sigtramp + 56
4  libsystem_pthread.dylib  0x000000018b5e1c20 pthread_kill + 288
5  libsystem_c.dylib        0x000000018b4eea30 abort + 180
6  swift-frontend           0x0000000100c9b658 swift::ModuleFileSharedCore::outputDiagnosticInfo(llvm::raw_ostream&) const + 0
7  swift-frontend           0x0000000100c2fab4 swift::ModuleFile::getSourceLoc() const + 0
8  swift-frontend           0x0000000100c77ce8 swift::SILDeserializer::readSILFunctionChecked(llvm::PointerEmbeddedInt<unsigned int, 31>, swift::SILFunction*, llvm::StringRef, bool, bool) + 1916
9  swift-frontend           0x0000000100c869d8 swift::SILDeserializer::lookupSILFunction(swift::SILFunction*, bool) + 452
10 swift-frontend           0x0000000100d48c24 swift::SILLinkerVisitor::maybeAddFunctionToWorklist(swift::SILFunction*, swift::SerializedKind_t) + 360
11 swift-frontend           0x0000000100d48e44 swift::SILLinkerVisitor::process() + 476
12 swift-frontend           0x0000000100dc3d0c swift::SILModule::linkFunction(swift::SILFunction*, swift::SILModule::LinkingMode) + 120
13 swift-frontend           0x000000010147f8f0 (anonymous namespace)::SILLinker::run() + 64
14 swift-frontend           0x0000000101357a4c swift::SILPassManager::executePassPipelinePlan(swift::SILPassPipelinePlan const&) + 13436
15 swift-frontend           0x00000001013909dc swift::SimpleRequest<swift::ExecuteSILPipelineRequest, std::__1::tuple<> (swift::SILPipelineExecutionDescriptor), (swift::RequestFlags)1>::evaluateRequest(swift::ExecuteSILPipelineRequest const&, swift::Evaluator&) + 56
16 swift-frontend           0x0000000101374340 swift::ExecuteSILPipelineRequest::OutputType swift::Evaluator::getResultUncached<swift::ExecuteSILPipelineRequest, swift::ExecuteSILPipelineRequest::OutputType swift::evaluateOrFatal<swift::ExecuteSILPipelineRequest>(swift::Evaluator&, swift::ExecuteSILPipelineRequest)::'lambda'()>(swift::ExecuteSILPipelineRequest const&, swift::ExecuteSILPipelineRequest::OutputType swift::evaluateOrFatal<swift::ExecuteSILPipelineRequest>(swift::Evaluator&, swift::ExecuteSILPipelineRequest)::'lambda'()) + 412
17 swift-frontend           0x0000000101376abc swift::runSILDiagnosticPasses(swift::SILModule&) + 612
18 swift-frontend           0x00000001008f42cc swift::CompilerInstance::performSILProcessing(swift::SILModule*) + 84
19 swift-frontend           0x0000000100520268 performCompileStepsPostSILGen(swift::CompilerInstance&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>>, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::PrimarySpecificPaths const&, int&, swift::FrontendObserver*) + 1376
20 swift-frontend           0x000000010051f474 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 984
21 swift-frontend           0x0000000100522728 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1680
22 swift-frontend           0x0000000100521458 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 3572
23 swift-frontend           0x00000001004a850c swift::mainEntry(int, char const**) + 3680
24 dyld                     0x000000018b257154 start + 2476
Command SwiftCompile failed with a nonzero exit code

Expected behavior

I expect it compile without a crash.

Environment

swift-driver version: 1.113 Apple Swift version 6.0 (swiftlang-6.0.0.7.6 clang-1600.0.24.1) Target: arm64-apple-macosx14.0

Additional information

DecimalStringBug.zip

aehlke commented 2 months ago

Do you have a workaround?

flockoffiles commented 2 months ago

The only workaround I know is going back to Xcode 16 beta 4 (and praying that this issue gets fixed soon).

Cedrick84 commented 2 months ago

Experiencing the same issue.

flockoffiles commented 2 months ago

ok. There seems to be a work-around. It's quite verbose, but it works:

    let decimalNumber = NSDecimalNumber(decimal: tmpDecimal)
    let numberFormatter = NumberFormatter()
    numberFormatter.numberStyle = .decimal
    numberFormatter.locale = locale
    let formattedString = numberFormatter.string(from: decimalNumber) ?? "\(tmpDecimal)"
alvesmarcel commented 1 month ago

EDIT: Please see @robertmryan's comment below before choosing to use the approach I suggested here. Decimal.FormatStyle behaves differently from NumberFormatter in some scenarios.


It seems this is a known issue on Xcode 16.

According to the release notes, the suggested workaround is to use Decimal.FormatStyle. If you're supporting iOS 15.0 and above, you can do something like this too:

let decimalNumber: Decimal = 12345.67
let formatStyle = Decimal.FormatStyle(locale: locale)
let formattedNumber = decimalNumber.formatted(formatStyle)
let formattedNumberStr = String(formattedNumber)

The main advantage of this approach is that you don't have to deal with optionals.

robertmryan commented 1 month ago

I agree that for now we can just avoid using NSDecimalString, but, FWIW, the error message tells us what the problem is: The Swift interface indicates that the result is not optional, as indicated in the docs, but clearly elsewhere behind the scenes, it is being inferred as optional.

robertmryan commented 1 month ago

@alvesmarcel

It seems this is a known issue on Xcode 16.

According to the release notes, the suggested workaround is to use Decimal.FormatStyle. If you're supporting iOS 15.0 and above, you can do something like this too:

let decimalNumber: Decimal = 12345.67
let formatStyle = Decimal.FormatStyle(locale: locale)
let formattedNumber = decimalNumber.formatted(formatStyle)
let formattedNumberStr = String(formattedNumber)

The main advantage of this approach is that you don't have to deal with optionals.

I would be cautious with this: If the locale is your current locale, this will use the default numeric format for that locale, but it will ignore whatever custom number formatting you may have changed in settings. NumberFormatter will honor the device settings, but Decimal.FormatStyle will not. If you’re trying to generate some invariant style (a la en_US_POSIX), this doesn’t matter. But if trying to localize in a manner to honor the device settings, this is problematic.

aehlke commented 1 month ago

@robertmryan that solution has merged so you might want to copy your feedback to that PR or share an alternative

robertmryan commented 1 month ago

@aehlke

Are you talking about [swift5] Fix Xcode 16 compilation crash with Extensions.swift generation #19564? All those changes were using Locale(identifier: "en_US"). (Personally, I would have advised Locale(identifier: "en_US_POSIX"), but that distinction is subtle and beyond the scope of this conversation.) Using a fixed locale is fine. The limitations of formatted(_:) with Decimal.FormatStyle with locale of .current (or, worse, formatted()) only manifest themselves in those cases where one might reasonably assume that it would honor not only the device locale (which it will), but also any override settings of the “number format” in “Settings” (which it doesn't).

Or is there some other PR that you are referencing?

Knapiii commented 1 month ago

Got the same issue on 16.0 RC I have some SPM created by OpenAPI for our app, and the SDKs created there uses NSDecimalString. So I can't manually change these

Can I go back to a 16.0 beta where this is not an issue?

https://github.com/OpenAPITools/openapi-generator/issues/19459

alvesmarcel commented 1 month ago

@alvesmarcel

It seems this is a known issue on Xcode 16. According to the release notes, the suggested workaround is to use Decimal.FormatStyle. If you're supporting iOS 15.0 and above, you can do something like this too:

let decimalNumber: Decimal = 12345.67
let formatStyle = Decimal.FormatStyle(locale: locale)
let formattedNumber = decimalNumber.formatted(formatStyle)
let formattedNumberStr = String(formattedNumber)

The main advantage of this approach is that you don't have to deal with optionals.

I would be cautious with this: If the locale is your current locale, this will use the default numeric format for that locale, but it will ignore whatever custom number formatting you may have changed in settings. NumberFormatter will honor the device settings, but Decimal.FormatStyle will not. If you’re trying to generate some invariant style (a la en_US_POSIX), this doesn’t matter. But if trying to localize in a manner to honor the device settings, this is problematic.

Thanks for pointing that out, @robertmryan! I wasn’t aware of that important difference in behavior. Do you know if there’s a way for Decimal.FormatStyle to behave like NumberFormatter? I’m asking because the documentation suggests Decimal.FormatStyle as an alternative to NumberFormatter, so I’m wondering if I’m missing something in my implementation.

robertmryan commented 1 month ago

Thanks for pointing that out, @robertmryan! I wasn’t aware of that important difference in behavior. Do you know if there’s a way for Decimal.FormatStyle to behave like NumberFormatter? I’m asking because the documentation suggests Decimal.FormatStyle as an alternative to NumberFormatter, so I’m wondering if I’m missing something in my implementation.

@alvesmarcel – No, I don’t. So, when trying to format for the end-user’s preferred format, I stick with NumberFormatter. I only use this formatted function when dealing with fixed locales, like en_US_POSIX.

To make using NumberFormatter a little more natural with string interpolation, I personally extend default string interpolation behavior to support NumberFormatter:

extension String.StringInterpolation {
    mutating func appendInterpolation<T: Numeric>(_ value: T, using formatter: NumberFormatter) {
        if let formattedValue = formatter.string(for: value) {
            appendLiteral(formattedValue)
        }
    }
}

Then I can use NumberFormatter in my string interpolation:

let string = "\(value, using: formatter)")

FWIW, with floating points, you can use the specifier alternative and that seems to localize properly:

let string = "\(value, specifier: "%.2f")"

But that does not work with Decimal, hence my NumberFormatter approach outlined above.