Typealias pointing to inaccessible class in framework

Hi,

Overview:

I have a class called OperationQueue defined in FrameworkA

I created a typealias to point to FrameworkA.OperationQueue but forgot to make OperationQueue public

Code:

FrameworkA:

import Foundation

class OperationQueue : Foundation.OperationQueue { } //Forgot to make it public

App:

import Foundation
import FrameworkA

typealias OperationQueue = FrameworkA.OperationQueue

let oq = OperationQueue()

Problem:

When I ran the project, FrameworkA.OperationQueue wasn't used instead Foundation's class was used because my class wasn't public. It works as expected when I defined it as public.

Question:

It is my mistake not making it public, but curious, would the compiler be able to spot that the class being pointed to (by typealias) is not accessible and throw a compilation error ?

No, because internal and private typealiases are perfectly reasonable things to use. Also, I don't think that typealias is public.

EDIT: Actually, wait. The typealias was in the app? I wouldn't have thought that FrameworkA.Type would have included stuff from Foundation.

Thanks @Nobody1707 for the response, yes I was surprised as well.

The typealias is defined in the app.

I think (I might be wrong), since my class isn't accessible, the Foundation's class is used.

I meant the underlying class that the type alias points to is not accessible because it was defined in a framework.

In my code I was pointing to something that wasn't accessible.

It is definitely my mistake not making public, just wondering if it is something that the compiler might be aware of and can catch at compile time.

Note: Will edit question to include import statements

It's not hard to check for that, but is it worth the time? You might have equally intended to use Foundation.OperationQueue. It is not recommended to load the compiler with warnings that are rather a guess than helpful. That is in contrast to, say, when you explicitly refer to an inaccessible declaration, i.e. FrameworkA.OperationQueue. Moreover, I wouldn't call shadowing top-level declarations from other modules good practice. On rare occasions it can be useful as a hack.

This looks like a straightforward bug to me. I can't see a reason why typealias OperationQueue = FrameworkA.OperationQueue should compile at all when FrameworkA.OperationQueue isn't visible in the app.

3 Likes

Um, yeah. If importing module A in module B doesn't imply A being part of B when referring to the latter. But I think it does, otherwise accessibility would be messed up with declarations in B that use A. If so, FrameworkA.OperationQueue resolves to Foundation.OperationQueue if FrameworkA imports Foundation and the other OperationQueue is inaccessible.

Thanks @anthonylatsis for the response.

This was something that I stumbled upon while working on a framework, just felt it might add more transparency if it threw a compilation error if possible.

Thanks @xwu, is it ok to create a bug report for this issue ?

Pardon my ignorance, I have not created a bug report before , should I create it at https://bugs.swift.org ?

Would you be able to review it once I have created it, many thanks.

It doesn't; to re-export 'A' you must use the underscored @_exported import A in 'B'.

2 Likes

Yes, create a bug at bugs.swift.org. The core team and others will go through and do appropriate triaging.

1 Like

@xwu I also noticed something strange, not sure if I am making a blunder.

I tried to reference it directly without the typealias, the issue isn't with typealias but accessing classes defined in more than one framework.

Even when FrameworkA.OperationQueue() is explicitly mentioned while creating a variable, it still uses Foundation.OperationQueue()

Not sure if others are also facing the same issue or if I am missing something obvious.

FrameworkA

import Foundation

class OperationQueue { } //Forgot to make it public
class X {}

App:

import UIKit
import FrameworkA

let oq = FrameworkA.OperationQueue() //Uses Foundation.OperationQueue

//Following throw compilation errors:
let x1 = X() //Use of unresolved identifier 'X'
let x2 = FrameworkA.X() //Module 'FrameworkA' has no member named 'X'

Configuration:

Xcode 9.4
Swift 4.1
iOS 11.4

Bug report:

Created a bug https://bugs.swift.org/browse/SR-8092

1 Like

You don't import a module into another module, you import a module into a source code file - a single compilation unit. For example, if module B has two source files: b1.swift and b2.swift, if I import module A into b1.swift its public types are visible in b1.swift but they are not visible in b2.swift. Although, having done some experimentation, it seems that if a public method returns an object of a type defined in A, that type seems to be exported from B as well, which I guess is needed so that callers can use the return result.

However, if you see code that says let foo = A.OperationQueue() the intent is clearly that you want an instance of A.OperationQueue and not Foundation.OperationQueue. That should be an error IMO.

2 Likes

I did not mean you could literally import a module into another module, nor did I use the 'into' preposition.

The method can be internal. This also applies to generics and any at least internal precedent of a visible declaration or ready-to-use object of a type that comes from A, as long as you don't have to manually instantiate it in the latter case. Public entities can be further propagated this way across modules.