Skip to content

Commit dc4ff24

Browse files
authored
Merge pull request #84866 from j-hui/do-not-import-template-type-arguments
[cxx-interop] Do not import template type arguments
2 parents ddc7398 + 61f9f62 commit dc4ff24

File tree

6 files changed

+207
-56
lines changed

6 files changed

+207
-56
lines changed

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,53 @@ class CxxValueSemantics
624624
void simple_display(llvm::raw_ostream &out, CxxValueSemanticsDescriptor desc);
625625
SourceLoc extractNearestSourceLoc(CxxValueSemanticsDescriptor desc);
626626

627+
struct ClangTypeExplicitSafetyDescriptor final {
628+
clang::CanQualType type;
629+
630+
ClangTypeExplicitSafetyDescriptor(clang::CanQualType type) : type(type) {}
631+
ClangTypeExplicitSafetyDescriptor(clang::QualType type)
632+
: type(type->getCanonicalTypeUnqualified()) {}
633+
634+
friend llvm::hash_code
635+
hash_value(const ClangTypeExplicitSafetyDescriptor &desc) {
636+
return llvm::hash_combine(desc.type.getTypePtrOrNull());
637+
}
638+
639+
friend bool operator==(const ClangTypeExplicitSafetyDescriptor &lhs,
640+
const ClangTypeExplicitSafetyDescriptor &rhs) {
641+
return lhs.type == rhs.type;
642+
}
643+
644+
friend bool operator!=(const ClangTypeExplicitSafetyDescriptor &lhs,
645+
const ClangTypeExplicitSafetyDescriptor &rhs) {
646+
return !(lhs == rhs);
647+
}
648+
};
649+
650+
void simple_display(llvm::raw_ostream &out,
651+
ClangTypeExplicitSafetyDescriptor desc);
652+
SourceLoc extractNearestSourceLoc(ClangTypeExplicitSafetyDescriptor desc);
653+
654+
/// Determine the safety of the given Clang declaration.
655+
class ClangTypeExplicitSafety
656+
: public SimpleRequest<ClangTypeExplicitSafety,
657+
ExplicitSafety(ClangTypeExplicitSafetyDescriptor),
658+
RequestFlags::Cached> {
659+
public:
660+
using SimpleRequest::SimpleRequest;
661+
662+
// Source location
663+
SourceLoc getNearestLoc() const { return SourceLoc(); };
664+
bool isCached() const { return true; }
665+
666+
private:
667+
friend SimpleRequest;
668+
669+
// Evaluation.
670+
ExplicitSafety evaluate(Evaluator &evaluator,
671+
ClangTypeExplicitSafetyDescriptor desc) const;
672+
};
673+
627674
struct CxxDeclExplicitSafetyDescriptor final {
628675
const clang::Decl *decl;
629676
bool isClass;

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ SWIFT_REQUEST(ClangImporter, ClangTypeEscapability,
4848
SWIFT_REQUEST(ClangImporter, CxxValueSemantics,
4949
CxxValueSemanticsKind(CxxValueSemanticsDescriptor), Cached,
5050
NoLocationInfo)
51+
SWIFT_REQUEST(ClangImporter, ClangTypeExplicitSafety,
52+
ExplicitSafety(ClangTypeExplicitSafetyDescriptor), Cached,
53+
NoLocationInfo)
5154
SWIFT_REQUEST(ClangImporter, ClangDeclExplicitSafety,
5255
ExplicitSafety(CxxDeclExplicitSafetyDescriptor), Cached,
5356
NoLocationInfo)

lib/ClangImporter/ClangImporter.cpp

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8640,6 +8640,51 @@ SourceLoc swift::extractNearestSourceLoc(SafeUseOfCxxDeclDescriptor desc) {
86408640
return SourceLoc();
86418641
}
86428642

8643+
void swift::simple_display(llvm::raw_ostream &out,
8644+
ClangTypeExplicitSafetyDescriptor desc) {
8645+
auto qt = static_cast<clang::QualType>(desc.type);
8646+
out << "Checking if type '" << qt.getAsString() << "' is explicitly safe.\n";
8647+
}
8648+
8649+
SourceLoc swift::extractNearestSourceLoc(ClangTypeExplicitSafetyDescriptor desc) {
8650+
return SourceLoc();
8651+
}
8652+
8653+
ExplicitSafety ClangTypeExplicitSafety::evaluate(
8654+
Evaluator &evaluator, ClangTypeExplicitSafetyDescriptor desc) const {
8655+
auto clangType = static_cast<clang::QualType>(desc.type);
8656+
8657+
// Handle pointers.
8658+
auto pointeeType = clangType->getPointeeType();
8659+
if (!pointeeType.isNull()) {
8660+
// Function pointers are okay.
8661+
if (pointeeType->isFunctionType())
8662+
return ExplicitSafety::Safe;
8663+
8664+
// Pointers to record types are okay if they come in as foreign reference
8665+
// types.
8666+
if (auto *recordDecl = pointeeType->getAsRecordDecl()) {
8667+
if (hasImportAsRefAttr(recordDecl))
8668+
return ExplicitSafety::Safe;
8669+
}
8670+
8671+
// All other pointers are considered unsafe.
8672+
return ExplicitSafety::Unsafe;
8673+
}
8674+
8675+
// Handle records recursively.
8676+
if (auto recordDecl = clangType->getAsTagDecl()) {
8677+
// If we reached this point the types is not imported as a shared reference,
8678+
// so we don't need to check the bases whether they are shared references.
8679+
return evaluateOrDefault(evaluator,
8680+
ClangDeclExplicitSafety({recordDecl, false}),
8681+
ExplicitSafety::Unspecified);
8682+
}
8683+
8684+
// Everything else is safe.
8685+
return ExplicitSafety::Safe;
8686+
}
8687+
86438688
void swift::simple_display(llvm::raw_ostream &out,
86448689
CxxDeclExplicitSafetyDescriptor desc) {
86458690
out << "Checking if '";
@@ -8711,49 +8756,16 @@ CustomRefCountingOperationResult CustomRefCountingOperation::evaluate(
87118756

87128757
/// Check whether the given Clang type involves an unsafe type.
87138758
static bool hasUnsafeType(Evaluator &evaluator, clang::QualType clangType) {
8714-
// Handle pointers.
8715-
auto pointeeType = clangType->getPointeeType();
8716-
if (!pointeeType.isNull()) {
8717-
// Function pointers are okay.
8718-
if (pointeeType->isFunctionType())
8719-
return false;
8720-
8721-
// Pointers to record types are okay if they come in as foreign reference
8722-
// types.
8723-
if (auto recordDecl = pointeeType->getAsRecordDecl()) {
8724-
if (hasImportAsRefAttr(recordDecl))
8725-
return false;
8726-
}
8727-
8728-
// All other pointers are considered unsafe.
8729-
return true;
8730-
}
87318759

8732-
// Handle records recursively.
8733-
if (auto recordDecl = clangType->getAsTagDecl()) {
8734-
// If we reached this point the types is not imported as a shared reference,
8735-
// so we don't need to check the bases whether they are shared references.
8736-
auto safety = evaluateOrDefault(
8737-
evaluator, ClangDeclExplicitSafety({recordDecl, false}),
8738-
ExplicitSafety::Unspecified);
8739-
switch (safety) {
8740-
case ExplicitSafety::Unsafe:
8741-
return true;
8742-
8743-
case ExplicitSafety::Safe:
8744-
case ExplicitSafety::Unspecified:
8745-
return false;
8746-
}
8747-
}
8748-
8749-
// Everything else is safe.
8750-
return false;
8760+
auto safety =
8761+
evaluateOrDefault(evaluator, ClangTypeExplicitSafety({clangType}),
8762+
ExplicitSafety::Unspecified);
8763+
return safety == ExplicitSafety::Unsafe;
87518764
}
87528765

87538766
ExplicitSafety
87548767
ClangDeclExplicitSafety::evaluate(Evaluator &evaluator,
87558768
CxxDeclExplicitSafetyDescriptor desc) const {
8756-
// FIXME: Somewhat duplicative with importAsUnsafe.
87578769
// FIXME: Also similar to hasPointerInSubobjects
87588770
// FIXME: should probably also subsume IsSafeUseOfCxxDecl
87598771

lib/ClangImporter/ImportDecl.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#include "clang/AST/RecordLayout.h"
7070
#include "clang/AST/RecursiveASTVisitor.h"
7171
#include "clang/AST/StmtVisitor.h"
72+
#include "clang/AST/TemplateBase.h"
7273
#include "clang/AST/Type.h"
7374
#include "clang/Basic/Specifiers.h"
7475
#include "clang/Basic/TargetInfo.h"
@@ -2299,31 +2300,37 @@ namespace {
22992300
}
23002301
}
23012302

2302-
// We have to do this after populating ImportedDecls to avoid importing
2303-
// the same decl multiple times. Also after we imported the bases.
23042303
if (const auto *ctsd =
23052304
dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
23062305
for (auto arg : ctsd->getTemplateArgs().asArray()) {
2307-
llvm::SmallVector<clang::TemplateArgument, 1> nonPackArgs;
2306+
auto done = false;
2307+
auto checkUnsafe = [&](clang::TemplateArgument tyArg) {
2308+
if (tyArg.getKind() != clang::TemplateArgument::Type)
2309+
return;
2310+
2311+
auto safety =
2312+
evaluateOrDefault(Impl.SwiftContext.evaluator,
2313+
ClangTypeExplicitSafety({tyArg.getAsType()}),
2314+
ExplicitSafety::Unspecified);
2315+
2316+
if (safety == ExplicitSafety::Unsafe) {
2317+
result->getAttrs().add(new (Impl.SwiftContext)
2318+
UnsafeAttr(/*implicit=*/true));
2319+
done = true;
2320+
}
2321+
};
2322+
23082323
if (arg.getKind() == clang::TemplateArgument::Pack) {
2309-
auto pack = arg.getPackAsArray();
2310-
nonPackArgs.assign(pack.begin(), pack.end());
2311-
} else {
2312-
nonPackArgs.push_back(arg);
2313-
}
2314-
for (auto realArg : nonPackArgs) {
2315-
if (realArg.getKind() != clang::TemplateArgument::Type)
2316-
continue;
2317-
auto SwiftType = Impl.importTypeIgnoreIUO(
2318-
realArg.getAsType(), ImportTypeKind::Abstract,
2319-
[](Diagnostic &&diag) {}, false, Bridgeability::None,
2320-
ImportTypeAttrs());
2321-
if (SwiftType && SwiftType->isUnsafe()) {
2322-
auto attr = new (Impl.SwiftContext) UnsafeAttr(/*implicit=*/true);
2323-
result->getAttrs().add(attr);
2324-
break;
2324+
for (auto pkArg : arg.getPackAsArray()) {
2325+
checkUnsafe(pkArg);
2326+
if (done)
2327+
break;
23252328
}
2329+
} else {
2330+
checkUnsafe(arg);
23262331
}
2332+
if (done)
2333+
break;
23272334
}
23282335
}
23292336

test/Interop/Cxx/class/safe-interop-mode.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ struct OwnedData {
8282
void takeSharedObject(SharedObject *) const;
8383
};
8484

85+
// A class template that throws away its type argument.
86+
//
87+
// If this template is instantiated with an unsafe type, it should be considered
88+
// unsafe even if that type is never used.
89+
template <typename> struct TTake {};
90+
91+
using TTakeInt = TTake<int>;
92+
using TTakePtr = TTake<int *>;
93+
using TTakeSafeTuple = TTake<SafeTuple>;
94+
using TTakeUnsafeTuple = TTake<UnsafeTuple>;
95+
8596
//--- test.swift
8697

8798
import Test
@@ -175,3 +186,21 @@ func useSharedReference(frt: SharedObject, x: OwnedData) {
175186
func useSharedReference(frt: DerivedFromSharedObject) {
176187
let _ = frt
177188
}
189+
190+
func useTTakeInt(x: TTakeInt) {
191+
_ = x
192+
}
193+
194+
func useTTakePtr(x: TTakePtr) {
195+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
196+
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
197+
}
198+
199+
func useTTakeSafeTuple(x: TTakeSafeTuple) {
200+
_ = x
201+
}
202+
203+
func useTTakeUnsafeTuple(x: TTakeUnsafeTuple) {
204+
// expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}}
205+
_ = x // expected-note{{reference to parameter 'x' involves unsafe type}}
206+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: split-file %s %t
2+
// RUN: %target-clang -c -o /dev/null -Xclang -verify -I %t/Inputs %t/cxx.cpp
3+
// RUN: %target-swift-frontend -typecheck -verify -cxx-interoperability-mode=default -I %t%{fs-sep}Inputs -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}CxxHeader.h %t%{fs-sep}main.swift
4+
// RUN: %target-swift-ide-test -print-module -module-to-print=CxxModule -I %t/Inputs -source-filename=x -cxx-interoperability-mode=default | %FileCheck %t/Inputs/CxxHeader.h
5+
6+
//--- Inputs/module.modulemap
7+
module CxxModule {
8+
requires cplusplus
9+
header "CxxHeader.h"
10+
}
11+
12+
//--- Inputs/CxxHeader.h
13+
#pragma once
14+
15+
struct Empty {};
16+
template <typename T> struct MissingMember { typename T::Missing member; };
17+
using SUB = MissingMember<Empty>;
18+
19+
struct Incomplete;
20+
template <typename T> struct IncompleteField { T member; };
21+
using INC = IncompleteField<Incomplete>;
22+
23+
template <typename S = SUB, typename I = INC> struct DefaultedTemplateArgs {};
24+
using DefaultedTemplateArgsInst = DefaultedTemplateArgs<>;
25+
26+
// CHECK: struct DefaultedTemplateArgs<MissingMember<Empty>, IncompleteField<Incomplete>> {
27+
// CHECK: }
28+
// CHECK: typealias DefaultedTemplateArgsInst = DefaultedTemplateArgs<MissingMember<Empty>, IncompleteField<Incomplete>>
29+
30+
template <typename S, typename I> struct ThrowsAwayTemplateArgs {};
31+
using ThrowsAwayTemplateArgsInst = ThrowsAwayTemplateArgs<SUB, INC>;
32+
33+
// CHECK: struct ThrowsAwayTemplateArgs<MissingMember<Empty>, IncompleteField<Incomplete>> {
34+
// CHECK: }
35+
// CHECK: typealias ThrowsAwayTemplateArgsInst = ThrowsAwayTemplateArgs<MissingMember<Empty>, IncompleteField<Incomplete>>
36+
37+
//--- cxx.cpp
38+
// expected-no-diagnostics
39+
#include <CxxHeader.h>
40+
void make(void) {
41+
DefaultedTemplateArgs<> dta{};
42+
DefaultedTemplateArgsInst dtai{};
43+
44+
ThrowsAwayTemplateArgs<SUB, INC> tata{};
45+
ThrowsAwayTemplateArgsInst tatai{};
46+
}
47+
48+
//--- main.swift
49+
import CxxModule
50+
func make() {
51+
let _: DefaultedTemplateArgsInst = .init()
52+
let _: ThrowsAwayTemplateArgsInst = .init()
53+
}

0 commit comments

Comments
 (0)