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

[SR-15082] Forced checked cast runtime crash on optional<T> to T in array element position #57408

Closed
LucianoPAlmeida opened this issue Aug 17, 2021 · 5 comments

Comments

@LucianoPAlmeida
Copy link
Collaborator

@LucianoPAlmeida LucianoPAlmeida commented Aug 17, 2021

Previous ID SR-15082
Radar None
Original Reporter @LucianoPAlmeida
Type Bug
Status Closed
Resolution Invalid
Environment

Xcode 13 Beta 4

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, RunTimeCrash
Assignee @tbkka
Priority Medium

md5: 42e65820f8dec73893bd2ccaf5ad7ce4

relates to:

  • SR-15038 Incorrect "conditional downcast from ... does nothing" diagnostic

Issue Description:

As discussed in SR-15038 when using an array with an optional element of type X forced checked cast to an array of element of unwrapped X causes a runtime crash. Also as discussed there is not clear if that is something that shouldn't be allowed by the type checker and fail to compile or is really the runtime that should handle this.

protocol ExperimentDeserializable {
    static func deserializeExperiment(_ value: Any) -> Self?
}

extension String: ExperimentDeserializable {
    static func deserializeExperiment(_ value: Any) -> String? { value as? String }
}

extension Int: ExperimentDeserializable {
    static func deserializeExperiment(_ value: Any) -> Int? { value as? Int }
}

class Constant<T> {
    private init(getUnderlyingValue: @escaping () -> T) {
        print(getUnderlyingValue())
    }
}

extension Constant where T: Sequence, T.Element: ExperimentDeserializable {
    static func foo<U>(thing: Thing, defaultValue: T) -> T where T == [U] {
        guard let array = thing.storage["foo"] as? [Any] else {
            fatalError()
        }

        let value = array.map(T.Element.deserializeExperiment) as! [T.Element] // runtime crash
        return value
    }
}

struct Thing {
    let storage: [String: Any]
}

let thing = Thing(storage: ["foo": ["x", 1]])
let result = Constant.foo(thing: thing, defaultValue: ["a", "b"])
assert(result == ["a", "b"])

Runtime crash logs reported by @jpsim

Could not cast value of type 'Swift.Optional<Swift.String>' (0x7fff8170fd50) to 'Swift.String' (0x7fff81709120).
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project and the crash backtrace.
Stack dump:
0.  Program arguments: /Applications/Xcode-13.0.0-Beta.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend -frontend -interpret file.swift -enable-objc-interop -stack-check -sdk /Applications/Xcode-13.0.0-Beta.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk -color-diagnostics -new-driver-path /Applications/Xcode-13.0.0-Beta.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-driver -resource-dir /Applications/Xcode-13.0.0-Beta.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift -module-name file -target-sdk-version 12.0.0
1.  Apple Swift version 5.5 (swiftlang-1300.0.27.6 clang-1300.0.27.2)
2.  
3.  While running user code "file.swift"
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           0x0000000113ba0187 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 39
1  swift-frontend           0x0000000113b9f118 llvm::sys::RunSignalHandlers() + 248
2  swift-frontend           0x0000000113ba0796 SignalHandler(int) + 278
3  libsystem_platform.dylib 0x00007fff2056fd7d _sigtramp + 29
4  libsystem_malloc.dylib   0x00007fff2036bff4 _malloc_zone_calloc + 59
5  libsystem_c.dylib        0x00007fff2047f406 abort + 125
6  libswiftCore.dylib       0x00007fff2cbf37b5 swift::fatalError(unsigned int, char const*, ...) + 149
7  libswiftCore.dylib       0x00007fff2cbebb27 swift::swift_dynamicCastFailure(void const*, char const*, void const*, char const*, char const*) + 71
8  libswiftCore.dylib       0x00007fff2cbebb9a swift::swift_dynamicCastFailure(swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*, char const*) + 106
9  libswiftCore.dylib       0x00007fff2cbefecb swift_dynamicCast + 251
10 libswiftCore.dylib       0x00007fff2c8f1ba4 $ss15_arrayForceCastySayq_GSayxGr0_lF + 676
11 libswiftCore.dylib       0x00007fff2c8f0f7d _swift_arrayDownCastIndirect + 45
12 libswiftCore.dylib       0x00007fff2cbf0e88 tryCastToArray(swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*&, swift::TargetMetadata<swift::InProcess> const*&, bool, bool) + 104
13 libswiftCore.dylib       0x00007fff2cbf01d5 tryCast(swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*&, swift::TargetMetadata<swift::InProcess> const*&, bool, bool) + 565
14 libswiftCore.dylib       0x00007fff2cbf056f tryCast(swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*&, swift::TargetMetadata<swift::InProcess> const*&, bool, bool) + 1487
15 libswiftCore.dylib       0x00007fff2cbefe6c swift_dynamicCast + 156
16 libswiftCore.dylib       0x0000000118fec797 swift_dynamicCast + 18446603344479766983
17 libswiftCore.dylib       0x0000000118fec18a swift_dynamicCast + 18446603344479765434
18 swift-frontend           0x000000010edc5e40 llvm::orc::runAsMain(int (*)(int, char**), llvm::ArrayRef<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, llvm::Optional<llvm::StringRef>) + 1408
19 swift-frontend           0x000000010ed26cb7 swift::RunImmediately(swift::CompilerInstance&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, swift::IRGenOptions const&, swift::SILOptions const&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule> >&&) + 12839
20 swift-frontend           0x000000010ece5eac 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*) + 2604
21 swift-frontend           0x000000010ecd7cae swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 14398
22 swift-frontend           0x000000010ec19718 main + 1032
23 libdyld.dylib            0x00007fff20545f3d start + 1
[1]    91009 abort      DEVELOPER_DIR=/Applications/Xcode-13.0.0-Beta.4.app/Contents/Developer swift 
@LucianoPAlmeida
Copy link
Collaborator Author

@LucianoPAlmeida LucianoPAlmeida commented Aug 17, 2021

@tbkka
Copy link
Contributor

@tbkka tbkka commented Aug 18, 2021

To decipher this, note that:

  • `foo` is getting called with `defaultValue` being an Array<String>

  • Therefore: T == [String] and U == String

  • T.Element.deserializeExperiment is therefore String.deserializeExperiment

  • String.deserializeExperiment is `{ value as? String }`

Stripped to essentials, your example is this:

let array: [Any] = ["x", 1]
let array2: [String?] = array.map { $0 as? String }   // [ "x", nil ]
let value = array2 as! [String]                // runtime crash

Because the second element of array2 is nil, it cannot be cast to a non-optional string. Since you requested a force-cast `as!`, the program crashes when the cast fails.

@tbkka
Copy link
Contributor

@tbkka tbkka commented Aug 18, 2021

So I think this is being handled correctly. The program is crashing because the `as!` cast has failed, and the message is correctly explaining why it failed:

Could not cast value of type 'Swift.Optional<Swift.String>' (0x7fff8170fd50) to 'Swift.String' (0x7fff81709120).

@tbkka
Copy link
Contributor

@tbkka tbkka commented Aug 18, 2021

The behavior here is the correct and expected behavior when you use a force-cast operator to cast a nil optional to a non-optional type.

@LucianoPAlmeida
Copy link
Collaborator Author

@LucianoPAlmeida LucianoPAlmeida commented Aug 18, 2021

Ah I see now, this is indeed correct. Thank you @tbkka for the detailed response.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants