Skip to content

Commit 62ff0d6

Browse files
author
Gabor Horvath
committed
[cxx-interop] Check the safety of C++ template arguments
Swift imports template specializations as a standalone type (not as an instantiation of a generic) so unsafety is not propagated from the template arguments to the specialization. This PR propagates this information explicitly.
1 parent 635ea34 commit 62ff0d6

File tree

2 files changed

+49
-8
lines changed

2 files changed

+49
-8
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "clang/AST/DeclCXX.h"
6262
#include "clang/AST/DeclObjCCommon.h"
6363
#include "clang/AST/PrettyPrinter.h"
64+
#include "clang/AST/DeclTemplate.h"
6465
#include "clang/AST/Type.h"
6566
#include "clang/Basic/Specifiers.h"
6667
#include "clang/Basic/TargetInfo.h"
@@ -2178,6 +2179,29 @@ namespace {
21782179
dc);
21792180
Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result;
21802181

2182+
// We have to do this after populating ImportedDecls to avoid importing
2183+
// the same multiple times.
2184+
if (Impl.SwiftContext.LangOpts.hasFeature(Feature::SafeInterop) &&
2185+
Impl.SwiftContext.LangOpts.hasFeature(
2186+
Feature::AllowUnsafeAttribute)) {
2187+
if (const auto *ctsd =
2188+
dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
2189+
for (auto arg : ctsd->getTemplateArgs().asArray()) {
2190+
if (arg.getKind() != clang::TemplateArgument::Type)
2191+
continue;
2192+
auto SwiftType = Impl.importTypeIgnoreIUO(
2193+
arg.getAsType(), ImportTypeKind::Abstract,
2194+
[](Diagnostic &&diag) {}, false, Bridgeability::None,
2195+
ImportTypeAttrs());
2196+
if (SwiftType && SwiftType->isUnsafe()) {
2197+
auto attr = new (Impl.SwiftContext) UnsafeAttr(/*implicit=*/true);
2198+
result->getAttrs().add(attr);
2199+
break;
2200+
}
2201+
}
2202+
}
2203+
}
2204+
21812205
if (recordHasMoveOnlySemantics(decl)) {
21822206
if (decl->isInStdNamespace() && decl->getName() == "promise") {
21832207
// Do not import std::promise.
@@ -3001,14 +3025,14 @@ namespace {
30013025
bool isSpecializationDepthGreaterThan(
30023026
const clang::ClassTemplateSpecializationDecl *decl, unsigned maxDepth) {
30033027
for (auto arg : decl->getTemplateArgs().asArray()) {
3004-
if (arg.getKind() == clang::TemplateArgument::Type) {
3005-
if (auto classSpec =
3006-
dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
3007-
arg.getAsType()->getAsCXXRecordDecl())) {
3008-
if (maxDepth == 0 ||
3009-
isSpecializationDepthGreaterThan(classSpec, maxDepth - 1))
3010-
return true;
3011-
}
3028+
if (arg.getKind() != clang::TemplateArgument::Type)
3029+
continue;
3030+
if (auto classSpec =
3031+
dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
3032+
arg.getAsType()->getAsCXXRecordDecl())) {
3033+
if (maxDepth == 0 ||
3034+
isSpecializationDepthGreaterThan(classSpec, maxDepth - 1))
3035+
return true;
30123036
}
30133037
}
30143038
return false;
@@ -8387,6 +8411,13 @@ static bool importAsUnsafe(ClangImporter::Implementation &impl,
83878411
if (isa<ClassDecl>(MappedDecl))
83888412
return false;
83898413

8414+
// Most STL containers have std::allocator as their default allocator. We need
8415+
// to consider std::allocator safe for the STL containers to be ever
8416+
// considered safe.
8417+
if (decl->isInStdNamespace() && decl->getIdentifier() &&
8418+
decl->getName() == "allocator")
8419+
return false;
8420+
83908421
if (const auto *record = dyn_cast<clang::RecordDecl>(decl))
83918422
return evaluateOrDefault(
83928423
context.evaluator,

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Test {
1818
//--- Inputs/nonescapable.h
1919
#include "swift/bridging"
2020
#include <span>
21+
#include <vector>
2122

2223
struct SWIFT_NONESCAPABLE View {
2324
__attribute__((swift_attr("@lifetime(immortal)")))
@@ -54,6 +55,8 @@ struct MyContainer {
5455

5556
using SpanOfInt = std::span<int>;
5657
using SpanOfIntAlias = SpanOfInt;
58+
using VecOfPtr = std::vector<int*>;
59+
using VecOfInt = std::vector<int>;
5760

5861
//--- test.swift
5962

@@ -85,6 +88,13 @@ func useCfType(x: CFArray) {
8588
func useString(x: std.string) {
8689
}
8790

91+
// expected-warning@+1{{global function 'useVecOfPtr' involves unsafe code; use '@unsafe' to indicate that its use is not memory-safe}}
92+
func useVecOfPtr(x: VecOfPtr) { // expected-note{{reference to unsafe type alias 'VecOfPtr'}}
93+
}
94+
95+
func useVecOfInt(x: VecOfInt) {
96+
}
97+
8898
// expected-warning@+1{{global function 'useCppSpan' involves unsafe code; use '@unsafe' to indicate that its use is not memory-safe}}
8999
func useCppSpan(x: SpanOfInt) { // expected-note{{reference to unsafe type alias 'SpanOfInt'}}
90100
}

0 commit comments

Comments
 (0)