Files
swift-mirror/lib/SIL/IR/Bridging.cpp
Gabor Horvath 96ee6eaf18 [cxx-interop] Implicitly bridge annotated smart pointers to reference types
This PR introduces implicit bridging from annotated smart pointers to
reference counted objects to Swift foreign reference types. The bridging
only happens when these smart pointers are passed around by value. The
frontend only sees the Swift foreign reference types in the signature
and the actual bridging happens during SILGen when we look at the
original clang types and note that they differ significantly from the
native types.

The bridging of parameters did not quite fit into the existing structure
of bridging conversions as smart pointers are non-trivial types passed
around as addresses and existing bridging conversions were implemented
for types passed directly.

rdar://156521316
2026-01-28 12:34:21 +00:00

283 lines
11 KiB
C++

//===--- Bridging.cpp - Bridging imported Clang types to Swift ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines routines relating to bridging Swift types to C types,
// working in concert with the Clang importer.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "libsil"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/Module.h"
#include "swift/AST/ModuleLoader.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILType.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
using namespace swift;
using namespace swift::Lowering;
CanType TypeConverter::getLoweredTypeOfGlobal(VarDecl *var) {
AbstractionPattern origType = getAbstractionPattern(var);
assert(!origType.isTypeParameter());
return getLoweredRValueType(TypeExpansionContext::minimal(), origType,
origType.getType());
}
AnyFunctionType::Param
TypeConverter::getBridgedParam(SILFunctionTypeRepresentation rep,
AbstractionPattern pattern,
AnyFunctionType::Param param,
Bridgeability bridging) {
assert(!param.getParameterFlags().isVariadic());
auto bridged = getLoweredBridgedType(pattern, param.getPlainType(), bridging,
rep, TypeConverter::ForArgument);
if (!bridged) {
Context.Diags.diagnose(SourceLoc(), diag::could_not_find_bridge_type,
param.getPlainType());
llvm::report_fatal_error("unable to set up the ObjC bridge!");
}
return param.withType(bridged->getCanonicalType());
}
void TypeConverter::
getBridgedParams(SILFunctionTypeRepresentation rep,
AbstractionPattern pattern,
ArrayRef<AnyFunctionType::Param> params,
SmallVectorImpl<AnyFunctionType::Param> &bridgedParams,
Bridgeability bridging) {
for (unsigned i : indices(params)) {
auto paramPattern = pattern.getFunctionParamType(i);
auto bridgedParam = getBridgedParam(rep, paramPattern, params[i], bridging);
bridgedParams.push_back(bridgedParam);
}
}
/// Bridge a result type.
CanType TypeConverter::getBridgedResultType(SILFunctionTypeRepresentation rep,
AbstractionPattern pattern,
CanType result,
Bridgeability bridging,
bool suppressOptional) {
auto loweredType =
getLoweredBridgedType(pattern, result, bridging, rep,
suppressOptional
? TypeConverter::ForNonOptionalResult
: TypeConverter::ForResult);
if (!loweredType) {
Context.Diags.diagnose(SourceLoc(), diag::could_not_find_bridge_type,
result);
llvm::report_fatal_error("unable to set up the ObjC bridge!");
}
return loweredType->getCanonicalType();
}
clang::CXXRecordDecl *Lowering::getBridgedSmartPtr(AbstractionPattern pattern) {
if (!pattern.isClangType())
return nullptr;
auto ty = pattern.getClangType();
if (auto rd = ty->getAsCXXRecordDecl())
for (auto attr : rd->getAttrs())
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
if (swiftAttr->getAttribute().starts_with("@_refCountedPtr"))
return rd;
return nullptr;
}
Type TypeConverter::getLoweredBridgedType(AbstractionPattern pattern,
Type t,
Bridgeability bridging,
SILFunctionTypeRepresentation rep,
BridgedTypePurpose purpose) {
switch (rep) {
case SILFunctionTypeRepresentation::Thick:
case SILFunctionTypeRepresentation::Thin:
case SILFunctionTypeRepresentation::Method:
case SILFunctionTypeRepresentation::WitnessMethod:
case SILFunctionTypeRepresentation::Closure:
case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
case SILFunctionTypeRepresentation::KeyPathAccessorHash:
// No bridging needed for native CCs.
return t;
case SILFunctionTypeRepresentation::CFunctionPointer:
case SILFunctionTypeRepresentation::ObjCMethod:
case SILFunctionTypeRepresentation::Block:
case SILFunctionTypeRepresentation::CXXMethod:
// Map native types back to bridged types.
// Look through optional types.
if (auto valueTy = t->getOptionalObjectType()) {
pattern = pattern.getOptionalObjectType();
auto ty = getLoweredCBridgedType(pattern, valueTy, bridging, rep,
BridgedTypePurpose::ForNonOptionalResult);
return ty && !getBridgedSmartPtr(pattern) ? OptionalType::get(ty) : ty;
}
return getLoweredCBridgedType(pattern, t, bridging, rep, purpose);
}
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
}
Type TypeConverter::getLoweredCBridgedType(AbstractionPattern pattern,
Type t, Bridgeability bridging,
SILFunctionTypeRepresentation rep,
BridgedTypePurpose purpose) {
auto clangTy = pattern.isClangType() ? pattern.getClangType() : nullptr;
// Bridge Bool back to ObjC bool, unless the original Clang type was _Bool
// or the Darwin Boolean type.
auto nativeBoolTy = getBoolType();
if (nativeBoolTy && t->isEqual(nativeBoolTy)) {
// If we have a Clang type that was imported as Bool, it had better be
// one of a small set of types.
if (clangTy && clangTy->isBuiltinType()) {
auto builtinTy = clangTy->castAs<clang::BuiltinType>();
if (builtinTy->getKind() == clang::BuiltinType::Bool)
return t;
if (builtinTy->getKind() == clang::BuiltinType::UChar)
return getDarwinBooleanType();
if (builtinTy->getKind() == clang::BuiltinType::Int)
return getWindowsBoolType();
assert(builtinTy->getKind() == clang::BuiltinType::SChar);
return getObjCBoolType();
}
// Otherwise, always assume ObjC methods should use ObjCBool.
if (bridging != Bridgeability::None &&
rep == SILFunctionTypeRepresentation::ObjCMethod)
return getObjCBoolType();
return t;
}
if (t->isForeignReferenceType()) {
if (auto rd = getBridgedSmartPtr(pattern)) {
return cast<TypeDecl>(
Context.getClangModuleLoader()->lookupImportedDecl(rd))
->getDeclaredInterfaceType();
}
}
// Class metatypes bridge to ObjC metatypes.
if (auto metaTy = t->getAs<MetatypeType>()) {
if (metaTy->getInstanceType()->getClassOrBoundGenericClass()
// Self argument of an ObjC protocol
|| metaTy->getInstanceType()->is<GenericTypeParamType>()) {
return MetatypeType::get(metaTy->getInstanceType(),
MetatypeRepresentation::ObjC);
}
}
// ObjC-compatible existential metatypes.
if (auto metaTy = t->getAs<ExistentialMetatypeType>()) {
if (metaTy->getInstanceType()->isObjCExistentialType()) {
return ExistentialMetatypeType::get(metaTy->getInstanceType(),
MetatypeRepresentation::ObjC);
}
}
// Existentials consisting of only marker protocols can bridge to
// `AnyObject` (`id` in ObjC).
if (t->isMarkerExistential())
return Context.getAnyObjectType();
if (auto funTy = t->getAs<FunctionType>()) {
switch (funTy->getExtInfo().getSILRepresentation()) {
case SILFunctionType::Representation::Block:
case SILFunctionType::Representation::CFunctionPointer:
case SILFunctionType::Representation::Thin:
case SILFunctionType::Representation::Method:
case SILFunctionType::Representation::ObjCMethod:
case SILFunctionTypeRepresentation::CXXMethod:
case SILFunctionType::Representation::WitnessMethod:
case SILFunctionType::Representation::Closure:
case SILFunctionType::Representation::KeyPathAccessorGetter:
case SILFunctionType::Representation::KeyPathAccessorSetter:
case SILFunctionType::Representation::KeyPathAccessorEquals:
case SILFunctionType::Representation::KeyPathAccessorHash:
return t;
case SILFunctionType::Representation::Thick: {
// Thick functions (TODO: conditionally) get bridged to blocks.
// This bridging is more powerful than usual block bridging, however,
// so we use the ObjCMethod representation.
SmallVector<AnyFunctionType::Param, 8> newParams;
getBridgedParams(SILFunctionType::Representation::ObjCMethod,
pattern, funTy->getParams(), newParams, bridging);
Type newResult =
getBridgedResultType(SILFunctionType::Representation::ObjCMethod,
pattern.getFunctionResultType(),
funTy->getResult()->getCanonicalType(),
bridging,
/*non-optional*/false);
auto clangType = Context.getClangFunctionType(
newParams, {newResult}, FunctionTypeRepresentation::Block);
return FunctionType::get(
newParams, newResult,
funTy->getExtInfo()
.intoBuilder()
.withRepresentation(FunctionType::Representation::Block)
.withClangFunctionType(clangType)
.build());
}
}
}
auto foreignRepresentation =
t->getForeignRepresentableIn(ForeignLanguage::ObjectiveC, &M);
switch (foreignRepresentation.first) {
case ForeignRepresentableKind::None:
case ForeignRepresentableKind::Trivial:
case ForeignRepresentableKind::Object:
return t;
case ForeignRepresentableKind::Bridged:
case ForeignRepresentableKind::StaticBridged: {
auto conformance = foreignRepresentation.second;
assert(conformance && "Missing conformance?");
Type bridgedTy =
ProtocolConformanceRef(conformance).getTypeWitnessByName(
M.getASTContext().Id_ObjectiveCType);
if (purpose == BridgedTypePurpose::ForResult && clangTy)
bridgedTy = OptionalType::get(bridgedTy);
return bridgedTy;
}
case ForeignRepresentableKind::BridgedError: {
auto nsErrorTy = M.getASTContext().getNSErrorType();
assert(nsErrorTy && "Cannot bridge when NSError isn't available");
return nsErrorTy;
}
}
return t;
}