Skip to content

Commit b138f22

Browse files
authored
Merge pull request #70044 from apple/egorzhdan/default-arguments
[cxx-interop] Support C++ default arguments
2 parents bfa3707 + 494474b commit b138f22

22 files changed

+794
-10
lines changed

include/swift/AST/ClangModuleLoader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ class ClangModuleLoader : public ModuleLoader {
283283

284284
virtual bool isUnsafeCXXMethod(const FuncDecl *func) = 0;
285285

286+
virtual FuncDecl *getDefaultArgGenerator(const clang::ParmVarDecl *param) = 0;
287+
286288
virtual llvm::Optional<Type>
287289
importFunctionReturnType(const clang::FunctionDecl *clangDecl,
288290
DeclContext *dc) = 0;

include/swift/ClangImporter/ClangImporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@ class ClangImporter final : public ClangModuleLoader {
587587

588588
bool isUnsafeCXXMethod(const FuncDecl *func) override;
589589

590+
FuncDecl *getDefaultArgGenerator(const clang::ParmVarDecl *param) override;
591+
590592
bool isAnnotatedWith(const clang::CXXMethodDecl *method, StringRef attr);
591593

592594
/// Find the lookup table that corresponds to the given Clang module.

lib/ClangImporter/ClangImporter.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4199,14 +4199,20 @@ void ClangModuleUnit::getImportedModulesForLookup(
41994199
void ClangImporter::getMangledName(raw_ostream &os,
42004200
const clang::NamedDecl *clangDecl) const {
42014201
if (!Impl.Mangler)
4202-
Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext());
4202+
Impl.Mangler.reset(getClangASTContext().createMangleContext());
42034203

4204+
return Impl.getMangledName(Impl.Mangler.get(), clangDecl, os);
4205+
}
4206+
4207+
void ClangImporter::Implementation::getMangledName(
4208+
clang::MangleContext *mangler, const clang::NamedDecl *clangDecl,
4209+
raw_ostream &os) {
42044210
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
42054211
auto ctorGlobalDecl =
42064212
clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete);
4207-
Impl.Mangler->mangleCXXName(ctorGlobalDecl, os);
4213+
mangler->mangleCXXName(ctorGlobalDecl, os);
42084214
} else {
4209-
Impl.Mangler->mangleName(clangDecl, os);
4215+
mangler->mangleName(clangDecl, os);
42104216
}
42114217
}
42124218

@@ -7044,6 +7050,14 @@ bool ClangImporter::isAnnotatedWith(const clang::CXXMethodDecl *method,
70447050
});
70457051
}
70467052

7053+
FuncDecl *
7054+
ClangImporter::getDefaultArgGenerator(const clang::ParmVarDecl *param) {
7055+
auto it = Impl.defaultArgGenerators.find(param);
7056+
if (it != Impl.defaultArgGenerators.end())
7057+
return it->second;
7058+
return nullptr;
7059+
}
7060+
70477061
SwiftLookupTable *
70487062
ClangImporter::findLookupTable(const clang::Module *clangModule) {
70497063
return Impl.findLookupTable(clangModule);
@@ -7190,7 +7204,7 @@ static bool hasOwnedValueAttr(const clang::RecordDecl *decl) {
71907204
});
71917205
}
71927206

7193-
static bool hasUnsafeAPIAttr(const clang::Decl *decl) {
7207+
bool importer::hasUnsafeAPIAttr(const clang::Decl *decl) {
71947208
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
71957209
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
71967210
return swiftAttr->getAttribute() == "import_unsafe";
@@ -7256,6 +7270,10 @@ static bool hasPointerInSubobjects(const clang::CXXRecordDecl *decl) {
72567270
return false;
72577271
}
72587272

7273+
bool importer::isViewType(const clang::CXXRecordDecl *decl) {
7274+
return !hasOwnedValueAttr(decl) && hasPointerInSubobjects(decl);
7275+
}
7276+
72597277
static bool copyConstructorIsDefaulted(const clang::CXXRecordDecl *decl) {
72607278
auto ctor = llvm::find_if(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
72617279
return ctor->isCopyConstructor();
@@ -7581,8 +7599,7 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
75817599
// A projection of a view type (such as a string_view) from a self
75827600
// contained parent is a proejction (unsafe).
75837601
if (!anySubobjectsSelfContained(cxxRecordReturnType) &&
7584-
!hasOwnedValueAttr(cxxRecordReturnType) &&
7585-
hasPointerInSubobjects(cxxRecordReturnType)) {
7602+
isViewType(cxxRecordReturnType)) {
75867603
return !parentIsSelfContained;
75877604
}
75887605
}

lib/ClangImporter/ImportType.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "CFTypeInfo.h"
1818
#include "ClangDiagnosticConsumer.h"
1919
#include "ImporterImpl.h"
20+
#include "SwiftDeclSynthesizer.h"
2021
#include "swift/ABI/MetadataValues.h"
2122
#include "swift/AST/ASTContext.h"
2223
#include "swift/AST/Decl.h"
@@ -42,6 +43,7 @@
4243
#include "clang/AST/DeclCXX.h"
4344
#include "clang/AST/DeclObjCCommon.h"
4445
#include "clang/AST/DeclTemplate.h"
46+
#include "clang/AST/ExprCXX.h"
4547
#include "clang/AST/TypeVisitor.h"
4648
#include "clang/Basic/Builtins.h"
4749
#include "clang/Lex/Preprocessor.h"
@@ -2460,6 +2462,51 @@ ClangImporter::Implementation::importParameterType(
24602462
isParamTypeImplicitlyUnwrapped};
24612463
}
24622464

2465+
bool ClangImporter::Implementation::isDefaultArgSafeToImport(
2466+
const clang::ParmVarDecl *param) {
2467+
// If the argument is explicitly marked as import_unsafe, import its default
2468+
// expression regardless of the safety heuristics.
2469+
if (hasUnsafeAPIAttr(param))
2470+
return true;
2471+
2472+
auto functionDecl = cast<clang::FunctionDecl>(param->getDeclContext());
2473+
2474+
if (param->hasUninstantiatedDefaultArg() &&
2475+
!functionDecl->getTemplateInstantiationPattern(
2476+
/*ForDefinition*/ false))
2477+
// HACK: Clang will crash while trying to instantiate this default arg.
2478+
return false;
2479+
2480+
clang::CXXDefaultArgExpr *defaultArgExpr = nullptr;
2481+
// Try to instantiate the default expression.
2482+
auto defaultArgExprResult = getClangSema().BuildCXXDefaultArgExpr(
2483+
clang::SourceLocation(), const_cast<clang::FunctionDecl *>(functionDecl),
2484+
const_cast<clang::ParmVarDecl *>(param));
2485+
// If the default expression can't be instantiated, bail.
2486+
if (!defaultArgExprResult.isUsable())
2487+
return false;
2488+
else
2489+
defaultArgExpr = cast<clang::CXXDefaultArgExpr>(defaultArgExprResult.get());
2490+
2491+
// If the type of this parameter is a view type, do not import the
2492+
// default expression, since we cannot guarantee the lifetime of the
2493+
// pointee value.
2494+
if (auto paramRecordDecl = param->getType()->getAsCXXRecordDecl()) {
2495+
if (isViewType(paramRecordDecl))
2496+
return false;
2497+
}
2498+
// If the parameter is a const reference, check if the expression
2499+
// creates temporaries. Since we import const T& as T in Swift, the
2500+
// value of the default expression will get copied in Swift unlike C++,
2501+
// which might be unexpected and unsafe.
2502+
if (param->getType()->isReferenceType() &&
2503+
param->getType()->getPointeeType().isConstQualified()) {
2504+
if (isa<clang::MaterializeTemporaryExpr>(defaultArgExpr->getExpr()))
2505+
return false;
2506+
}
2507+
return true;
2508+
}
2509+
24632510
static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
24642511
const clang::ParmVarDecl *param,
24652512
const Identifier &name,
@@ -2484,6 +2531,24 @@ static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
24842531
paramInfo->setInterfaceType(swiftParamTy);
24852532
impl->recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped);
24862533

2534+
// Import the default expression for this parameter if possible.
2535+
// Swift doesn't support default values of inout parameters.
2536+
// TODO: support default arguments of constructors
2537+
// (https://github.com/apple/swift/issues/70124)
2538+
if (param->hasDefaultArg() && !isInOut &&
2539+
!isa<clang::CXXConstructorDecl>(param->getDeclContext()) &&
2540+
impl->isCxxInteropCompatVersionAtLeast(
2541+
version::getUpcomingCxxInteropCompatVersion()) &&
2542+
impl->isDefaultArgSafeToImport(param)) {
2543+
SwiftDeclSynthesizer synthesizer(*impl);
2544+
if (CallExpr *defaultArgExpr = synthesizer.makeDefaultArgument(
2545+
param, swiftParamTy, paramInfo->getParameterNameLoc())) {
2546+
paramInfo->setDefaultArgumentKind(DefaultArgumentKind::Normal);
2547+
paramInfo->setDefaultExpr(defaultArgExpr, /*isTypeChecked*/ true);
2548+
paramInfo->setDefaultValueStringRepresentation("cxxDefaultArg");
2549+
}
2550+
}
2551+
24872552
return paramInfo;
24882553
}
24892554

lib/ClangImporter/ImporterImpl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
589589
return Instance.get();
590590
}
591591

592+
/// Writes the mangled name of \p clangDecl to \p os.
593+
void getMangledName(clang::MangleContext *mangler,
594+
const clang::NamedDecl *clangDecl, raw_ostream &os);
595+
592596
/// Whether the C++ interoperability compatibility version is at least
593597
/// 'major'.
594598
///
@@ -664,6 +668,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
664668
clonedBaseMembers;
665669

666670
public:
671+
llvm::DenseMap<const clang::ParmVarDecl*, FuncDecl*> defaultArgGenerators;
672+
673+
bool isDefaultArgSafeToImport(const clang::ParmVarDecl *param);
674+
667675
ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext);
668676

669677
static size_t getImportedBaseMemberDeclArity(const ValueDecl *valueDecl);
@@ -1985,6 +1993,10 @@ inline std::string getPrivateOperatorName(const std::string &OperatorToken) {
19851993
return "None";
19861994
}
19871995

1996+
bool hasUnsafeAPIAttr(const clang::Decl *decl);
1997+
1998+
bool isViewType(const clang::CXXRecordDecl *decl);
1999+
19882000
}
19892001
}
19902002

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "SwiftDeclSynthesizer.h"
14+
#include "clang/AST/Mangle.h"
1415
#include "swift/AST/ASTMangler.h"
1516
#include "swift/AST/Builtins.h"
1617
#include "swift/AST/Expr.h"
@@ -2158,3 +2159,120 @@ SwiftDeclSynthesizer::makeComputedPropertyFromCXXMethods(FuncDecl *getter,
21582159

21592160
return result;
21602161
}
2162+
2163+
static std::pair<BraceStmt *, bool>
2164+
synthesizeDefaultArgumentBody(AbstractFunctionDecl *afd, void *context) {
2165+
auto funcDecl = cast<FuncDecl>(afd);
2166+
auto clangParam = static_cast<const clang::ParmVarDecl *>(context);
2167+
auto clangFuncDecl = cast<clang::FunctionDecl>(clangParam->getDeclContext());
2168+
2169+
ASTContext &ctx = funcDecl->getASTContext();
2170+
clang::ASTContext &clangCtx = clangParam->getASTContext();
2171+
clang::Sema &clangSema = ctx.getClangModuleLoader()->getClangSema();
2172+
2173+
auto clangDeclName = clang::DeclarationName(
2174+
&clangCtx.Idents.get(("__cxx" + funcDecl->getNameStr()).str()));
2175+
auto clangDeclContext = clangCtx.getTranslationUnitDecl();
2176+
2177+
// The following also instantiates the default argument if needed.
2178+
auto defaultArgCallExpr = clangSema.BuildCXXDefaultArgExpr(
2179+
clang::SourceLocation(), const_cast<clang::FunctionDecl *>(clangFuncDecl),
2180+
const_cast<clang::ParmVarDecl *>(clangParam));
2181+
if (!defaultArgCallExpr.isUsable())
2182+
return {nullptr, /*isTypeChecked=*/true};
2183+
2184+
// The following requires the default argument to be instantiated.
2185+
clang::QualType clangParamTy = clangParam->getDefaultArg()->getType();
2186+
clang::QualType funcTy = clangCtx.getFunctionType(
2187+
clangParamTy, {}, clang::FunctionProtoType::ExtProtoInfo());
2188+
2189+
// Synthesize `return {default expr};`.
2190+
auto defaultArgReturnStmt = clang::ReturnStmt::Create(
2191+
clangCtx, clang::SourceLocation(), defaultArgCallExpr.get(), nullptr);
2192+
2193+
// Synthesize `ParamTy __cxx__defaultArg_XYZ() { return {default expr}; }`.
2194+
auto defaultArgFuncDecl = clang::FunctionDecl::Create(
2195+
clangCtx, clangDeclContext, clang::SourceLocation(),
2196+
clang::SourceLocation(), clangDeclName, funcTy,
2197+
clangCtx.getTrivialTypeSourceInfo(clangParamTy),
2198+
clang::StorageClass::SC_Static);
2199+
defaultArgFuncDecl->setImplicit();
2200+
defaultArgFuncDecl->setImplicitlyInline();
2201+
defaultArgFuncDecl->setAccess(clang::AccessSpecifier::AS_public);
2202+
defaultArgFuncDecl->setBody(defaultArgReturnStmt);
2203+
2204+
// Import `func __cxx__defaultArg_XYZ() -> ParamTY` into Swift.
2205+
auto defaultArgGenerator = dyn_cast_or_null<FuncDecl>(
2206+
ctx.getClangModuleLoader()->importDeclDirectly(defaultArgFuncDecl));
2207+
if (!defaultArgGenerator)
2208+
return {nullptr, /*isTypeChecked=*/true};
2209+
2210+
auto defaultArgGeneratorRef = new (ctx) DeclRefExpr(
2211+
ConcreteDeclRef(defaultArgGenerator), DeclNameLoc(), /*Implicit=*/true);
2212+
defaultArgGeneratorRef->setType(defaultArgGenerator->getInterfaceType());
2213+
2214+
// Synthesize a call to `__cxx__defaultArg_XYZ()`.
2215+
auto initCall = CallExpr::createImplicit(
2216+
ctx, defaultArgGeneratorRef, ArgumentList::createImplicit(ctx, {}));
2217+
initCall->setType(defaultArgGenerator->getResultInterfaceType());
2218+
initCall->setThrows(nullptr);
2219+
2220+
// Synthesize `return __cxx__defaultArg_XYZ()`.
2221+
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), initCall,
2222+
/*implicit=*/true);
2223+
2224+
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
2225+
/*implicit=*/true);
2226+
return {body, /*isTypeChecked=*/true};
2227+
}
2228+
2229+
CallExpr *
2230+
SwiftDeclSynthesizer::makeDefaultArgument(const clang::ParmVarDecl *param,
2231+
const swift::Type &swiftParamTy,
2232+
SourceLoc paramLoc) {
2233+
assert(param->hasDefaultArg() && "must have a C++ default argument");
2234+
if (!param->getIdentifier())
2235+
// Work around an assertion failure in CXXNameMangler::mangleUnqualifiedName
2236+
// when mangling std::__fs::filesystem::path::format.
2237+
return nullptr;
2238+
2239+
ASTContext &ctx = ImporterImpl.SwiftContext;
2240+
clang::ASTContext &clangCtx = param->getASTContext();
2241+
auto clangFunc =
2242+
cast<clang::FunctionDecl>(param->getParentFunctionOrMethod());
2243+
if (isa<clang::CXXConstructorDecl>(clangFunc))
2244+
// TODO: support default arguments of constructors
2245+
// (https://github.com/apple/swift/issues/70124)
2246+
return nullptr;
2247+
2248+
std::string s;
2249+
llvm::raw_string_ostream os(s);
2250+
std::unique_ptr<clang::ItaniumMangleContext> mangler{
2251+
clang::ItaniumMangleContext::create(clangCtx, clangCtx.getDiagnostics())};
2252+
os << "__defaultArg_" << param->getFunctionScopeIndex() << "_";
2253+
ImporterImpl.getMangledName(mangler.get(), clangFunc, os);
2254+
2255+
// Synthesize `func __defaultArg_XYZ() -> ParamTy { ... }`.
2256+
DeclName funcName(ctx, DeclBaseName(ctx.getIdentifier(s)),
2257+
ParameterList::createEmpty(ctx));
2258+
auto funcDecl = FuncDecl::createImplicit(
2259+
ctx, StaticSpellingKind::None, funcName, paramLoc, false, false, Type(),
2260+
{}, ParameterList::createEmpty(ctx), swiftParamTy,
2261+
ImporterImpl.ImportedHeaderUnit);
2262+
funcDecl->setBodySynthesizer(synthesizeDefaultArgumentBody, (void *)param);
2263+
funcDecl->setAccess(AccessLevel::Public);
2264+
2265+
ImporterImpl.defaultArgGenerators[param] = funcDecl;
2266+
2267+
auto declRefExpr = new (ctx)
2268+
DeclRefExpr(ConcreteDeclRef(funcDecl), DeclNameLoc(), /*Implicit*/ true);
2269+
declRefExpr->setType(funcDecl->getInterfaceType());
2270+
declRefExpr->setFunctionRefKind(FunctionRefKind::SingleApply);
2271+
2272+
auto callExpr = CallExpr::createImplicit(
2273+
ctx, declRefExpr, ArgumentList::forImplicitUnlabeled(ctx, {}));
2274+
callExpr->setType(funcDecl->getResultInterfaceType());
2275+
callExpr->setThrows(nullptr);
2276+
2277+
return callExpr;
2278+
}

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
namespace swift {
2020

21+
class CallExpr;
22+
2123
enum class MakeStructRawValuedFlags {
2224
/// whether to also create an unlabeled init
2325
MakeUnlabeledValueInit = 0x01,
@@ -294,6 +296,10 @@ class SwiftDeclSynthesizer {
294296
VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter,
295297
FuncDecl *setter);
296298

299+
CallExpr *makeDefaultArgument(const clang::ParmVarDecl *param,
300+
const swift::Type &swiftParamTy,
301+
SourceLoc paramLoc);
302+
297303
private:
298304
Type getConstantLiteralType(Type type, ConstantConvertKind convertKind);
299305
};

0 commit comments

Comments
 (0)