Can't get Error pattern matching to work cross framework and command line tool


(Joakim Hassila) #1

Ok, I am stumped and wanted to see if anyone had any ideas on how to troubleshoot this.

Short background:
First off, I do have throwing/do/catch working perfectly fine in Playgrounds as well as in standalone apps as well as in an app that links with one of my own test frameworks, so I would hope this is not a simple user error.

I’m testing this on OS X w. XCode 7.3.1 using Swift 2.2 all with Debug builds.

I have a command line tool project "X" that links with two of my own Swift frameworks, “Y" and “Z". Z also links with a pure C-language library, “W” using a module map.
All of this works fine, as X can use entities from both Y and Z perfectly fine, including indirect access of the C-library beneath.

So the issue is when I am looking at throwing errors from Z, as X can’t properly pattern match the errors.

Short snippet showing relevant code and output from a testrun:

From the command line tool X:

···

——
func localThrow () throws -> Void
{
    throw Transaction.Error.NotFound
}

do{
    try localThrow() // try throwing from embedded local function
} catch Transaction.Error.NotFound { // this matches as expectd
    print("Transaction not found (local)")
} catch {
    print("Unknown error (local)")
}

let transaction2 = Transaction()

do {
    try transaction2.errorNow() // try throwing from framework
} catch Transaction.Error.NotFound { // this never matches!
    print("Transaction not found (framework)")
} catch {
    print("Unknown error (framework)")
}
——

In the framework Z, we have:
——
import W

public class Transaction {
// ...
}

public extension Transaction {
    
    public final func errorNow() throws -> Void
    {
        throw Transaction.Error.NotFound
    }
}
——

and the output when run is:
——
Transaction not found (local)
Unknown error (framework)
——

Setting a breakpoint and inspecting ‘error’ shows the following when stepping over the call, and single stepping in the debugger shows that it throws properly, it is just the matching that seems to fail. The Xcode lldb inspector shows the error value as:

error = (Z.Transaction.Error) NotFound

When single-stepping the local call, ‘error’ does not seem to be populated properly but it matches as it should.

I tried minimizing it with a simple app (not command line though) and framework, but there it works as expected of course…

Anyone have any ideas what might be the problem?

Cheers,

Joakim


(Ian Terrell) #2

Is this a naming collision? Do multiple modules defined a
Transaction.Error.NotFound?

For instance, in your passing case, is the Transaction.Error.NotFound that
is caught a Z.Transaction.Error.NotFound, or one in your local app module
(i.e. CLI.Transaction.Error.NotFound).

If your CLI module has that defined, then that is what you are looking for
in the catch statement. And since a Z.Transaction is not a CLI.Transaction
it fails to catch it appropriately.

Just a thought.

Ian

···

On Tue, May 31, 2016 at 12:44 AM, Joakim Hassila via swift-users < swift-users@swift.org> wrote:

Ok, I am stumped and wanted to see if anyone had any ideas on how to
troubleshoot this.

Short background:
First off, I do have throwing/do/catch working perfectly fine in
Playgrounds as well as in standalone apps as well as in an app that links
with one of my own test frameworks, so I would hope this is not a simple
user error.

I’m testing this on OS X w. XCode 7.3.1 using Swift 2.2 all with Debug
builds.

I have a command line tool project "X" that links with two of my own Swift
frameworks, “Y" and “Z". Z also links with a pure C-language library, “W”
using a module map.
All of this works fine, as X can use entities from both Y and Z perfectly
fine, including indirect access of the C-library beneath.

So the issue is when I am looking at throwing errors from Z, as X can’t
properly pattern match the errors.

Short snippet showing relevant code and output from a testrun:

From the command line tool X:
——
func localThrow () throws -> Void
{
    throw Transaction.Error.NotFound
}

do{
    try localThrow() // try throwing from embedded local function
} catch Transaction.Error.NotFound { // this matches as expectd
    print("Transaction not found (local)")
} catch {
    print("Unknown error (local)")
}

let transaction2 = Transaction()

do {
    try transaction2.errorNow() // try throwing from framework
} catch Transaction.Error.NotFound { // this never matches!
    print("Transaction not found (framework)")
} catch {
    print("Unknown error (framework)")
}
——

In the framework Z, we have:
——
import W

public class Transaction {
// ...
}

public extension Transaction {

    public final func errorNow() throws -> Void
    {
        throw Transaction.Error.NotFound
    }
}
——

and the output when run is:
——
Transaction not found (local)
Unknown error (framework)
——

Setting a breakpoint and inspecting ‘error’ shows the following when
stepping over the call, and single stepping in the debugger shows that it
throws properly, it is just the matching that seems to fail. The Xcode lldb
inspector shows the error value as:

error = (Z.Transaction.Error) NotFound

When single-stepping the local call, ‘error’ does not seem to be populated
properly but it matches as it should.

I tried minimizing it with a simple app (not command line though) and
framework, but there it works as expected of course…

Anyone have any ideas what might be the problem?

Cheers,

Joakim

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Joakim Hassila) #3

Hi Ian,

Nope, it is the only definition - I also actually have tried to fully qualify it as Z.Transaction.Error.NotFound without any difference both at the throw and catch sides.

I do believe the compiler would have warned if the enum was ambigious also...

Thanks,

Joakim

···

On 31 maj 2016, at 17:23, Ian Terrell <ian.terrell@gmail.com> wrote:

Is this a naming collision? Do multiple modules defined a Transaction.Error.NotFound?

For instance, in your passing case, is the Transaction.Error.NotFound that is caught a Z.Transaction.Error.NotFound, or one in your local app module (i.e. CLI.Transaction.Error.NotFound).

If your CLI module has that defined, then that is what you are looking for in the catch statement. And since a Z.Transaction is not a CLI.Transaction it fails to catch it appropriately.

Just a thought.

Ian


(Joe Groff) #4

Would you be able to file a bug with the project that displays this behavior?

-Joe

···

On May 31, 2016, at 8:50 AM, Joakim Hassila via swift-users <swift-users@swift.org> wrote:

Hi Ian,

Nope, it is the only definition - I also actually have tried to fully qualify it as Z.Transaction.Error.NotFound without any difference both at the throw and catch sides.

I do believe the compiler would have warned if the enum was ambigious also...


(Joakim Hassila) #5

Hi Joe,

Absolutely, I just filed rdar://26569913 (used Radar according to community guidelines of putting projects requiring Xcode to reproduce there).

I hope I captured the dependencies / reproduction steps properly in the report, let me know if anything else is needed.

Cheers,

Joakim

···

On 31 maj 2016, at 23:23, Joe Groff <jgroff@apple.com> wrote:

On May 31, 2016, at 8:50 AM, Joakim Hassila via swift-users <swift-users@swift.org> wrote:

Hi Ian,

Nope, it is the only definition - I also actually have tried to fully qualify it as Z.Transaction.Error.NotFound without any difference both at the throw and catch sides.

I do believe the compiler would have warned if the enum was ambigious also...

Would you be able to file a bug with the project that displays this behavior?

-Joe


(Joe Groff) #6

Thank you!

-Joe

···

On May 31, 2016, at 9:49 PM, Joakim Hassila <joj@mac.com> wrote:

Hi Joe,

Absolutely, I just filed rdar://26569913 (used Radar according to community guidelines of putting projects requiring Xcode to reproduce there).

I hope I captured the dependencies / reproduction steps properly in the report, let me know if anything else is needed.

Cheers,

Joakim

On 31 maj 2016, at 23:23, Joe Groff <jgroff@apple.com> wrote:

On May 31, 2016, at 8:50 AM, Joakim Hassila via swift-users <swift-users@swift.org> wrote:

Hi Ian,

Nope, it is the only definition - I also actually have tried to fully qualify it as Z.Transaction.Error.NotFound without any difference both at the throw and catch sides.

I do believe the compiler would have warned if the enum was ambigious also...

Would you be able to file a bug with the project that displays this behavior?

-Joe


(Eddy Hahn) #7

Hi,

I am facing the same issue. Have you found a solution?

Eddy


(Joe Groff) #8

Guessing from the thread title, this might be a configuration problem. Without cajoling, Xcode will default to building command line tools by statically linking the Swift runtime and standard library into the executable, but if you dynamically link against frameworks that also use Swift, those frameworks will in turn link against the dynamic libraries for the Swift runtime and standard library. Each copy of the runtime will be confused trying to understand the types and other metadata from the other's. If you configure the command line tool to also use the dynamic Swift libraries, it should avoid this problem, though I'm not sure exactly how to do that; @jrose is there a way to configure Xcode to do this?


(Jordan Rose) #9

There's a hidden build setting SWIFT_FORCE_DYNAMIC_LINK_STDLIB that can control this. If you do this, though, you'll have to set up the "Runpath Search Paths" build setting so that it can find the Swift libraries somewhere else, since they can't be "copied into the app bundle" when there's no app bundle.

EDIT: That is, it's not really well-supported; it's a known hole.