Skip to content

Commit 270f1d8

Browse files
committed
[Swiftify] don't add macro when required type is unavailable
A Swift module only imports a type definition once. This means that some clang modules may have function signatures imported with `UnsafePointer<qux>` even if `qux` is only forward declared in that module (which would normally be imported as `OpaquePointer`), because some Swift file in the module imported the clang module with the definition, so ClangImporter sees the definition. The macro expansion only imports the imports of the clang module it belongs to however, so namelookup for `qux` fails. This patch detects when this will result in an error and avoids attaching the macro in those cases. rdar://165864358
1 parent f083c4b commit 270f1d8

File tree

2 files changed

+195
-4
lines changed

2 files changed

+195
-4
lines changed

lib/ClangImporter/SwiftifyDecl.cpp

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
#include "clang/AST/ASTContext.h"
2525
#include "clang/AST/Attr.h"
2626
#include "clang/AST/Decl.h"
27+
#include "clang/AST/RecursiveASTVisitor.h"
2728
#include "clang/AST/StmtVisitor.h"
2829
#include "clang/AST/Type.h"
30+
#include "clang/Basic/Module.h"
2931

3032
using namespace swift;
3133
using namespace importer;
@@ -271,14 +273,15 @@ struct CountedByExpressionValidator
271273
};
272274

273275

274-
// Don't try to transform any Swift types that _SwiftifyImport doesn't know how
275-
// to handle.
276-
static bool SwiftifiableCountedByPointerType(Type swiftType) {
276+
static bool IsConcretePointerType(Type swiftType) {
277277
Type nonnullType = swiftType->lookThroughSingleOptionalType();
278278
PointerTypeKind PTK;
279279
return nonnullType->getAnyPointerElementType(PTK) &&
280280
(PTK == PTK_UnsafePointer || PTK == PTK_UnsafeMutablePointer);
281281
}
282+
283+
// Don't try to transform any Swift types that _SwiftifyImport doesn't know how
284+
// to handle.
282285
static bool SwiftifiableSizedByPointerType(const clang::ASTContext &ctx,
283286
Type swiftType,
284287
const clang::CountAttributedType *CAT) {
@@ -311,7 +314,7 @@ static bool SwiftifiableCAT(const clang::ASTContext &ctx,
311314
return CAT && CountedByExpressionValidator().Visit(CAT->getCountExpr()) &&
312315
(CAT->isCountInBytes() ?
313316
SwiftifiableSizedByPointerType(ctx, swiftType, CAT)
314-
: SwiftifiableCountedByPointerType(swiftType));
317+
: IsConcretePointerType(swiftType));
315318
}
316319

317320
// Searches for template instantiations that are not behind type aliases.
@@ -331,6 +334,36 @@ struct UnaliasedInstantiationVisitor
331334
}
332335
};
333336

337+
struct ForwardDeclaredConcreteTypeVisitor
338+
: clang::RecursiveASTVisitor<ForwardDeclaredConcreteTypeVisitor> {
339+
bool hasForwardDeclaredConcreteType = false;
340+
clang::Module *Owner;
341+
342+
ForwardDeclaredConcreteTypeVisitor(clang::Module *Owner) : Owner(Owner) {};
343+
344+
bool VisitRecordType(clang::RecordType *RT) {
345+
const clang::RecordDecl *RD = RT->getDecl()->getDefinition();
346+
ASSERT(RD && "pointer to concrete type without type definition?");
347+
const clang::Module *M = RD->getOwningModule();
348+
if (!Owner->isModuleVisible(M)) {
349+
hasForwardDeclaredConcreteType = true;
350+
DLOG("Imported signature contains concrete type not available in clang module, skipping");
351+
LLVM_DEBUG(DUMP(RD));
352+
return false;
353+
}
354+
return true;
355+
}
356+
357+
bool IsIncompatibleImport(Type SwiftTy, clang::QualType ClangTy) {
358+
DLOG_SCOPE("Checking compatibility of type: " << ClangTy << "\n");
359+
if (!IsConcretePointerType(SwiftTy)) {
360+
return false;
361+
}
362+
TraverseType(ClangTy);
363+
return hasForwardDeclaredConcreteType;
364+
}
365+
};
366+
334367
// until CountAttributedType::getAttributeName lands in our LLVM branch
335368
static StringRef getAttributeName(const clang::CountAttributedType *CAT) {
336369
switch (CAT->getKind()) {
@@ -375,6 +408,8 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
375408

376409
clang::ASTContext &clangASTContext = Self.getClangASTContext();
377410

411+
ForwardDeclaredConcreteTypeVisitor CheckForwardDecls(ClangDecl->getOwningModule());
412+
378413
// We only attach the macro if it will produce an overload. Any __counted_by
379414
// will produce an overload, since UnsafeBufferPointer is still an improvement
380415
// over UnsafePointer, but std::span will only produce an overload if it also
@@ -389,6 +424,10 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
389424
else
390425
ABORT("Unexpected AbstractFunctionDecl subclass.");
391426
clang::QualType clangReturnTy = ClangDecl->getReturnType();
427+
428+
if (CheckForwardDecls.IsIncompatibleImport(swiftReturnTy, clangReturnTy))
429+
return false;
430+
392431
bool returnIsStdSpan = printer.registerStdSpanTypeMapping(
393432
swiftReturnTy, clangReturnTy);
394433
auto *CAT = clangReturnTy->getAs<clang::CountAttributedType>();
@@ -440,6 +479,10 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
440479
}
441480
ASSERT(swiftParam);
442481
Type swiftParamTy = swiftParam->getInterfaceType();
482+
483+
if (CheckForwardDecls.IsIncompatibleImport(swiftParamTy, clangParamTy))
484+
return false;
485+
443486
bool paramHasBoundsInfo = false;
444487
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
445488
if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file %s %t
5+
6+
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/test.swiftmodule %t/test.swift -I %t -enable-experimental-feature SafeInteropWrappers -strict-memory-safety \
7+
// RUN: -verify -verify-additional-file %t%{fs-sep}foo.h -verify-additional-file %t%{fs-sep}bar.h -verify-additional-file %t%{fs-sep}baz.h -verify-additional-file %t%{fs-sep}qux.h -Rmacro-expansions
8+
9+
// Macro expansions in foo.h do not have access to the definition of `struct qux`,
10+
// so don't attach macro on functions that use contain that type in foo.h.
11+
// bar.h imports the type, so attach the macro.
12+
// baz.h imports bar.h which re-exports the type, so attach the macro there too.
13+
14+
//--- foo.h
15+
#pragma once
16+
17+
#define __counted_by(x) __attribute__((__counted_by__(x)))
18+
19+
struct qux;
20+
// expected-note@+1{{'foo' declared here}}
21+
void foo(struct qux *x, int * __counted_by(len) p, int len);
22+
// expected-note@+1{{'fooReturn' declared here}}
23+
struct qux * fooReturn(int * __counted_by(len) p, int len);
24+
25+
26+
//--- bar.h
27+
#pragma once
28+
#include "qux.h"
29+
30+
#define __counted_by(x) __attribute__((__counted_by__(x)))
31+
32+
// expected-expansion@+10:59{{
33+
// expected-remark@1{{macro content: |/// This is an auto-generated wrapper for safer interop|}}
34+
// expected-remark@2{{macro content: |@_alwaysEmitIntoClient @_disfavoredOverload public func bar(_ x: UnsafeMutablePointer<qux>!, _ p: UnsafeMutableBufferPointer<Int32>) {|}}
35+
// expected-remark@3{{macro content: | let len = Int32(exactly: p.count)!|}}
36+
// expected-remark@4{{macro content: | return unsafe bar(x, p.baseAddress!, len)|}}
37+
// expected-remark@5{{macro content: |}|}}
38+
// }}
39+
// expected-expansion@+3:6{{
40+
// expected-note@1 5{{in expansion of macro '_SwiftifyImport' on global function 'bar' here}}
41+
// }}
42+
void bar(struct qux *x, int * __counted_by(len) p, int len);
43+
// expected-expansion@+10:58{{
44+
// expected-remark@1{{macro content: |/// This is an auto-generated wrapper for safer interop|}}
45+
// expected-remark@2{{macro content: |@_alwaysEmitIntoClient @_disfavoredOverload public func barReturn(_ p: UnsafeMutableBufferPointer<Int32>) -> UnsafeMutablePointer<qux>! {|}}
46+
// expected-remark@3{{macro content: | let len = Int32(exactly: p.count)!|}}
47+
// expected-remark@4{{macro content: | return unsafe barReturn(p.baseAddress!, len)|}}
48+
// expected-remark@5{{macro content: |}|}}
49+
// }}
50+
// expected-expansion@+3:14{{
51+
// expected-note@1 5{{in expansion of macro '_SwiftifyImport' on global function 'barReturn' here}}
52+
// }}
53+
struct qux * barReturn(int * __counted_by(len) p, int len);
54+
55+
56+
//--- baz.h
57+
#pragma once
58+
59+
#include "bar.h"
60+
61+
struct qux;
62+
// expected-expansion@+10:59{{
63+
// expected-remark@1{{macro content: |/// This is an auto-generated wrapper for safer interop|}}
64+
// expected-remark@2{{macro content: |@_alwaysEmitIntoClient @_disfavoredOverload public func baz(_ x: UnsafeMutablePointer<qux>!, _ p: UnsafeMutableBufferPointer<Int32>) {|}}
65+
// expected-remark@3{{macro content: | let len = Int32(exactly: p.count)!|}}
66+
// expected-remark@4{{macro content: | return unsafe baz(x, p.baseAddress!, len)|}}
67+
// expected-remark@5{{macro content: |}|}}
68+
// }}
69+
// expected-expansion@+3:6{{
70+
// expected-note@1 5{{in expansion of macro '_SwiftifyImport' on global function 'baz' here}}
71+
// }}
72+
void baz(struct qux *x, int * __counted_by(len) p, int len);
73+
// expected-expansion@+10:58{{
74+
// expected-remark@1{{macro content: |/// This is an auto-generated wrapper for safer interop|}}
75+
// expected-remark@2{{macro content: |@_alwaysEmitIntoClient @_disfavoredOverload public func bazReturn(_ p: UnsafeMutableBufferPointer<Int32>) -> UnsafeMutablePointer<qux>! {|}}
76+
// expected-remark@3{{macro content: | let len = Int32(exactly: p.count)!|}}
77+
// expected-remark@4{{macro content: | return unsafe bazReturn(p.baseAddress!, len)|}}
78+
// expected-remark@5{{macro content: |}|}}
79+
// }}
80+
// expected-expansion@+3:14{{
81+
// expected-note@1 5{{in expansion of macro '_SwiftifyImport' on global function 'bazReturn' here}}
82+
// }}
83+
struct qux * bazReturn(int * __counted_by(len) p, int len);
84+
85+
86+
//--- qux.h
87+
#pragma once
88+
89+
struct qux { int placeholder; };
90+
91+
92+
//--- test.swift
93+
import Foo
94+
import Bar
95+
import Baz
96+
97+
func callFoo(_ x: UnsafeMutablePointer<qux>, _ p: UnsafeMutablePointer<CInt>, _ len: CInt) {
98+
unsafe foo(x, p, len)
99+
let _: UnsafeMutablePointer<qux> = unsafe fooReturn(p, len)
100+
}
101+
102+
func callFoo2(_ x: UnsafeMutablePointer<qux>, _ p: UnsafeMutableBufferPointer<CInt>) {
103+
// expected-error@+2{{missing argument for parameter #3 in call}}
104+
// expected-error@+1{{cannot convert value of type 'UnsafeMutableBufferPointer<CInt>' (aka 'UnsafeMutableBufferPointer<Int32>') to expected argument type 'UnsafeMutablePointer<Int32>'}}
105+
unsafe foo(x, p)
106+
// expected-error@+2{{missing argument for parameter #2 in call}}
107+
// expected-error@+1{{cannot convert value of type 'UnsafeMutableBufferPointer<CInt>' (aka 'UnsafeMutableBufferPointer<Int32>') to expected argument type 'UnsafeMutablePointer<Int32>'}}
108+
let _: UnsafeMutablePointer<qux> = unsafe fooReturn(p)
109+
}
110+
111+
112+
func callBar(_ x: UnsafeMutablePointer<qux>, _ p: UnsafeMutablePointer<CInt>, _ len: CInt) {
113+
unsafe bar(x, p, len)
114+
let _: UnsafeMutablePointer<qux> = unsafe barReturn(p, len)
115+
}
116+
117+
func callBar2(_ x: UnsafeMutablePointer<qux>, _ p: UnsafeMutableBufferPointer<CInt>) {
118+
unsafe bar(x, p)
119+
let _: UnsafeMutablePointer<qux> = unsafe barReturn(p)
120+
}
121+
122+
123+
func callBaz(_ x: UnsafeMutablePointer<qux>, _ p: UnsafeMutablePointer<CInt>, _ len: CInt) {
124+
unsafe baz(x, p, len)
125+
let _: UnsafeMutablePointer<qux> = unsafe bazReturn(p, len)
126+
}
127+
128+
func callBaz2(_ x: UnsafeMutablePointer<qux>, _ p: UnsafeMutableBufferPointer<CInt>) {
129+
unsafe baz(x, p)
130+
let _: UnsafeMutablePointer<qux> = unsafe bazReturn(p)
131+
}
132+
133+
134+
//--- module.modulemap
135+
module Foo {
136+
header "foo.h"
137+
}
138+
module Bar {
139+
header "bar.h"
140+
export qux
141+
}
142+
module Baz {
143+
header "baz.h"
144+
export Bar
145+
}
146+
module qux {
147+
header "qux.h"
148+
}

0 commit comments

Comments
 (0)