importer::findOptionSetEnum() uses some fragile heuristics to determine
whether a typedef is involved in the construction of a CF_OPTIONS or
NS_OPTIONS type. This patch adds an explicit check that the typedef is
expanded from either of those macros, to prevent, e.g., an unavailable
NS_ENUM, from being mistakenly recognized as an NS_OPTIONS.
Note that doing this is still kind of fragile, and prevents users from
building {NS,CF}_OPTIONS with their own macros. The right thing to do is
probably specifically look for the flag_enum attribute, but that is not
currently what we're doing for reasons whose discovery is left as
an exercise to the future git archaeologist.
This patch also removes (part of) a test case that builds
a CF_OPTIONS-like type with the "SOME_OPTIONS" macro, which is no longer
supported as of this patch.
rdar://150399978
ClangImporter has logic that infers default arguments of certain C/C++ types, such as the types declared via `CF_OPTIONS`/`NS_OPTIONS` macros.
There were some workarounds in place which triggered for C++ interop mode specifically. The workarounds were applying a heuristic based on the name of the type, which tried to match the behavior to non-C++ interop mode for certain types from the OS SDK. That was not working well for user-defined types, causing source compatibility breakages when enabling C++ interop.
This change replaces the name-based heuristic with a more robust criteria.
See also 3791ccb6.
rdar://142961112
This removes a workaround from the module interface loader, which was forcing AppKit and UIKit to be rebuilt from their textual interfaces with C++ interop disabled, even if the current compilation explicitly enables it.
The workaround was previously put in place because of a compiler error:
```
error: type 'AttributeScopes.AppKitAttributes.StrikethroughStyleAttribute' does not conform to protocol 'AttributedStringKey'
note: possibly intended match 'AttributeScopes.AppKitAttributes.StrikethroughStyleAttribute.Value' (aka 'NSUnderlineStyle') does not conform to 'Hashable'
```
`NSUnderlineStyle` is a C/C++ type from AppKit that is declared using `NS_OPTIONS` macro. `NS_OPTIONS`/`CF_OPTIONS` macros have different expansions in C vs C++ language modes. The C++ expansions weren't handled correctly by ClangImporter, resulting in two distinct Swift types being created: a `typealias NSUnderlineStyle` which was marked as unavailable in Swift, and `enum NSUnderlineStyle`. This mostly worked fine, since the lookup logic was picking the enum during regular name lookup. However, this silently broke down when rebuilding the explicit conformance from `AppKit.swiftinterface`:
```
extension AppKit.NSUnderlineStyle : Swift.Hashable {}
```
Swift was picking the (unavailable) typealias when rebuilding this extension, which means the (available) enum wasn't getting the conformance.
This is verified by an existing test (`test/Interop/Cxx/objc-correctness/appkit-uikit.swift`).
rdar://142961112
Type annotations for instruction operands are omitted, e.g.
```
%3 = struct $S(%1, %2)
```
Operand types are redundant anyway and were only used for sanity checking in the SIL parser.
But: operand types _are_ printed if the definition of the operand value was not printed yet.
This happens:
* if the block with the definition appears after the block where the operand's instruction is located
* if a block or instruction is printed in isolation, e.g. in a debugger
The old behavior can be restored with `-Xllvm -sil-print-types`.
This option is added to many existing test files which check for operand types in their check-lines.
The hash of enum values are different from that of their underlying
rawValue due to the implementation details of Int on 64-bit platform
which are not true on 32-bit platforms. This commit removes the tests
that rely on this behavior.
rdar://138032645
* [cxx-interop] Add Hashable conformance to imported enums
Previously, imported enums only conformed to RawRepresentable and Equatable,
so they could not be used as members of a Set or keys of a Dictionary.
This patch adds Hashable conformance to give them that ability,
as well as some test cases to clarify the expected behavior.
Existing test cases are updated to reflect this new conformance.
rdar://129713687
Try importing ObjCPropertyDecl field types the C++-Interop-NS_OPTIONS
way. This will fix cases such as:
```
import UIKit
func f(gesture: UISwipeGestureRecognizer,
direction: UISwipeGestureRecognizer.Direction) {
gesture.direction = direction // error
}
```
because it will make sure the field inside class UIGestureRecognizer is
of the enum-struct type and not the typedef-rawValue type when importing
an ObjC class.
CF_OPTIONS is defined differently in the SDK based on
a __cplusplus preprocessor branch. As a result, declarations
referencing CF_OPTIONS are mangled differently depending
on if C++ interop is enabled.
This meant a module compiled with cxx interop on could
not be linked with a module compiled without and vice versa.
This patch modifies the mangler such that the mangled names
are consistent. This is achieved by feeding the mangler a modified
AST node that looks like the Objective-C definition of CF_OPTIONS,
even when we have cxx interop enabled.
When Objective-c methods are imported into Swift, the labels are
transforms to remove suffixes based on the type of the parameter. Due
to the structure of NS_OPTION when importing in C++ mode, there is
some special handling for this case introduced in PR #59421.
This patch fixes some of this hanlding for when a multi-word suffix
needs removing, specifically 'ControlEvents' and 'ScrollPosition'.
At the call sites of `findAnonymousEnumForTypedef` we often wish to
import the returned enum declaration and return the type the declaration
creates.
Prior to this patch, we assumed that the enum declaration would be
imported as a `NominalTypeDecl`. This is not always the case. For
whatever reason, sometimes in typechecking we import a declaration
for various different naming versions. If the Swift name for an imported
enum differs between the canonical name version, and currently
requested name version, we import the enum as a `TypeAliasDecl` instead.
Prior to this patch, this meant we would hit asserts importing some
components of UIKit.
This patch relaxes the assumption that the import is a `NominalTypeDecl`
to just a `TypeDecl`, which as of yet, seems to be true.
When the ClangImporter imports a name, it associates it with a
an EffectiveClangContext. An EffectiveClangContext can be thought of
as the Clang scope the declaration will reside in, as far as importing
into Swift is concerned. This helps API notes and NS_SWIFT_NAME
to manipulate the SDK interface presented to Swift users.
When a entry is added to the Swift lookup table, it is associated
with a context. This context is a type and a name, used to effectively
namespace entries in the table. This context is derived from the
EffectiveClangContext associated with the name. This translation is
handled by SwiftLookupTable::translateContextDecl among other machinery.
This method in particular, understands only how to translate a set of
Clang nodes, and fails to create a context in other cases.
Prior to this patch, the EffectiveClangContext of declarations annotated
with UIKIT_EXTERN, with cxx-interop turned on, was a LinkageSpecDecl.
This results in context translation failure, and warnings produced in
SwiftLookupTable::finalizeLookupTable. This patch corrects name import
behavior to skip over the LinkageSpecDecl, and use the enclosing
TranslationUnit instead. This is appropriate and performed by
`determineEffectiveContext` as a LinkageSpecDecl is a so called
"transparent" context. That is its members are semantically declared and
accessible outside the context without additional qualification.
This patch tests using API notes, as that is the method UIKit uses. The
issue could just as easily be surface with a NS_SWIFT_NAME annotation.
Even without any annotation at all, the we would still fail to
translate, though there would likely be no corresponding warnings.
Prior to this patch, SwiftLookupTable::translateDeclToContext relied
on the `TypedefNameDeclOrQualifier` field of an anonymous tag decl to
create a name for entry representing an anonymous tag in the lookup
table. This field is not always populated by Clang, it is often
populated only for the purposes of generating a linkage name when the
type is introduced via typedef as follows:
```
typedef enum { option1, option2} MyAnonEnum;
```
The field is not populated for anonymous enums introduced by NS_OPTIONS
with cxx interop enabled. This patch adds a fallback check in
`translateDeclToContext` that if the field is empty, check if the enum
is backed by a typedef that is unavailable in Swift. If that is the
case, use that name for the lookup table entry.
When an anonymous enum is imported, its imported name is under some
circumstances derived from the type that it is backed by.
NS_REFINED_FOR_SWIFT is a macro that produces a __attribute__((swift_private))
that when attached to a declaration, the imported name of this
declaration should have two underscores prepended. When the name
anonymous enum is derived from another declaration, and said declaration
does not have the same swift private attribute, the __ is dropped. This
patch fixes this.
When ClangImporter::Implementation::inferDefaultArgument processes
func/method arguments as part of omitNeedlessWordsInFunctionName it
processes information about how the typenames for the parameters related
to the parameter names to form a parameter names list. The parameter
names list is used to determine if the argument label for a function
should be clipped based on the typename. So for example a type like
NSOrderedCollectionDifferenceCalculationOptions would cause a label
ending with "Options" to get clipped so that for instance "withOptions"
becomes simply "with".
Unfortunately in the context of C++-Interop, the typename for the
parameter often resolves to what the type backing the typedef or enum is
and not the actual name of the typedef
(so `typedef NSUInteger NSOrderedCollectionDifferenceCalculationOptions`
resolves to a name of NSUInteger rather than
NSOrderedCollectionDifferenceCalculationOptions).
This patch seeks to collect a bit more information when processing
NS_OPTIONS typedefs and providing that to the calling
omitNeedlessWordsInFunctionName to handle more inteligently.
In practice this fixes anywhere in Foundatio where
`withOptions: NSOrderedCollectionDifferenceCalculationOptions` is used.
This patch fixes an issue with C++-Interop that has been making it so
that enums under interop were not getting properly looked up and
therefore not getting imported. The reason for this was that the proper
DeclContext was not getting applied when grabbing the decls use by
VisitDecls later in the ClangImporter.
The lookup code at SwiftLookupTable::lookup is given a clang TU which is
what implcitly gets turned into an EffectiveClangContext. The
EffectiveClangContext is the piece that decides which DeclContext to
use. In the case of an extern "C" (ie LinkageSpecDecl), the
EffectiveClangContext was not traversing inside the lexical scope of the
extern "C" as the context for searching the EnumDecl (the NS_OPTIONS
Enum).
This patch adds new behavior when EffectiveClangContext is given a
LinkageSpecDecl. It sets the DeclContext to the lexical decl context.
With this fix in place in the presence of C++-Interop we not only import
the NS_OPTIONS typedef properly, but we also import the enum (and
therefore the EnumConstants) correctly (which are used for getting to
the flags for populating the NS_OPTIONS bitfields.
As I understand it, enums in C++ do not allow for bitwise operations
that can also be assigned or returned as the same enum type. As a result
there are macros in (Core)Foundation like NS/CF_OPTIONS that produce
enums differently depending on #if __cplusplus or not.
Because of this, code in omitNeedlessWordsInFunctionName and
subsequently inferDefaultArgument in the ClangImporter that is in charge
of replacing "needless words" from method and enum names does not
trigger in the case of C++ because it is looking for EnumDecls that do
not exists because they are actually typedefs on wrap integers or
NSIntegers.
This change attempts to do the renaming off of the typedef alone when
such code exists.
Special thanks to @bulbazord (Alex Langford) for taking the time to
investigate this issue.
If possible, add imported members to the StructDecl's LookupTable rather than adding them directly as members. This will fix the issues with ordering that #39436 poorly attempted to solve during IRGen.
This also allows us to break out most of the test changes from #39436.
The C++ interop modules require C++ support. Explicitly require C++ as
a feature when building these modules. This has no impact on the
changes as all the tests enable C++ already.
Simply treat scoped enums as (pre-existing) "non frozen enums". C++
scoped enums are actually imported as Swift enums (unlike other enums)
and no global variables need be created (given their "scoped" nature).
This is a small fix to prevent a crash. This change simply adds another
condition for the "bool" branch that checks if "type" is associated with
a "clang::EnumDecl" with an underlying type of "bool", and if so, treats
"type" as a "Bool".