Proposal or Bug fix: allow canImport to check for submodule availability

Motivation

Currently, canImport only supports checking for top-level module availability while many have submodules with different availability. For example, while CIFilter was introduced in iOS 5, CIFilterBuiltins is only available on iOS 13+. Therefore, one might want to write code like this:

#if canImport(CoreImage.CIFilterBuiltins)
    ^ Unexpected platform condition argument: expected identifier

import CoreImage.CIFilterBuiltins
...
#endif

See also canImport(os.signpost)

Proposal? Bug fix?

My pull request extends canImport to check for submodule availability (and submodule availability only). According to the original proposal SE-0075,

#if canImport(module-name) tests for module support by name.

The question of "is this a proposal or a bug fix" comes down to the interpretation of "module name". Here are some facts:

  1. The original review / discussion never mentioned anything about submodules
  2. The Swift Programming Language reference specifies that module-name → identifier (likely based on the original implementation), but extending canImport to check for submodules means the syntax will need to be updated to module-name → import-path.

Do you all think this is just a simple bug fix, or is this something that has go through Swift evolution?

1 Like

bug fix IMO but can we have a diagnostic when a user tries #if canImport(A.B) where .B is not a submodule but a type like an enum.

What happens if canImport ever adds support for testing for types? In retrospect maybe canImport should have been named canImportModule.

I think current implementation emits diagnostics if the given "module" is not a module, but I'll add some tests to ensure that.

Regarding future directions, here is another thread for that: Using canImport with submodules and specific types.

Why I don't think renaming to canImportModule is a good idea:

If we decide to support checking for availability of types and rename canImport to canImportModule, I would assume we would similarly introduce canImportClass, canImportStruct, canImportEnum, etc., analogous to import class, import struct, import enum, etc..

1 Like

Actually, the current implementation does not emit any diagnostics if a type name is given. I might update my PR to emit a warning if no such submodule if found, but a matching type is.

1 Like

Now I'm a little reluctant to add the warning because, not only do we have to load the module to implement this (while currently we don't have to, nor do we want to), but also, for a hypothetical situation where there (somehow) exists 3 valid versions of module A:

  1. module A with neither submodule B nor type B
  2. module A with submodule B
  3. module A with type B

would we expect case 3 to behave just like case 1 for this piece of code, or should we emit an additional warning?

#if canImport(A.B)
...
#else
...
#endif

To me, emitting a warning in this case seems to be a false positive, and there's no way of silencing it (unlike "nearly matches protocol requirement").


Since we are on the subject of diagnostics, I do NOT think this existing test should produce a compile time error as it's doing now:

#if canImport(SomeModule)
// CHECK: :[[@LINE-1]]:5: error: could not find module 'SomeModule' for target '{{.*}}'; found: i386
#endif
1 Like

If you wanted to support types instead of just submodules, I think it would make the most sense to align with existing import syntax:

#if canImport(A.B)  // only checks for submodule A.B
  import A.B
#endif

#if canImport(class A.B)  // only checks for class B in module A
  import class A.B
#endif

However, I don't know if there are any additional concerns about checking for types, since it would involve loading the whole module/submodule anyway in order to determine whether or not the module exists, whereas (I believe? I could be wrong) checking canImport for a module doesn't actually require loading it, just verifying that it exists on the search path.

IMO (I speak with no authority here), I think the most sensible approach going forward would be to extend canImport(...) to handle submodules only, and leave support for types as a separate problem to be solved later.

8 Likes

Personally, I would consider this to be a bug fix. Import allows submodules, and so it's completely reasonable for canImport to be able to check for submodules.

Doug

6 Likes

Thanks to everyone for the feedback, the implementation is now merged.

4 Likes