Skip to content

[6.2][cxx-interop] Only swiftify template instantiations behind typealiases #82105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: release/6.2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@
#include "clang/AST/DeclObjCCommon.h"
#include "clang/AST/Expr.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TargetInfo.h"
Expand Down Expand Up @@ -9119,6 +9120,24 @@ namespace {
};
} // namespace

namespace {
// Searches for template instantiations that are not behind type aliases.
// FIXME: make sure the generated code compiles for template
// instantiations that are not behind type aliases.
struct UnaliasedInstantiationVisitor
: clang::RecursiveASTVisitor<UnaliasedInstantiationVisitor> {
bool hasUnaliasedInstantiation = false;

bool TraverseTypedefType(const clang::TypedefType *) { return true; }

bool
VisitTemplateSpecializationType(const clang::TemplateSpecializationType *) {
hasUnaliasedInstantiation = true;
return false;
}
};
} // namespace

// Don't try to transform any Swift types that _SwiftifyImport doesn't know how
// to handle.
static bool SwiftifiableCountedByPointerType(Type swiftType) {
Expand Down Expand Up @@ -9167,6 +9186,13 @@ void ClangImporter::Implementation::swiftify(FuncDecl *MappedDecl) {
if (ClangDecl->getNumParams() != MappedDecl->getParameters()->size())
return;

{
UnaliasedInstantiationVisitor visitor;
visitor.TraverseType(ClangDecl->getType());
if (visitor.hasUnaliasedInstantiation)
return;
}

llvm::SmallString<128> MacroString;
// We only attach the macro if it will produce an overload. Any __counted_by
// will produce an overload, since UnsafeBufferPointer is still an improvement
Expand Down
8 changes: 8 additions & 0 deletions test/Interop/Cxx/stdlib/Inputs/std-span.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,20 @@ inline SpanOfInt MixedFuncWithMutableSafeWrapper7(int * __counted_by(len) p, int
return SpanOfInt(p, len);
}

template <typename X>
struct S {};

struct SpanWithoutTypeAlias {
std::span<const int> bar() [[clang::lifetimebound]];
void foo(std::span<const int> s [[clang::noescape]]);
void otherTemplatedType(ConstSpanOfInt copy [[clang::noescape]], S<int>);
void otherTemplatedType2(ConstSpanOfInt copy [[clang::noescape]], S<int> *);
};

inline void func(ConstSpanOfInt copy [[clang::noescape]]) {}
inline void mutableKeyword(SpanOfInt copy [[clang::noescape]]) {}

inline void spanWithoutTypeAlias(std::span<const int> s [[clang::noescape]]) {}
inline void mutableSpanWithoutTypeAlias(std::span<int> s [[clang::noescape]]) {}

#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SPAN_H
21 changes: 12 additions & 9 deletions test/Interop/Cxx/stdlib/std-span-interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// RUN: %FileCheck %s < %t/interface.swift

// Make sure we trigger typechecking and SIL diagnostics
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence -cxx-interoperability-mode=default -strict-memory-safety -warnings-as-errors -Xcc -std=c++20 %s
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %S/Inputs -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence -cxx-interoperability-mode=default -strict-memory-safety -warnings-as-errors -verify -Xcc -std=c++20 %s

// REQUIRES: swift_feature_SafeInteropWrappers
// REQUIRES: swift_feature_LifetimeDependence
Expand Down Expand Up @@ -34,15 +34,10 @@ import CxxStdlib
// CHECK-NEXT: }
// CHECK: struct SpanWithoutTypeAlias {
// CHECK-NEXT: init()
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
// CHECK-NEXT: @lifetime(borrow self)
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public mutating func bar() -> Span<CInt>
// CHECK-NEXT: mutating func bar() -> std.{{.*}}span<__cxxConst<CInt>, _C{{.*}}_{{.*}}>
// CHECK-NEXT: /// This is an auto-generated wrapper for safer interop
// CHECK-NEXT: @available(visionOS 1.1, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *)
// CHECK-NEXT: @_alwaysEmitIntoClient @_disfavoredOverload public mutating func foo(_ s: Span<CInt>)
// CHECK-NEXT: mutating func foo(_ s: std.{{.*}}span<__cxxConst<CInt>, _C{{.*}}_{{.*}}>)
// CHECK-NEXT: mutating func otherTemplatedType(_ copy: ConstSpanOfInt, _: S<CInt>)
// CHECK-NEXT: mutating func otherTemplatedType2(_ copy: ConstSpanOfInt, _: UnsafeMutablePointer<S<CInt>>!)
// CHECK-NEXT: }

// CHECK: /// This is an auto-generated wrapper for safer interop
Expand Down Expand Up @@ -150,7 +145,7 @@ func callMethodWithSafeWrapper(_ x: inout X, s: Span<CInt>) {
}

func callFooBar(_ x: inout SpanWithoutTypeAlias, _ s: ConstSpanOfInt) {
let _: Span<CInt> = x.bar()
let _: Span<CInt> = x.bar() // expected-error {{cannot convert value of type}}
unsafe x.foo(s)
}

Expand Down Expand Up @@ -242,3 +237,11 @@ func callMixedFuncWithSafeWrapper7(_ p: UnsafeBufferPointer<CInt>) {
func callMutableKeyword(_ span: inout MutableSpan<CInt>) {
mutableKeyword(&span)
}

func callSpanWithoutTypeAlias(_ span: Span<CInt>) {
spanWithoutTypeAlias(span) // expected-error {{cannot convert value of type}}
}

func callMutableSpanWithoutTypeAlias(_ span: consuming MutableSpan<CInt>) {
mutableSpanWithoutTypeAlias(&span) // expected-error {{cannot convert value of type}}
}