File: pch-bridging-header-vs-modular-interface-defn.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (86 lines) | stat: -rw-r--r-- 4,229 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//
// Scenario for this test (this actually occurs in the wild):
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//   1. A header for module M, in include-path, that defines @interface Foo
//      and a @protocol Bar that references Foo in one of its methods.
//
//   2. A symlink in include-path that links into M's (modular) header dir,
//      which effectively makes a "second definition" of the same interface
//      and protocol pair, but in a non-modular context.
//
//   3. A bridging header that imports the (non-modular)
//      header-defining-Foo-and-Bar
//
//   4. A swift file that imports M and implements Bar (thus referencing Foo).
//
//   5. Another swift file that does nothing special, but makes for a multi-file
//      compilation (requiring a merge-module step).
//
//
// What was previously going wrong (that we're testing for):
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//   1. Module M gets compiled into a PCM file with a local defn for M.Foo and
//      M.Bar referencing M.Foo.
//
//   2. The bridging header gets precompiled to a PCH with a non-modular
//      definition of Foo and Bar (because of the order we pass -Xcc includes
//      to the clang importer, they are preferred over frameworks).
//
//   3. Every time swift asks clang to import a defn -- modular or non-modular
//      -- clang reads _both_ definitions: first the one requested, then the
//      other one as a completion of its view of the set of defns for the
//      symbol. This wouldn't normally be a huge problem _except_ that in clang
//      rev 2ba19793, the rules for interfaces and protocols diverged:
//      interfaces became first-read-wins, protocols remained last-read-wins.
//
//   4. This means that when swift looks up (protocol) Bar, the non-modular
//      definition is found in the PCH, clang completes it with a modular defn,
//      and (since it's a protocol) last-read-wins: the modular M.Bar defn
//      sticks.  Swift then does a load-all-members on M.Bar and gets the
//      (directly-referenced) M.Foo defn, but since Foo is an interface, M.Foo
//      is the first-read and it sticks (ignoring the non-modular Foo read
//      immediately-after from the PCH). So Swift emits an XRef to M.Foo into
//      the partial .swiftmodule.
//
//   5. Later, the merge-modules step executes and reloads the .swiftmodule
//      file, reading the XRef to M.Foo. Unfortunately when it's run with a
//      textual bridging header, the textual, non-modular defn of Foo is fed
//      directly into the compiler by the preprocessor, so it sticks before any
//      lookups get started. This makes the modular XRef lookup of M.Foo fail.
//
//
// Why disabling bridging PCH fixes this
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// The first-read defn of Foo is fed into clang early, which sticks (as in
// the merge-modules step). This makes the serialized XRef have the form
// __ObjC.Foo, which succeeds when performed the second time in merge-modules.
//
//
// How to fix it:
// ~~~~~~~~~~~~~~
//
// One might hope that this can be fixed by having the merge-modules step take a
// PCH as well as other steps in the compilation. That unfortunately only
// inverts the problem, which resurfaces in the definition-order of Bar itself:
// an XRef to __ObjC.Bar gets serialized, and then _it_ can't be found during
// merge-modules, because the *last-read* defn of Bar -- the modular one, M.Bar
// -- wins during lookup.
//
// So while we _do_ propose to have the merge-modules step use the PCH, we also
// put a patch into clang to apply the first-read-wins logic to protocols as
// well as interfaces.
//

// RUN: rm -rf %t
// RUN: mkdir -p %t/Headers/Simple
// RUN: ln -s %S/Inputs/frameworks/Simple.framework/Headers/Simple.h %t/Headers/Simple/Simple.h
// RUN: %target-build-swift -emit-module -module-name test -Xfrontend -enable-objc-interop -Xfrontend -disable-deserialization-recovery -v -F %S/Inputs/frameworks -Xcc "-I%t/Headers" -module-cache-path %t/clang-module-cache -import-objc-header %S/Inputs/pch-bridging-header-with-non-modular-import.h %S/Inputs/other.swift %s

import Simple
class Foo: SimpleProtocol {
    func foo(_ bar: SimpleInterface) {
    }
}