Files
swift-mirror/lib/ClangImporter/SwiftDeclSynthesizer.cpp
Hamish Knight 73710e3eef [AST] Introduce Decl::addAttribute
Introduce a convenience entrypoint that also calls `attachToDecl` on
the attribute, and migrate all existing uses of `getAttrs().add` onto
it.
2025-10-16 11:21:54 +01:00

3137 lines
123 KiB
C++

//===--- DeclSynthesizer.cpp - Synthesize helper Swift decls --------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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
//
//===----------------------------------------------------------------------===//
#include "SwiftDeclSynthesizer.h"
#include "CXXMethodBridging.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Attr.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/Builtins.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsClangImporter.h"
#include "swift/AST/Expr.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Assertions.h"
#include "swift/ClangImporter/ClangImporterRequests.h"
#include "clang/AST/Mangle.h"
#include "clang/Sema/DelayedDiagnostic.h"
using namespace swift;
using namespace importer;
static Argument createSelfArg(AccessorDecl *accessorDecl) {
ASTContext &ctx = accessorDecl->getASTContext();
auto selfDecl = accessorDecl->getImplicitSelfDecl();
auto selfRefExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
if (!accessorDecl->isMutating()) {
selfRefExpr->setType(selfDecl->getInterfaceType());
return Argument::unlabeled(selfRefExpr);
}
selfRefExpr->setType(LValueType::get(selfDecl->getInterfaceType()));
return Argument::implicitInOut(ctx, selfRefExpr);
}
static CallExpr *createAccessorImplCallExpr(FuncDecl *accessorImpl,
Argument selfArg,
ArrayRef<Expr *> keyRefExprs) {
ASTContext &ctx = accessorImpl->getASTContext();
auto accessorImplExpr =
new (ctx) DeclRefExpr(ConcreteDeclRef(accessorImpl), DeclNameLoc(),
/*Implicit*/ true);
accessorImplExpr->setType(accessorImpl->getInterfaceType());
auto accessorImplDotCallExpr =
DotSyntaxCallExpr::create(ctx, accessorImplExpr, SourceLoc(), selfArg);
accessorImplDotCallExpr->setType(accessorImpl->getMethodInterfaceType());
accessorImplDotCallExpr->setThrows(nullptr);
ArgumentList *argList = ArgumentList::forImplicitUnlabeled(ctx, keyRefExprs);
auto *accessorImplCallExpr =
CallExpr::createImplicit(ctx, accessorImplDotCallExpr, argList);
accessorImplCallExpr->setType(accessorImpl->getResultInterfaceType());
accessorImplCallExpr->setThrows(nullptr);
return accessorImplCallExpr;
}
static DeclRefExpr *createParamRefExpr(AccessorDecl *accessorDecl,
unsigned index) {
ASTContext &ctx = accessorDecl->getASTContext();
auto paramDecl = accessorDecl->getParameters()->get(index);
auto paramRefExpr = new (ctx) DeclRefExpr(paramDecl, DeclNameLoc(),
/*Implicit*/ true);
paramRefExpr->setType(paramDecl->getTypeInContext());
return paramRefExpr;
}
static AccessorDecl *makeFieldGetterDecl(ClangImporter::Implementation &Impl,
NominalTypeDecl *importedDecl,
VarDecl *importedFieldDecl,
ClangNode clangNode = ClangNode()) {
auto &C = Impl.SwiftContext;
auto *params = ParameterList::createEmpty(C);
auto getterType = importedFieldDecl->getInterfaceType();
auto getterDecl = AccessorDecl::create(
C,
/*declLoc=*/importedFieldDecl->getLoc(),
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, importedFieldDecl,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
params, getterType, importedDecl, clangNode);
getterDecl->setAccess(importedFieldDecl->getFormalAccess());
getterDecl->setIsObjC(false);
getterDecl->setIsDynamic(false);
return getterDecl;
}
static AccessorDecl *makeFieldSetterDecl(ClangImporter::Implementation &Impl,
NominalTypeDecl *importedDecl,
VarDecl *importedFieldDecl,
ClangNode clangNode = ClangNode()) {
auto &C = Impl.SwiftContext;
auto newValueDecl = new (C) ParamDecl(SourceLoc(), SourceLoc(), Identifier(),
SourceLoc(), C.Id_value, importedDecl);
newValueDecl->setSpecifier(ParamSpecifier::Default);
newValueDecl->setInterfaceType(importedFieldDecl->getInterfaceType());
auto *params = ParameterList::createWithoutLoc(newValueDecl);
auto voidTy = TupleType::getEmpty(C);
auto setterDecl = AccessorDecl::create(
C,
/*declLoc=*/SourceLoc(),
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Set, importedFieldDecl,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
params, voidTy, importedDecl, clangNode);
setterDecl->setIsObjC(false);
setterDecl->setIsDynamic(false);
if (!isa<ClassDecl>(importedDecl))
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
setterDecl->setAccess(importedFieldDecl->getSetterFormalAccess());
return setterDecl;
}
std::pair<VarDecl *, PatternBindingDecl *>
SwiftDeclSynthesizer::createVarWithPattern(DeclContext *dc, Identifier name,
Type ty,
VarDecl::Introducer introducer,
bool isImplicit, AccessLevel access,
AccessLevel setterAccess) {
ASTContext &ctx = dc->getASTContext();
// Create a variable to store the underlying value.
auto var = new (ctx) VarDecl(
/*IsStatic*/ false, introducer, SourceLoc(), name, dc);
if (isImplicit)
var->setImplicit();
var->setInterfaceType(ty);
var->setAccess(access);
var->setSetterAccess(setterAccess);
// Create a pattern binding to describe the variable.
Pattern *varPattern = createTypedNamedPattern(var);
auto *patternBinding = PatternBindingDecl::create(
ctx, /*StaticLoc*/ SourceLoc(), StaticSpellingKind::None,
/*VarLoc*/ SourceLoc(), varPattern, /*EqualLoc*/ SourceLoc(),
/*InitExpr*/ nullptr, dc);
if (isImplicit)
patternBinding->setImplicit();
return {var, patternBinding};
}
Pattern *SwiftDeclSynthesizer::createTypedNamedPattern(VarDecl *decl) {
ASTContext &Ctx = decl->getASTContext();
Type ty = decl->getTypeInContext();
Pattern *P = new (Ctx) NamedPattern(decl);
P->setType(ty);
P->setImplicit();
return TypedPattern::createImplicit(Ctx, P, ty);
}
namespace {
using ConstantGetterBodyContextData =
llvm::PointerIntPair<Expr *, 2, ConstantConvertKind>;
}
Type SwiftDeclSynthesizer::getConstantLiteralType(
Type type, ConstantConvertKind convertKind) {
switch (convertKind) {
case ConstantConvertKind::Construction:
case ConstantConvertKind::ConstructionWithUnwrap: {
auto found = ImporterImpl.RawTypes.find(type->getAnyNominal());
assert(found != ImporterImpl.RawTypes.end());
return found->second;
}
default:
return type;
}
}
// This method is exposed on SwiftDeclSynthesizer to keep code that accesses
// RawTypes together.
bool SwiftDeclSynthesizer::isCGFloat(Type type) {
auto found = ImporterImpl.RawTypes.find(type->getAnyNominal());
if (found == ImporterImpl.RawTypes.end()) {
return false;
}
Type importTy = found->second;
return importTy->isCGFloat();
}
// This method is exposed on SwiftDeclSynthesizer to keep code that accesses
// RawTypes together.
bool SwiftDeclSynthesizer::isObjCBool(Type type) {
auto found = ImporterImpl.RawTypes.find(type->getAnyNominal());
if (found == ImporterImpl.RawTypes.end()) {
return false;
}
Type importTy = found->second;
return importTy->isObjCBool();
}
bool SwiftDeclSynthesizer::isUnicodeScalar(Type type) {
auto found = ImporterImpl.RawTypes.find(type->getAnyNominal());
if (found == ImporterImpl.RawTypes.end()) {
return false;
}
Type importTy = found->second;
return importTy->isUnicodeScalar();
}
ValueDecl *SwiftDeclSynthesizer::createConstant(Identifier name,
DeclContext *dc, Type type,
const clang::APValue &value,
ConstantConvertKind convertKind,
bool isStatic, ClangNode ClangN,
AccessLevel access) {
auto &context = ImporterImpl.SwiftContext;
// Create the integer literal value.
Expr *expr = nullptr;
switch (value.getKind()) {
case clang::APValue::AddrLabelDiff:
case clang::APValue::Array:
case clang::APValue::ComplexFloat:
case clang::APValue::ComplexInt:
case clang::APValue::FixedPoint:
case clang::APValue::Indeterminate:
case clang::APValue::LValue:
case clang::APValue::MemberPointer:
case clang::APValue::None:
case clang::APValue::Struct:
case clang::APValue::Union:
case clang::APValue::Vector:
llvm_unreachable("Unhandled APValue kind");
case clang::APValue::Float:
case clang::APValue::Int: {
// Print the value.
llvm::SmallString<16> printedValueBuf;
if (value.getKind() == clang::APValue::Int) {
value.getInt().toString(printedValueBuf);
} else {
assert(value.getFloat().isFinite() && "can't handle infinities or NaNs");
value.getFloat().toString(printedValueBuf);
}
StringRef printedValue = printedValueBuf.str();
// If this was a negative number, record that and strip off the '-'.
bool isNegative = printedValue.front() == '-';
if (isNegative)
printedValue = printedValue.drop_front();
auto literalType = getConstantLiteralType(type, convertKind);
// Create the expression node.
StringRef printedValueCopy(context.AllocateCopy(printedValue));
if (value.getKind() == clang::APValue::Int) {
bool isBool = type->getCanonicalType()->isBool();
// Check if "type" is a C++ enum with an underlying type of "bool".
if (!isBool && type->getStructOrBoundGenericStruct() &&
type->getStructOrBoundGenericStruct()->getClangDecl()) {
if (auto enumDecl = dyn_cast<clang::EnumDecl>(
type->getStructOrBoundGenericStruct()->getClangDecl())) {
isBool = enumDecl->getIntegerType()->isBooleanType();
}
}
if (isBool) {
auto *boolExpr = new (context)
BooleanLiteralExpr(value.getInt().getBoolValue(), SourceLoc(),
/*Implicit=*/true);
boolExpr->setBuiltinInitializer(context.getBoolBuiltinInitDecl());
boolExpr->setType(literalType);
expr = boolExpr;
} else {
auto *intExpr =
new (context) IntegerLiteralExpr(printedValueCopy, SourceLoc(),
/*Implicit=*/true);
auto *intDecl = literalType->getAnyNominal();
intExpr->setBuiltinInitializer(context.getIntBuiltinInitDecl(intDecl));
intExpr->setType(literalType);
expr = intExpr;
}
} else {
auto *floatExpr =
new (context) FloatLiteralExpr(printedValueCopy, SourceLoc(),
/*Implicit=*/true);
auto maxFloatTypeDecl = context.get_MaxBuiltinFloatTypeDecl();
floatExpr->setBuiltinType(maxFloatTypeDecl->getUnderlyingType());
auto *floatDecl = literalType->getAnyNominal();
floatExpr->setBuiltinInitializer(
context.getFloatBuiltinInitDecl(floatDecl));
floatExpr->setType(literalType);
expr = floatExpr;
}
if (isNegative)
cast<NumberLiteralExpr>(expr)->setNegative(SourceLoc());
break;
}
}
assert(expr);
return createConstant(name, dc, type, expr, convertKind, isStatic, ClangN,
access);
}
ValueDecl *SwiftDeclSynthesizer::createConstant(Identifier name,
DeclContext *dc, Type type,
StringRef value,
ConstantConvertKind convertKind,
bool isStatic, ClangNode ClangN,
AccessLevel access) {
ASTContext &ctx = ImporterImpl.SwiftContext;
auto expr = new (ctx) StringLiteralExpr(value, SourceRange());
auto literalType = getConstantLiteralType(type, convertKind);
auto *stringDecl = literalType->getAnyNominal();
expr->setBuiltinInitializer(ctx.getStringBuiltinInitDecl(stringDecl));
expr->setType(literalType);
return createConstant(name, dc, type, expr, convertKind, isStatic, ClangN,
access);
}
/// Synthesizer callback to synthesize the getter for a constant value.
static std::pair<BraceStmt *, bool>
synthesizeConstantGetterBody(AbstractFunctionDecl *afd, void *voidContext) {
ASTContext &ctx = afd->getASTContext();
auto func = cast<AccessorDecl>(afd);
VarDecl *constantVar = cast<VarDecl>(func->getStorage());
Type type = func->mapTypeIntoContext(constantVar->getValueInterfaceType());
auto contextData =
ConstantGetterBodyContextData::getFromOpaqueValue(voidContext);
Expr *expr = contextData.getPointer();
ConstantConvertKind convertKind = contextData.getInt();
// If we need a conversion, add one now.
switch (convertKind) {
case ConstantConvertKind::None:
break;
case ConstantConvertKind::Construction:
case ConstantConvertKind::ConstructionWithUnwrap: {
auto typeRef = TypeExpr::createImplicit(type, ctx);
// Reference init(rawValue: T)
ConstructorDecl *init = nullptr;
DeclName initName =
DeclName(ctx, DeclBaseName::createConstructor(), {ctx.Id_rawValue});
auto nominal = type->getAnyNominal();
for (auto found : nominal->lookupDirect(initName)) {
init = dyn_cast<ConstructorDecl>(found);
if (init && init->getDeclContext() == nominal)
break;
}
assert(init && "did not find init(rawValue:)");
auto initTy = init->getInterfaceType()->removeArgumentLabels(1);
auto declRef = new (ctx) DeclRefExpr(init, DeclNameLoc(), /*Implicit=*/true,
AccessSemantics::Ordinary, initTy);
// (Self) -> ...
initTy = initTy->castTo<FunctionType>()->getResult();
auto initRef = DotSyntaxCallExpr::create(
ctx, declRef, SourceLoc(), Argument::unlabeled(typeRef), initTy);
initRef->setThrows(nullptr);
// (rawValue: T) -> ...
initTy = initTy->castTo<FunctionType>()->getResult();
auto *argList = ArgumentList::forImplicitSingle(ctx, ctx.Id_rawValue, expr);
auto initCall = CallExpr::createImplicit(ctx, initRef, argList);
initCall->setType(initTy);
initCall->setThrows(nullptr);
expr = initCall;
// Force unwrap if our init(rawValue:) is failable, which is currently
// the case with enums.
if (convertKind == ConstantConvertKind::ConstructionWithUnwrap) {
initTy = initTy->getOptionalObjectType();
expr = new (ctx) ForceValueExpr(expr, SourceLoc());
expr->setType(initTy);
}
assert(initTy->isEqual(type));
break;
}
}
// Create the return statement.
auto ret = ReturnStmt::createImplicit(ctx, expr);
return {BraceStmt::create(ctx, SourceLoc(), ASTNode(ret), SourceLoc()),
/*isTypeChecked=*/true};
}
ValueDecl *SwiftDeclSynthesizer::createConstant(Identifier name,
DeclContext *dc, Type type,
Expr *valueExpr,
ConstantConvertKind convertKind,
bool isStatic, ClangNode ClangN,
AccessLevel access) {
auto &C = ImporterImpl.SwiftContext;
VarDecl *var = nullptr;
if (ClangN) {
var = ImporterImpl.createDeclWithClangNode<VarDecl>(
ClangN, access,
/*IsStatic*/ isStatic, VarDecl::Introducer::Var, SourceLoc(), name, dc);
} else {
var = new (C) VarDecl(
/*IsStatic*/ isStatic, VarDecl::Introducer::Var, SourceLoc(), name, dc);
}
var->setInterfaceType(type);
var->setIsObjC(false);
var->setIsDynamic(false);
auto *params = ParameterList::createEmpty(C);
// Create the getter function declaration.
auto func = AccessorDecl::create(
C,
/*declLoc=*/SourceLoc(),
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, var,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
params, type, dc);
func->setStatic(isStatic);
func->setIsObjC(false);
func->setIsDynamic(false);
func->setBodySynthesizer(
synthesizeConstantGetterBody,
ConstantGetterBodyContextData(valueExpr, convertKind).getOpaqueValue());
// Mark the function transparent so that we inline it away completely.
func->addAttribute(new (C) TransparentAttr(/*implicit*/ true));
var->addAttribute(NonisolatedAttr::createImplicit(C));
// Set the function up as the getter.
ImporterImpl.makeComputed(var, func, nullptr);
return var;
}
// MARK: Struct default initializers
/// Synthesize the body for an struct default initializer.
static std::pair<BraceStmt *, bool>
synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd,
void *context) {
auto constructor = cast<ConstructorDecl>(afd);
ASTContext &ctx = constructor->getASTContext();
auto structDecl = static_cast<StructDecl *>(context);
// Use a builtin to produce a zero initializer, and assign it to self.
// Construct the left-hand reference to self.
auto *selfDecl = constructor->getImplicitSelfDecl();
Expr *lhs = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), /*Implicit=*/true);
auto selfType = structDecl->getDeclaredInterfaceType();
lhs->setType(LValueType::get(selfType));
auto emptyTuple = TupleType::getEmpty(ctx);
// Construct the right-hand call to Builtin.zeroInitializer.
Identifier zeroInitID = ctx.getIdentifier("zeroInitializer");
auto zeroInitializerFunc =
cast<FuncDecl>(getBuiltinValueDecl(ctx, zeroInitID));
SubstitutionMap subMap = SubstitutionMap::get(
zeroInitializerFunc->getGenericSignature(), llvm::ArrayRef(selfType),
LookUpConformanceInModule());
ConcreteDeclRef concreteDeclRef(zeroInitializerFunc, subMap);
auto zeroInitializerRef =
new (ctx) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
zeroInitializerRef->setType(FunctionType::get({}, selfType, info));
auto call = CallExpr::createImplicitEmpty(ctx, zeroInitializerRef);
call->setType(selfType);
call->setThrows(nullptr);
auto assign = new (ctx) AssignExpr(lhs, SourceLoc(), call, /*implicit*/ true);
assign->setType(emptyTuple);
auto *ret = ReturnStmt::createImplicit(ctx, /*expr*/ nullptr);
// Create the function body.
auto body = BraceStmt::create(ctx, SourceLoc(), {assign, ret}, SourceLoc());
return {body, /*isTypeChecked*/ true};
}
ConstructorDecl *
SwiftDeclSynthesizer::createDefaultConstructor(NominalTypeDecl *structDecl) {
auto &context = ImporterImpl.SwiftContext;
auto emptyPL = ParameterList::createEmpty(context);
// Create the constructor.
DeclName name(context, DeclBaseName::createConstructor(), emptyPL);
auto constructor = new (context)
ConstructorDecl(name, structDecl->getLoc(),
/*Failable=*/false, /*FailabilityLoc=*/SourceLoc(),
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
/*ThrownType=*/TypeLoc(), emptyPL,
/*GenericParams=*/nullptr, structDecl);
constructor->copyFormalAccessFrom(structDecl);
// Mark the constructor transparent so that we inline it away completely.
constructor->addAttribute(new (context) TransparentAttr(/*implicit*/ true));
constructor->setBodySynthesizer(synthesizeStructDefaultConstructorBody,
structDecl);
// We're done.
return constructor;
}
// MARK: Struct value initializers
/// Synthesizer callback for the body of a struct value constructor.
static std::pair<BraceStmt *, bool>
synthesizeValueConstructorBody(AbstractFunctionDecl *afd, void *context) {
auto constructor = cast<ConstructorDecl>(afd);
ArrayRef<VarDecl *> members(static_cast<VarDecl **>(context) + 1,
static_cast<uintptr_t *>(context)[0]);
ASTContext &ctx = constructor->getASTContext();
// Assign all of the member variables appropriately.
SmallVector<ASTNode, 4> stmts;
auto *selfDecl = constructor->getImplicitSelfDecl();
// To keep DI happy, initialize stored properties before computed.
auto parameters = constructor->getParameters();
for (unsigned pass = 0; pass < 2; ++pass) {
unsigned paramPos = 0;
for (unsigned i = 0, e = members.size(); i < e; ++i) {
auto var = members[i];
if (isa_and_nonnull<clang::IndirectFieldDecl>(var->getClangDecl()))
continue;
if (var->hasStorage() == (pass != 0)) {
++paramPos;
continue;
}
// Construct left-hand side.
Expr *lhs = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*Implicit=*/true);
lhs->setType(LValueType::get(selfDecl->getTypeInContext()));
auto semantics = (var->hasStorage() ? AccessSemantics::DirectToStorage
: AccessSemantics::Ordinary);
lhs = new (ctx) MemberRefExpr(lhs, SourceLoc(), var, DeclNameLoc(),
/*Implicit=*/true, semantics);
lhs->setType(LValueType::get(var->getTypeInContext()));
// Construct right-hand side.
auto rhs = new (ctx) DeclRefExpr(parameters->get(paramPos), DeclNameLoc(),
/*Implicit=*/true);
rhs->setType(parameters->get(paramPos)->getTypeInContext());
// Add assignment.
auto assign = new (ctx) AssignExpr(lhs, SourceLoc(), rhs,
/*Implicit=*/true);
assign->setType(TupleType::getEmpty(ctx));
stmts.push_back(assign);
++paramPos;
}
}
stmts.push_back(ReturnStmt::createImplicit(ctx, /*expr*/ nullptr));
// Create the function body.
auto body = BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc());
return {body, /*isTypeChecked=*/true};
}
ConstructorDecl *SwiftDeclSynthesizer::createValueConstructor(
NominalTypeDecl *structDecl, ArrayRef<VarDecl *> members,
bool wantCtorParamNames, bool wantBody) {
auto &context = ImporterImpl.SwiftContext;
// Construct the set of parameters from the list of members.
SmallVector<ParamDecl *, 8> valueParameters;
for (auto var : members) {
if (var->isStatic())
continue;
bool generateParamName = wantCtorParamNames;
if (var->hasClangNode()) {
// TODO create value constructor with indirect fields instead of the
// generated __Anonymous_field.
if (isa<clang::IndirectFieldDecl>(var->getClangDecl()))
continue;
if (auto clangField = dyn_cast<clang::FieldDecl>(var->getClangDecl()))
if (clangField->isAnonymousStructOrUnion() ||
clangField->getDeclName().isEmpty())
generateParamName = false;
}
Identifier argName = generateParamName ? var->getName() : Identifier();
auto param =
new (context) ParamDecl(SourceLoc(), SourceLoc(), argName, SourceLoc(),
var->getName(), structDecl);
param->setSpecifier(ParamSpecifier::Default);
param->setInterfaceType(var->getInterfaceType());
ImporterImpl.recordImplicitUnwrapForDecl(
param, var->isImplicitlyUnwrappedOptional());
// Don't allow the parameter to accept temporary pointer conversions.
param->setNonEphemeralIfPossible();
valueParameters.push_back(param);
}
auto *paramList = ParameterList::create(context, valueParameters);
// Create the constructor
DeclName name(context, DeclBaseName::createConstructor(), paramList);
auto constructor = new (context)
ConstructorDecl(name, structDecl->getLoc(),
/*Failable=*/false, /*FailabilityLoc=*/SourceLoc(),
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
/*ThrownType=*/TypeLoc(), paramList,
/*GenericParams=*/nullptr, structDecl);
constructor->copyFormalAccessFrom(structDecl);
// Make the constructor transparent so we inline it away completely.
constructor->addAttribute(new (context) TransparentAttr(/*implicit*/ true));
if (wantBody) {
auto memberMemory =
context.AllocateUninitialized<uintptr_t>(members.size() + 1);
memberMemory[0] = members.size();
for (unsigned i : indices(members)) {
memberMemory[i + 1] = reinterpret_cast<uintptr_t>(members[i]);
}
constructor->setBodySynthesizer(synthesizeValueConstructorBody,
memberMemory.data());
}
// We're done.
return constructor;
}
// MARK: Struct RawValue initializers
/// Synthesizer callback for a raw value bridging constructor body.
static std::pair<BraceStmt *, bool>
synthesizeRawValueBridgingConstructorBody(AbstractFunctionDecl *afd,
void *context) {
auto init = cast<ConstructorDecl>(afd);
VarDecl *storedRawValue = static_cast<VarDecl *>(context);
ASTContext &ctx = init->getASTContext();
auto selfDecl = init->getImplicitSelfDecl();
auto storedType = storedRawValue->getInterfaceType();
// Construct left-hand side.
Expr *lhs = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*Implicit=*/true);
lhs->setType(LValueType::get(selfDecl->getTypeInContext()));
lhs = new (ctx)
MemberRefExpr(lhs, SourceLoc(), storedRawValue, DeclNameLoc(),
/*Implicit=*/true, AccessSemantics::DirectToStorage);
lhs->setType(LValueType::get(storedType));
// Construct right-hand side.
// FIXME: get the parameter from the init, and plug it in here.
auto *paramDecl = init->getParameters()->get(0);
auto *paramRef =
new (ctx) DeclRefExpr(paramDecl, DeclNameLoc(), /*Implicit=*/true);
paramRef->setType(paramDecl->getTypeInContext());
Expr *rhs = paramRef;
if (!storedRawValue->getInterfaceType()->isEqual(paramDecl->getInterfaceType())) {
auto bridge = new (ctx) BridgeToObjCExpr(paramRef, storedType);
bridge->setType(storedType);
rhs = CoerceExpr::createImplicit(ctx, bridge, storedType);
}
// Add assignment.
auto assign = new (ctx) AssignExpr(lhs, SourceLoc(), rhs,
/*Implicit=*/true);
assign->setType(TupleType::getEmpty(ctx));
auto *ret = ReturnStmt::createImplicit(ctx, /*expr*/ nullptr);
auto body = BraceStmt::create(ctx, SourceLoc(), {assign, ret}, SourceLoc());
return {body, /*isTypeChecked=*/true};
}
ConstructorDecl *SwiftDeclSynthesizer::createRawValueBridgingConstructor(
StructDecl *structDecl, VarDecl *computedRawValue, VarDecl *storedRawValue,
bool wantLabel, bool wantBody) {
auto init = createValueConstructor(structDecl, computedRawValue,
/*wantCtorParamNames=*/wantLabel,
/*wantBody=*/false);
// Insert our custom init body
if (wantBody) {
init->setBodySynthesizer(synthesizeRawValueBridgingConstructorBody,
storedRawValue);
}
return init;
}
void SwiftDeclSynthesizer::makeStructRawValuedWithBridge(
StructDecl *structDecl, Type storedUnderlyingType, Type bridgedType,
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
bool makeUnlabeledValueInit) {
auto &ctx = ImporterImpl.SwiftContext;
ImporterImpl.addSynthesizedProtocolAttrs(structDecl,
synthesizedProtocolAttrs);
auto storedVarName = ctx.getIdentifier("_rawValue");
auto computedVarName = ctx.Id_rawValue;
// Create a variable to store the underlying value.
VarDecl *storedVar;
PatternBindingDecl *storedPatternBinding;
std::tie(storedVar, storedPatternBinding) = createVarWithPattern(
structDecl, storedVarName, storedUnderlyingType, VarDecl::Introducer::Var,
/*isImplicit=*/true, AccessLevel::Private, AccessLevel::Private);
// Create a computed value variable.
auto computedVar = new (ctx) VarDecl(
/*IsStatic*/ false, VarDecl::Introducer::Var, SourceLoc(),
computedVarName, structDecl);
computedVar->setInterfaceType(bridgedType);
computedVar->setImplicit();
computedVar->copyFormalAccessFrom(structDecl);
computedVar->setSetterAccess(AccessLevel::Private);
// Create the getter for the computed value variable.
auto computedVarGetter =
makeStructRawValueGetter(structDecl, computedVar, storedVar);
ImporterImpl.makeComputed(computedVar, computedVarGetter, nullptr);
// Create a pattern binding to describe the variable.
Pattern *computedBindingPattern = createTypedNamedPattern(computedVar);
auto *computedPatternBinding = PatternBindingDecl::createImplicit(
ctx, StaticSpellingKind::None, computedBindingPattern,
/*InitExpr*/ nullptr, structDecl);
auto init =
createRawValueBridgingConstructor(structDecl, computedVar, storedVar,
/*wantLabel*/ true,
/*wantBody*/ true);
ConstructorDecl *unlabeledCtor = nullptr;
if (makeUnlabeledValueInit)
unlabeledCtor = createRawValueBridgingConstructor(
structDecl, computedVar, storedVar,
/*wantLabel*/ false, /*wantBody*/ true);
if (unlabeledCtor)
structDecl->addMember(unlabeledCtor);
structDecl->addMember(init);
structDecl->addMember(storedPatternBinding);
structDecl->addMember(storedVar);
structDecl->addMember(computedPatternBinding);
structDecl->addMember(computedVar);
ImporterImpl.addSynthesizedTypealias(structDecl, ctx.Id_RawValue,
bridgedType);
ImporterImpl.RawTypes[structDecl] = bridgedType;
}
void SwiftDeclSynthesizer::makeStructRawValued(
StructDecl *structDecl, Type underlyingType,
ArrayRef<KnownProtocolKind> synthesizedProtocolAttrs,
MakeStructRawValuedOptions options, AccessLevel setterAccess) {
auto &ctx = ImporterImpl.SwiftContext;
ImporterImpl.addSynthesizedProtocolAttrs(structDecl,
synthesizedProtocolAttrs);
// Create a variable to store the underlying value.
VarDecl *var;
PatternBindingDecl *patternBinding;
auto introducer = (options.contains(MakeStructRawValuedFlags::IsLet)
? VarDecl::Introducer::Let
: VarDecl::Introducer::Var);
std::tie(var, patternBinding) = createVarWithPattern(
structDecl, ctx.Id_rawValue, underlyingType, introducer,
options.contains(MakeStructRawValuedFlags::IsImplicit),
structDecl->getFormalAccess(), setterAccess);
assert(var->hasStorage());
// Create constructors to initialize that value from a value of the
// underlying type.
if (options.contains(MakeStructRawValuedFlags::MakeUnlabeledValueInit))
structDecl->addMember(createValueConstructor(structDecl, var,
/*wantCtorParamNames=*/false,
/*wantBody=*/true));
auto *initRawValue = createValueConstructor(structDecl, var,
/*wantCtorParamNames=*/true,
/*wantBody=*/true);
structDecl->addMember(initRawValue);
structDecl->addMember(patternBinding);
structDecl->addMember(var);
ImporterImpl.addSynthesizedTypealias(structDecl, ctx.Id_RawValue,
underlyingType);
ImporterImpl.RawTypes[structDecl] = underlyingType;
}
// MARK: Unions
/// Synthesizer for the body of a union field getter.
static std::pair<BraceStmt *, bool>
synthesizeUnionFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
auto getterDecl = cast<AccessorDecl>(afd);
ASTContext &ctx = getterDecl->getASTContext();
auto importedFieldDecl = static_cast<VarDecl *>(context);
auto selfDecl = getterDecl->getImplicitSelfDecl();
auto selfRef = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
selfRef->setType(selfDecl->getInterfaceType());
auto reinterpretCast = cast<FuncDecl>(
getBuiltinValueDecl(ctx, ctx.getIdentifier("reinterpretCast")));
ConcreteDeclRef reinterpretCastRef(
reinterpretCast,
SubstitutionMap::get(
reinterpretCast->getGenericSignature(),
{selfDecl->getInterfaceType(), importedFieldDecl->getInterfaceType()},
LookUpConformanceInModule()));
auto reinterpretCastRefExpr =
new (ctx) DeclRefExpr(reinterpretCastRef, DeclNameLoc(),
/*implicit*/ true);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
reinterpretCastRefExpr->setType(
FunctionType::get(AnyFunctionType::Param(selfDecl->getInterfaceType()),
importedFieldDecl->getInterfaceType(), info));
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfRef});
auto reinterpreted =
CallExpr::createImplicit(ctx, reinterpretCastRefExpr, argList);
reinterpreted->setType(importedFieldDecl->getInterfaceType());
reinterpreted->setThrows(nullptr);
auto *ret = ReturnStmt::createImplicit(ctx, reinterpreted);
auto body = BraceStmt::create(ctx, SourceLoc(), ASTNode(ret), SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked*/ true};
}
/// Synthesizer for the body of a union field setter.
static std::pair<BraceStmt *, bool>
synthesizeUnionFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
auto setterDecl = cast<AccessorDecl>(afd);
ASTContext &ctx = setterDecl->getASTContext();
auto inoutSelfDecl = setterDecl->getImplicitSelfDecl();
auto inoutSelfRef = new (ctx) DeclRefExpr(inoutSelfDecl, DeclNameLoc(),
/*implicit*/ true);
inoutSelfRef->setType(LValueType::get(inoutSelfDecl->getInterfaceType()));
auto newValueDecl = setterDecl->getParameters()->get(0);
auto newValueRef = new (ctx) DeclRefExpr(newValueDecl, DeclNameLoc(),
/*implicit*/ true);
newValueRef->setType(newValueDecl->getInterfaceType());
auto addressofFn =
cast<FuncDecl>(getBuiltinValueDecl(ctx, ctx.getIdentifier("unprotectedAddressOf")));
ConcreteDeclRef addressofFnRef(
addressofFn, SubstitutionMap::get(addressofFn->getGenericSignature(),
{inoutSelfDecl->getInterfaceType()},
LookUpConformanceInModule()));
auto addressofFnRefExpr =
new (ctx) DeclRefExpr(addressofFnRef, DeclNameLoc(), /*implicit*/ true);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo addressOfInfo;
addressofFnRefExpr->setType(FunctionType::get(
AnyFunctionType::Param(inoutSelfDecl->getInterfaceType(), Identifier(),
ParameterTypeFlags().withInOut(true)),
ctx.TheRawPointerType, addressOfInfo));
auto *selfPtrArgs = ArgumentList::createImplicit(
ctx, {Argument::implicitInOut(ctx, inoutSelfRef)});
auto selfPointer =
CallExpr::createImplicit(ctx, addressofFnRefExpr, selfPtrArgs);
selfPointer->setType(ctx.TheRawPointerType);
selfPointer->setThrows(nullptr);
auto initializeFn =
cast<FuncDecl>(getBuiltinValueDecl(ctx, ctx.getIdentifier("initialize")));
ConcreteDeclRef initializeFnRef(
initializeFn, SubstitutionMap::get(initializeFn->getGenericSignature(),
{newValueDecl->getInterfaceType()},
LookUpConformanceInModule()));
auto initializeFnRefExpr =
new (ctx) DeclRefExpr(initializeFnRef, DeclNameLoc(), /*implicit*/ true);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo initializeInfo;
initializeFnRefExpr->setType(FunctionType::get(
{AnyFunctionType::Param(newValueDecl->getInterfaceType()),
AnyFunctionType::Param(ctx.TheRawPointerType)},
TupleType::getEmpty(ctx), initializeInfo));
auto *initArgs =
ArgumentList::forImplicitUnlabeled(ctx, {newValueRef, selfPointer});
auto initialize =
CallExpr::createImplicit(ctx, initializeFnRefExpr, initArgs);
initialize->setType(TupleType::getEmpty(ctx));
initialize->setThrows(nullptr);
auto body = BraceStmt::create(ctx, SourceLoc(), {initialize}, SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked*/ true};
}
std::pair<AccessorDecl *, AccessorDecl *>
SwiftDeclSynthesizer::makeUnionFieldAccessors(
NominalTypeDecl *importedUnionDecl, VarDecl *importedFieldDecl) {
auto &C = ImporterImpl.SwiftContext;
auto getterDecl =
makeFieldGetterDecl(ImporterImpl, importedUnionDecl, importedFieldDecl);
getterDecl->setBodySynthesizer(synthesizeUnionFieldGetterBody,
importedFieldDecl);
getterDecl->addAttribute(new (C) TransparentAttr(/*implicit*/ true));
auto setterDecl =
makeFieldSetterDecl(ImporterImpl, importedUnionDecl, importedFieldDecl);
setterDecl->setBodySynthesizer(synthesizeUnionFieldSetterBody,
importedFieldDecl);
setterDecl->addAttribute(new (C) TransparentAttr(/*implicit*/ true));
ImporterImpl.makeComputed(importedFieldDecl, getterDecl, setterDecl);
return {getterDecl, setterDecl};
}
static clang::DeclarationName
getAccessorDeclarationName(clang::ASTContext &Ctx, NominalTypeDecl *structDecl,
VarDecl *fieldDecl, const char *suffix) {
std::string id;
llvm::raw_string_ostream IdStream(id);
Mangle::ASTMangler mangler(structDecl->getASTContext());
IdStream << "$" << mangler.mangleDeclAsUSR(structDecl, "") << "$"
<< fieldDecl->getName() << "$" << suffix;
return clang::DeclarationName(&Ctx.Idents.get(IdStream.str()));
}
std::pair<FuncDecl *, FuncDecl *> SwiftDeclSynthesizer::makeBitFieldAccessors(
clang::RecordDecl *structDecl, NominalTypeDecl *importedStructDecl,
clang::FieldDecl *fieldDecl, VarDecl *importedFieldDecl) {
clang::ASTContext &Ctx = ImporterImpl.getClangASTContext();
// Getter: static inline FieldType get(RecordType self);
auto recordType = Ctx.getRecordType(structDecl);
auto recordPointerType = Ctx.getPointerType(recordType);
auto fieldType = fieldDecl->getType();
auto cGetterName = getAccessorDeclarationName(Ctx, importedStructDecl,
importedFieldDecl, "getter");
auto cGetterType =
Ctx.getFunctionType(fieldDecl->getType(), recordType,
clang::FunctionProtoType::ExtProtoInfo());
auto cGetterTypeInfo = Ctx.getTrivialTypeSourceInfo(cGetterType);
auto cGetterDecl = clang::FunctionDecl::Create(
Ctx, structDecl->getDeclContext(), clang::SourceLocation(),
clang::SourceLocation(), cGetterName, cGetterType, cGetterTypeInfo,
clang::SC_Static);
cGetterDecl->setImplicit();
cGetterDecl->setImplicitlyInline();
assert(!cGetterDecl->isExternallyVisible());
auto getterDecl = makeFieldGetterDecl(ImporterImpl, importedStructDecl,
importedFieldDecl, cGetterDecl);
// Setter: static inline void set(FieldType newValue, RecordType *self);
SmallVector<clang::QualType, 8> cSetterParamTypes;
cSetterParamTypes.push_back(fieldType);
cSetterParamTypes.push_back(recordPointerType);
auto cSetterName = getAccessorDeclarationName(Ctx, importedStructDecl,
importedFieldDecl, "setter");
auto cSetterType = Ctx.getFunctionType(
Ctx.VoidTy, cSetterParamTypes, clang::FunctionProtoType::ExtProtoInfo());
auto cSetterTypeInfo = Ctx.getTrivialTypeSourceInfo(cSetterType);
auto cSetterDecl = clang::FunctionDecl::Create(
Ctx, structDecl->getDeclContext(), clang::SourceLocation(),
clang::SourceLocation(), cSetterName, cSetterType, cSetterTypeInfo,
clang::SC_Static);
cSetterDecl->setImplicit();
cSetterDecl->setImplicitlyInline();
assert(!cSetterDecl->isExternallyVisible());
auto setterDecl = makeFieldSetterDecl(ImporterImpl, importedStructDecl,
importedFieldDecl, cSetterDecl);
ImporterImpl.makeComputed(importedFieldDecl, getterDecl, setterDecl);
// Synthesize the getter body
{
auto cGetterSelfId = nullptr;
auto recordTypeInfo = Ctx.getTrivialTypeSourceInfo(recordType);
auto cGetterSelf = clang::ParmVarDecl::Create(
Ctx, cGetterDecl, clang::SourceLocation(), clang::SourceLocation(),
cGetterSelfId, recordType, recordTypeInfo, clang::SC_None, nullptr);
cGetterSelf->setImplicit();
cGetterDecl->setParams(cGetterSelf);
auto cGetterSelfExpr = new (Ctx)
clang::DeclRefExpr(Ctx, cGetterSelf, false, recordType,
clang::VK_PRValue, clang::SourceLocation());
auto cGetterExpr = clang::MemberExpr::CreateImplicit(
Ctx, cGetterSelfExpr,
/*isarrow=*/false, fieldDecl, fieldType, clang::VK_PRValue,
clang::OK_BitField);
auto cGetterBody = clang::ReturnStmt::Create(Ctx, clang::SourceLocation(),
cGetterExpr, nullptr);
cGetterDecl->setBody(cGetterBody);
}
// Synthesize the setter body
{
SmallVector<clang::ParmVarDecl *, 2> cSetterParams;
auto fieldTypeInfo = Ctx.getTrivialTypeSourceInfo(fieldType);
auto cSetterValue = clang::ParmVarDecl::Create(
Ctx, cSetterDecl, clang::SourceLocation(), clang::SourceLocation(),
/* nameID? */ nullptr, fieldType, fieldTypeInfo, clang::SC_None,
nullptr);
cSetterValue->setImplicit();
cSetterParams.push_back(cSetterValue);
auto recordPointerTypeInfo =
Ctx.getTrivialTypeSourceInfo(recordPointerType);
auto cSetterSelf = clang::ParmVarDecl::Create(
Ctx, cSetterDecl, clang::SourceLocation(), clang::SourceLocation(),
/* nameID? */ nullptr, recordPointerType, recordPointerTypeInfo,
clang::SC_None, nullptr);
cSetterSelf->setImplicit();
cSetterParams.push_back(cSetterSelf);
cSetterDecl->setParams(cSetterParams);
auto cSetterSelfExpr = new (Ctx)
clang::DeclRefExpr(Ctx, cSetterSelf, false, recordPointerType,
clang::VK_PRValue, clang::SourceLocation());
auto cSetterMemberExpr = clang::MemberExpr::CreateImplicit(
Ctx, cSetterSelfExpr,
/*isarrow=*/true, fieldDecl, fieldType, clang::VK_LValue,
clang::OK_BitField);
auto cSetterValueExpr = new (Ctx)
clang::DeclRefExpr(Ctx, cSetterValue, false, fieldType,
clang::VK_PRValue, clang::SourceLocation());
auto cSetterExpr = clang::BinaryOperator::Create(
Ctx, cSetterMemberExpr, cSetterValueExpr, clang::BO_Assign, fieldType,
clang::VK_PRValue, clang::OK_Ordinary, clang::SourceLocation(),
clang::FPOptionsOverride());
cSetterDecl->setBody(cSetterExpr);
}
return {getterDecl, setterDecl};
}
/// Find the anonymous inner field declaration for the given anonymous field.
static VarDecl *findAnonymousInnerFieldDecl(VarDecl *importedFieldDecl,
VarDecl *anonymousFieldDecl) {
auto anonymousFieldType = anonymousFieldDecl->getInterfaceType();
auto anonymousFieldTypeDecl =
anonymousFieldType->getStructOrBoundGenericStruct();
for (auto decl :
anonymousFieldTypeDecl->lookupDirect(importedFieldDecl->getName())) {
if (isa<VarDecl>(decl)) {
return cast<VarDecl>(decl);
}
}
llvm_unreachable("couldn't find anonymous inner field decl");
}
// MARK: Indirect fields
/// Synthesize the getter body for an indirect field.
static std::pair<BraceStmt *, bool>
synthesizeIndirectFieldGetterBody(AbstractFunctionDecl *afd, void *context) {
auto getterDecl = cast<AccessorDecl>(afd);
auto anonymousFieldDecl = static_cast<VarDecl *>(context);
ASTContext &ctx = getterDecl->getASTContext();
auto selfDecl = getterDecl->getImplicitSelfDecl();
Expr *expr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
expr->setType(selfDecl->getInterfaceType());
expr = new (ctx) MemberRefExpr(expr, SourceLoc(), anonymousFieldDecl,
DeclNameLoc(), /*implicit*/ true);
expr->setType(anonymousFieldDecl->getInterfaceType());
auto importedFieldDecl = cast<VarDecl>(getterDecl->getStorage());
auto anonymousInnerFieldDecl =
findAnonymousInnerFieldDecl(importedFieldDecl, anonymousFieldDecl);
expr = new (ctx) MemberRefExpr(expr, SourceLoc(), anonymousInnerFieldDecl,
DeclNameLoc(), /*implicit*/ true);
expr->setType(anonymousInnerFieldDecl->getInterfaceType());
auto *ret = ReturnStmt::createImplicit(ctx, expr);
auto body = BraceStmt::create(ctx, SourceLoc(), ASTNode(ret), SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked=*/true};
}
/// Synthesize the setter body for an indirect field.
static std::pair<BraceStmt *, bool>
synthesizeIndirectFieldSetterBody(AbstractFunctionDecl *afd, void *context) {
auto setterDecl = cast<AccessorDecl>(afd);
auto anonymousFieldDecl = static_cast<VarDecl *>(context);
ASTContext &ctx = setterDecl->getASTContext();
auto selfDecl = setterDecl->getImplicitSelfDecl();
Expr *lhs = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
lhs->setType(LValueType::get(selfDecl->getInterfaceType()));
lhs = new (ctx) MemberRefExpr(lhs, SourceLoc(), anonymousFieldDecl,
DeclNameLoc(), /*implicit*/ true);
lhs->setType(LValueType::get(anonymousFieldDecl->getInterfaceType()));
auto importedFieldDecl = cast<VarDecl>(setterDecl->getStorage());
auto anonymousInnerFieldDecl =
findAnonymousInnerFieldDecl(importedFieldDecl, anonymousFieldDecl);
lhs = new (ctx) MemberRefExpr(lhs, SourceLoc(), anonymousInnerFieldDecl,
DeclNameLoc(), /*implicit*/ true);
lhs->setType(LValueType::get(anonymousInnerFieldDecl->getInterfaceType()));
auto newValueDecl = setterDecl->getParameters()->get(0);
auto rhs = new (ctx) DeclRefExpr(newValueDecl, DeclNameLoc(),
/*implicit*/ true);
rhs->setType(newValueDecl->getInterfaceType());
auto assign = new (ctx) AssignExpr(lhs, SourceLoc(), rhs, /*implicit*/ true);
assign->setType(TupleType::getEmpty(ctx));
auto body = BraceStmt::create(ctx, SourceLoc(), {assign}, SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked=*/true};
}
std::pair<AccessorDecl *, AccessorDecl *>
SwiftDeclSynthesizer::makeIndirectFieldAccessors(
const clang::IndirectFieldDecl *indirectField, ArrayRef<VarDecl *> members,
NominalTypeDecl *importedStructDecl, VarDecl *importedFieldDecl) {
auto &C = ImporterImpl.SwiftContext;
auto getterDecl =
makeFieldGetterDecl(ImporterImpl, importedStructDecl, importedFieldDecl);
getterDecl->addAttribute(new (C) TransparentAttr(/*implicit*/ true));
auto setterDecl =
makeFieldSetterDecl(ImporterImpl, importedStructDecl, importedFieldDecl);
setterDecl->addAttribute(new (C) TransparentAttr(/*implicit*/ true));
ImporterImpl.makeComputed(importedFieldDecl, getterDecl, setterDecl);
auto containingField = indirectField->chain().front();
VarDecl *anonymousFieldDecl = nullptr;
// Reverse scan of the members because indirect field are generated just
// after the corresponding anonymous type, so a reverse scan allows
// switching from O(n) to O(1) here.
for (auto decl : reverse(members)) {
if (decl->getClangDecl() == containingField) {
anonymousFieldDecl = cast<VarDecl>(decl);
break;
}
}
assert(anonymousFieldDecl && "anonymous field not generated");
getterDecl->setBodySynthesizer(synthesizeIndirectFieldGetterBody,
anonymousFieldDecl);
setterDecl->setBodySynthesizer(synthesizeIndirectFieldSetterBody,
anonymousFieldDecl);
return {getterDecl, setterDecl};
}
// MARK: Enum RawValue initializers
/// Synthesize the body of \c init?(rawValue:RawType) for an imported enum.
static std::pair<BraceStmt *, bool>
synthesizeEnumRawValueConstructorBody(AbstractFunctionDecl *afd,
void *context) {
ASTContext &ctx = afd->getASTContext();
auto ctorDecl = cast<ConstructorDecl>(afd);
auto enumDecl = static_cast<EnumDecl *>(context);
auto selfDecl = ctorDecl->getImplicitSelfDecl();
auto selfRef = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
selfRef->setType(LValueType::get(selfDecl->getTypeInContext()));
auto param = ctorDecl->getParameters()->get(0);
auto paramRef = new (ctx) DeclRefExpr(param, DeclNameLoc(),
/*implicit*/ true);
paramRef->setType(param->getTypeInContext());
auto reinterpretCast = cast<FuncDecl>(
getBuiltinValueDecl(ctx, ctx.getIdentifier("reinterpretCast")));
auto rawTy = enumDecl->getRawType();
auto enumTy = enumDecl->getDeclaredInterfaceType();
SubstitutionMap subMap = SubstitutionMap::get(
reinterpretCast->getGenericSignature(), {rawTy, enumTy},
LookUpConformanceInModule());
ConcreteDeclRef concreteDeclRef(reinterpretCast, subMap);
auto reinterpretCastRef =
new (ctx) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
reinterpretCastRef->setType(
FunctionType::get({FunctionType::Param(rawTy)}, enumTy, info));
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {paramRef});
auto reinterpreted =
CallExpr::createImplicit(ctx, reinterpretCastRef, argList);
reinterpreted->setType(enumTy);
reinterpreted->setThrows(nullptr);
auto assign = new (ctx) AssignExpr(selfRef, SourceLoc(), reinterpreted,
/*implicit*/ true);
assign->setType(TupleType::getEmpty(ctx));
auto *ret = ReturnStmt::createImplicit(ctx, /*expr*/ nullptr);
auto body = BraceStmt::create(ctx, SourceLoc(), {assign, ret}, SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked=*/true};
}
ConstructorDecl *
SwiftDeclSynthesizer::makeEnumRawValueConstructor(EnumDecl *enumDecl) {
ASTContext &C = ImporterImpl.SwiftContext;
auto rawTy = enumDecl->getRawType();
auto param = new (C) ParamDecl(SourceLoc(), SourceLoc(), C.Id_rawValue,
SourceLoc(), C.Id_rawValue, enumDecl);
param->setSpecifier(ParamSpecifier::Default);
param->setInterfaceType(rawTy);
auto paramPL = ParameterList::createWithoutLoc(param);
DeclName name(C, DeclBaseName::createConstructor(), paramPL);
auto *ctorDecl =
new (C) ConstructorDecl(name, enumDecl->getLoc(),
/*Failable=*/true, /*FailabilityLoc=*/SourceLoc(),
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false, /*ThrowsLoc=*/SourceLoc(),
/*ThrownType=*/TypeLoc(), paramPL,
/*GenericParams=*/nullptr, enumDecl);
ctorDecl->setImplicit();
ctorDecl->copyFormalAccessFrom(enumDecl);
ctorDecl->setBodySynthesizer(synthesizeEnumRawValueConstructorBody, enumDecl);
return ctorDecl;
}
// MARK: Enum RawValue getters & setters
/// Synthesizer callback for an enum's rawValue getter.
static std::pair<BraceStmt *, bool>
synthesizeEnumRawValueGetterBody(AbstractFunctionDecl *afd, void *context) {
auto getterDecl = cast<AccessorDecl>(afd);
auto enumDecl = static_cast<EnumDecl *>(context);
auto rawTy = enumDecl->getRawType();
auto enumTy = enumDecl->getDeclaredInterfaceType();
ASTContext &ctx = getterDecl->getASTContext();
auto *selfDecl = getterDecl->getImplicitSelfDecl();
auto selfRef = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
selfRef->setType(selfDecl->getTypeInContext());
auto reinterpretCast = cast<FuncDecl>(
getBuiltinValueDecl(ctx, ctx.getIdentifier("reinterpretCast")));
SubstitutionMap subMap = SubstitutionMap::get(
reinterpretCast->getGenericSignature(), {enumTy, rawTy},
LookUpConformanceInModule());
ConcreteDeclRef concreteDeclRef(reinterpretCast, subMap);
auto reinterpretCastRef =
new (ctx) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
reinterpretCastRef->setType(
FunctionType::get({FunctionType::Param(enumTy)}, rawTy, info));
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {selfRef});
auto reinterpreted =
CallExpr::createImplicit(ctx, reinterpretCastRef, argList);
reinterpreted->setType(rawTy);
reinterpreted->setThrows(nullptr);
auto *ret = ReturnStmt::createImplicit(ctx, reinterpreted);
auto body = BraceStmt::create(ctx, SourceLoc(), ASTNode(ret), SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked=*/true};
}
// Build the rawValue getter for an imported NS_ENUM.
// enum NSSomeEnum: RawType {
// var rawValue: RawType {
// return Builtin.reinterpretCast(self)
// }
// }
// Unlike a standard init(rawValue:) enum initializer, this does a reinterpret
// cast in order to preserve unknown or future cases from C.
void SwiftDeclSynthesizer::makeEnumRawValueGetter(EnumDecl *enumDecl,
VarDecl *rawValueDecl) {
ASTContext &C = ImporterImpl.SwiftContext;
auto rawTy = enumDecl->getRawType();
auto *params = ParameterList::createEmpty(C);
auto getterDecl = AccessorDecl::create(
C,
/*declLoc=*/SourceLoc(),
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, rawValueDecl,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
params, rawTy, enumDecl);
getterDecl->setImplicit();
getterDecl->setIsObjC(false);
getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(false);
getterDecl->copyFormalAccessFrom(enumDecl);
getterDecl->setBodySynthesizer(synthesizeEnumRawValueGetterBody, enumDecl);
ImporterImpl.makeComputed(rawValueDecl, getterDecl, nullptr);
}
// MARK: Struct RawValue getters
/// Synthesizer for the rawValue getter for an imported struct.
static std::pair<BraceStmt *, bool>
synthesizeStructRawValueGetterBody(AbstractFunctionDecl *afd, void *context) {
auto getterDecl = cast<AccessorDecl>(afd);
VarDecl *storedVar = static_cast<VarDecl *>(context);
ASTContext &ctx = getterDecl->getASTContext();
auto *selfDecl = getterDecl->getImplicitSelfDecl();
auto selfRef = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
selfRef->setType(selfDecl->getTypeInContext());
auto storedType = storedVar->getInterfaceType();
auto storedRef = new (ctx)
MemberRefExpr(selfRef, SourceLoc(), storedVar, DeclNameLoc(),
/*Implicit=*/true, AccessSemantics::DirectToStorage);
storedRef->setType(storedType);
Expr *result = storedRef;
Type computedType = getterDecl->getResultInterfaceType();
if (!computedType->isEqual(storedType)) {
auto bridge = new (ctx) BridgeFromObjCExpr(storedRef, computedType);
bridge->setType(computedType);
result = CoerceExpr::createImplicit(ctx, bridge, computedType);
}
auto ret = ReturnStmt::createImplicit(ctx, result);
auto body = BraceStmt::create(ctx, SourceLoc(), ASTNode(ret), SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked=*/true};
}
AccessorDecl *SwiftDeclSynthesizer::makeStructRawValueGetter(
StructDecl *structDecl, VarDecl *computedVar, VarDecl *storedVar) {
assert(storedVar->hasStorage());
ASTContext &C = ImporterImpl.SwiftContext;
auto *params = ParameterList::createEmpty(C);
auto computedType = computedVar->getInterfaceType();
auto getterDecl = AccessorDecl::create(
C,
/*declLoc=*/SourceLoc(),
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, computedVar,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
params, computedType, structDecl);
getterDecl->setImplicit();
getterDecl->setIsObjC(false);
getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(false);
getterDecl->copyFormalAccessFrom(structDecl);
getterDecl->setBodySynthesizer(synthesizeStructRawValueGetterBody, storedVar);
return getterDecl;
}
// MARK: ObjC subscripts
AccessorDecl *SwiftDeclSynthesizer::buildSubscriptGetterDecl(
SubscriptDecl *subscript, const FuncDecl *getter, Type elementTy,
DeclContext *dc, ParamDecl *index) {
auto &C = ImporterImpl.SwiftContext;
auto loc = getter->getLoc();
auto *params = ParameterList::create(C, index);
// Create the getter thunk.
auto thunk = AccessorDecl::create(
C,
/*declLoc=*/loc,
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Get, subscript,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
params, elementTy, dc, getter->getClangNode());
thunk->setAccess(getOverridableAccessLevel(dc));
if (auto objcAttr = getter->getAttrs().getAttribute<ObjCAttr>())
thunk->addAttribute(objcAttr->clone(C));
thunk->setIsObjC(getter->isObjC());
thunk->setIsDynamic(getter->isDynamic());
// FIXME: Should we record thunks?
return thunk;
}
AccessorDecl *SwiftDeclSynthesizer::buildSubscriptSetterDecl(
SubscriptDecl *subscript, const FuncDecl *setter, Type elementInterfaceTy,
DeclContext *dc, ParamDecl *index) {
auto &C = ImporterImpl.SwiftContext;
auto loc = setter->getLoc();
// Objective-C subscript setters are imported with a function type
// such as:
//
// (self) -> (value, index) -> ()
//
// Build a setter thunk with the latter signature that maps to the
// former.
auto valueIndex = setter->getParameters();
auto paramVarDecl = new (C) ParamDecl(SourceLoc(), SourceLoc(), Identifier(),
loc, valueIndex->get(0)->getName(), dc);
paramVarDecl->setSpecifier(ParamSpecifier::Default);
paramVarDecl->setInterfaceType(elementInterfaceTy);
auto valueIndicesPL = ParameterList::create(C, {paramVarDecl, index});
// Create the setter thunk.
auto thunk = AccessorDecl::create(
C,
/*declLoc=*/setter->getLoc(),
/*AccessorKeywordLoc=*/SourceLoc(), AccessorKind::Set, subscript,
/*Async=*/false, /*AsyncLoc=*/SourceLoc(),
/*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), /*ThrownType=*/TypeLoc(),
valueIndicesPL, TupleType::getEmpty(C), dc,
setter->getClangNode());
thunk->setAccess(getOverridableAccessLevel(dc));
if (auto objcAttr = setter->getAttrs().getAttribute<ObjCAttr>())
thunk->addAttribute(objcAttr->clone(C));
thunk->setIsObjC(setter->isObjC());
thunk->setIsDynamic(setter->isDynamic());
return thunk;
}
// MARK: C++ subscripts
Expr *SwiftDeclSynthesizer::synthesizeReturnReinterpretCast(ASTContext &ctx,
Type givenType,
Type exprType,
Expr *baseExpr) {
auto reinterpretCast = cast<FuncDecl>(
getBuiltinValueDecl(ctx, ctx.getIdentifier("reinterpretCast")));
SubstitutionMap subMap = SubstitutionMap::get(
reinterpretCast->getGenericSignature(), {givenType, exprType},
LookUpConformanceInModule());
ConcreteDeclRef concreteDeclRef(reinterpretCast, subMap);
auto reinterpretCastRef =
new (ctx) DeclRefExpr(concreteDeclRef, DeclNameLoc(), /*implicit*/ true);
FunctionType::ExtInfo info;
reinterpretCastRef->setType(
FunctionType::get({FunctionType::Param(givenType)}, exprType, info));
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {baseExpr});
auto reinterpreted =
CallExpr::createImplicit(ctx, reinterpretCastRef, argList);
reinterpreted->setType(exprType);
reinterpreted->setThrows(nullptr);
return reinterpreted;
}
/// Synthesizer callback for a subscript getter or a getter for a
/// dereference property (`var pointee`). If the getter's implementation returns
/// an UnsafePointer or UnsafeMutablePointer, it unwraps the pointer and returns
/// the underlying value.
static std::pair<BraceStmt *, bool>
synthesizeUnwrappingGetterOrAddressGetterBody(AbstractFunctionDecl *afd,
void *context, bool isAddress) {
auto getterDecl = cast<AccessorDecl>(afd);
auto getterImpl = static_cast<FuncDecl *>(context);
ASTContext &ctx = getterDecl->getASTContext();
auto selfArg = createSelfArg(getterDecl);
SmallVector<Expr *> arguments;
for (size_t idx = 0, end = getterDecl->getParameters()->size(); idx < end;
++idx)
arguments.push_back(createParamRefExpr(getterDecl, idx));
Type elementTy = getterDecl->getResultInterfaceType();
auto *getterImplCallExpr =
createAccessorImplCallExpr(getterImpl, selfArg, arguments);
// This default handles C++'s operator[] that returns a value type.
Expr *propertyExpr = getterImplCallExpr;
PointerTypeKind ptrKind;
// The following check returns true if the subscript operator returns a
// C++ reference type. This check actually checks to see if the type is
// a pointer type, but this does not apply to C pointers because they
// are Optional types when imported. TODO: Use a more obvious check
// here.
if (!isAddress &&
getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) {
// `getterImpl` can return either UnsafePointer or
// UnsafeMutablePointer. Retrieve the corresponding `.pointee`
// declaration.
VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind);
// Handle operator[] that returns a reference type.
SubstitutionMap subMap =
SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(),
{elementTy}, LookUpConformanceInModule());
auto pointeePropertyRefExpr = new (ctx) MemberRefExpr(
getterImplCallExpr, SourceLoc(),
ConcreteDeclRef(pointeePropertyDecl, subMap), DeclNameLoc(),
/*implicit*/ true);
pointeePropertyRefExpr->setType(elementTy);
propertyExpr = pointeePropertyRefExpr;
}
// Cast an 'address' result from a mutable pointer if needed.
if (isAddress &&
getterImpl->getResultInterfaceType()->isUnsafeMutablePointer())
propertyExpr = SwiftDeclSynthesizer::synthesizeReturnReinterpretCast(
ctx, getterImpl->getResultInterfaceType(), elementTy, propertyExpr);
auto *returnStmt = ReturnStmt::createImplicit(ctx, propertyExpr);
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked*/ true};
}
static std::pair<BraceStmt *, bool>
synthesizeUnwrappingGetterBody(AbstractFunctionDecl *afd, void *context) {
return synthesizeUnwrappingGetterOrAddressGetterBody(afd, context,
/*isAddress=*/false);
}
static std::pair<BraceStmt *, bool>
synthesizeUnwrappingAddressGetterBody(AbstractFunctionDecl *afd,
void *context) {
return synthesizeUnwrappingGetterOrAddressGetterBody(afd, context,
/*isAddress=*/true);
}
/// Synthesizer callback for a subscript setter or a setter for a dereference
/// property (`var pointee`).
static std::pair<BraceStmt *, bool>
synthesizeUnwrappingSetterBody(AbstractFunctionDecl *afd, void *context) {
auto setterDecl = cast<AccessorDecl>(afd);
auto setterImpl = static_cast<FuncDecl *>(context);
ASTContext &ctx = setterDecl->getASTContext();
auto selfArg = createSelfArg(setterDecl);
DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0);
SmallVector<Expr *> arguments;
for (size_t idx = 1, end = setterDecl->getParameters()->size(); idx < end;
++idx)
arguments.push_back(createParamRefExpr(setterDecl, idx));
Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType();
auto *setterImplCallExpr =
createAccessorImplCallExpr(setterImpl, selfArg, arguments);
VarDecl *pointeePropertyDecl =
ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer);
SubstitutionMap subMap = SubstitutionMap::get(
ctx.getUnsafeMutablePointerDecl()->getGenericSignature(), {elementTy},
LookUpConformanceInModule());
auto pointeePropertyRefExpr = new (ctx)
MemberRefExpr(setterImplCallExpr, SourceLoc(),
ConcreteDeclRef(pointeePropertyDecl, subMap), DeclNameLoc(),
/*implicit*/ true);
pointeePropertyRefExpr->setType(LValueType::get(elementTy));
auto assignExpr = new (ctx)
AssignExpr(pointeePropertyRefExpr, SourceLoc(), valueParamRefExpr,
/*implicit*/ true);
assignExpr->setType(TupleType::getEmpty(ctx));
auto body = BraceStmt::create(ctx, SourceLoc(),
{
assignExpr,
},
SourceLoc());
return {body, /*isTypeChecked*/ true};
}
static std::pair<BraceStmt *, bool>
synthesizeUnwrappingAddressSetterBody(AbstractFunctionDecl *afd,
void *context) {
auto setterDecl = cast<AccessorDecl>(afd);
auto setterImpl = static_cast<FuncDecl *>(context);
ASTContext &ctx = setterDecl->getASTContext();
auto selfArg = createSelfArg(setterDecl);
auto *setterImplCallExpr =
createAccessorImplCallExpr(setterImpl, selfArg, {});
auto *returnStmt = ReturnStmt::createImplicit(ctx, setterImplCallExpr);
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked*/ true};
}
SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
FuncDecl *setter) {
assert((getter || setter) &&
"getter or setter required to generate subscript");
// If only a setter (imported from non-const `operator[]`) is defined,
// generate both get & set accessors from it.
FuncDecl *getterImpl = getter ? getter : setter;
FuncDecl *setterImpl = setter;
// FIXME: support unsafeAddress accessors.
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
const auto rawElementTy = getterImpl->getResultInterfaceType();
// Unwrap `T`. Use rawElementTy for return by value.
const auto elementTy = rawElementTy->getAnyPointerElementType()
? rawElementTy->getAnyPointerElementType()
: rawElementTy;
auto &ctx = ImporterImpl.SwiftContext;
SmallVector<ParamDecl *> paramVec;
for (auto [i, param] : llvm::enumerate(*getterImpl->getParameters())) {
auto clonedParam = ParamDecl::clone(ctx, param);
// If the subscript parameter is unnamed, give it a name to make sure SILGen
// creates a variable for it.
if (clonedParam->getName().empty())
clonedParam->setName(ctx.getIdentifier("__index" + std::to_string(i)));
paramVec.push_back(clonedParam);
}
auto bodyParams = ParameterList::create(ctx, paramVec);
DeclName name(ctx, DeclBaseName::createSubscript(), bodyParams);
auto dc = getterImpl->getDeclContext();
SubscriptDecl *subscript = SubscriptDecl::createImported(
ctx, name, getterImpl->getLoc(), bodyParams, getterImpl->getLoc(),
elementTy, dc, getterImpl->getGenericParams(),
getterImpl->getClangNode());
subscript->copyFormalAccessFrom(getterImpl);
AccessorDecl *getterDecl =
AccessorDecl::create(ctx, getterImpl->getLoc(), getterImpl->getLoc(),
AccessorKind::Get, subscript,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(),
/*ThrownType=*/TypeLoc(), bodyParams, elementTy, dc);
getterDecl->copyFormalAccessFrom(subscript);
getterDecl->setImplicit();
getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(true);
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody, getterImpl);
if (getterImpl->isMutating()) {
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
subscript->setIsGetterMutating(true);
}
AccessorDecl *setterDecl = nullptr;
if (setterImpl) {
auto paramVarDecl =
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
ctx.getIdentifier("newValue"), dc);
paramVarDecl->setSpecifier(ParamSpecifier::Default);
paramVarDecl->setInterfaceType(elementTy);
SmallVector<ParamDecl *> setterParams;
setterParams.push_back(paramVarDecl);
setterParams.append(bodyParams->begin(), bodyParams->end());
auto setterParamList = ParameterList::create(ctx, setterParams);
setterDecl = AccessorDecl::create(
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
subscript,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
setterParamList, TupleType::getEmpty(ctx), dc);
setterDecl->copyFormalAccessFrom(subscript);
setterDecl->setImplicit();
setterDecl->setIsDynamic(false);
setterDecl->setIsTransparent(true);
setterDecl->setBodySynthesizer(synthesizeUnwrappingSetterBody, setterImpl);
if (setterImpl->isMutating()) {
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
subscript->setIsSetterMutating(true);
}
}
ImporterImpl.makeComputed(subscript, getterDecl, setterDecl);
// Implicitly unwrap Optional types for T *operator[].
ImporterImpl.recordImplicitUnwrapForDecl(
subscript, getterImpl->isImplicitlyUnwrappedOptional());
return subscript;
}
// MARK: C++ dereference operator
VarDecl *
SwiftDeclSynthesizer::makeDereferencedPointeeProperty(FuncDecl *getter,
FuncDecl *setter) {
assert((getter || setter) &&
"getter or setter required to generate a pointee property");
auto &ctx = ImporterImpl.SwiftContext;
FuncDecl *getterImpl = getter ? getter : setter;
FuncDecl *setterImpl = setter;
auto dc = getterImpl->getDeclContext();
bool resultDependsOnSelf =
ImporterImpl.returnsSelfDependentValue.contains(getterImpl);
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
const auto rawElementTy = getterImpl->getResultInterfaceType();
// Unwrap `T`. Use rawElementTy for return by value.
const auto elementTy = rawElementTy->getAnyPointerElementType()
? rawElementTy->getAnyPointerElementType()
: rawElementTy;
// Use 'address' or 'mutableAddress' accessors for non-copyable
// types that are returned indirectly.
bool isNoncopyable = dc->mapTypeIntoContext(elementTy)->isNoncopyable();
bool isImplicit = !(isNoncopyable || resultDependsOnSelf);
bool useAddress =
rawElementTy->getAnyPointerElementType() && (isNoncopyable || resultDependsOnSelf);
auto result = new (ctx)
VarDecl(/*isStatic*/ false, VarDecl::Introducer::Var,
getterImpl->getStartLoc(), ctx.getIdentifier("pointee"), dc);
result->setInterfaceType(elementTy);
result->copyFormalAccessFrom(getterImpl);
AccessorDecl *getterDecl = AccessorDecl::create(
ctx, getterImpl->getLoc(), getterImpl->getLoc(),
useAddress ? AccessorKind::Address : AccessorKind::Get, result,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
ParameterList::createEmpty(ctx),
useAddress ? elementTy->wrapInPointer(PTK_UnsafePointer) : elementTy, dc);
getterDecl->copyFormalAccessFrom(getterImpl);
if (isImplicit)
getterDecl->setImplicit();
getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(true);
getterDecl->setBodySynthesizer(useAddress
? synthesizeUnwrappingAddressGetterBody
: synthesizeUnwrappingGetterBody,
getterImpl);
if (getterImpl->isMutating()) {
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
result->setIsGetterMutating(true);
} else {
getterDecl->setSelfAccessKind(SelfAccessKind::NonMutating);
result->setIsGetterMutating(false);
}
AccessorDecl *setterDecl = nullptr;
if (setterImpl) {
auto paramVarDecl =
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
ctx.getIdentifier("newValue"), dc);
paramVarDecl->setSpecifier(ParamSpecifier::Default);
paramVarDecl->setInterfaceType(elementTy);
auto setterParamList = useAddress
? ParameterList::create(ctx, {})
: ParameterList::create(ctx, {paramVarDecl});
setterDecl = AccessorDecl::create(
ctx, setterImpl->getLoc(), setterImpl->getLoc(),
useAddress ? AccessorKind::MutableAddress : AccessorKind::Set, result,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
setterParamList,
useAddress ? elementTy->wrapInPointer(PTK_UnsafeMutablePointer)
: TupleType::getEmpty(ctx),
dc);
setterDecl->copyFormalAccessFrom(setterImpl);
if (isImplicit)
setterDecl->setImplicit();
setterDecl->setIsDynamic(false);
setterDecl->setIsTransparent(true);
setterDecl->setBodySynthesizer(useAddress
? synthesizeUnwrappingAddressSetterBody
: synthesizeUnwrappingSetterBody,
setterImpl);
if (setterImpl->isMutating()) {
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
result->setIsSetterMutating(true);
} else {
setterDecl->setSelfAccessKind(SelfAccessKind::NonMutating);
result->setIsSetterMutating(false);
}
}
ImporterImpl.makeComputed(result, getterDecl, setterDecl);
return result;
}
// MARK: C++ increment operator
/// Synthesizer callback for a successor function.
///
/// \code
/// var __copy: Self
/// __copy = self
/// __copy.__operatorPlusPlus()
/// return __copy
/// \endcode
static std::pair<BraceStmt *, bool>
synthesizeSuccessorFuncBody(AbstractFunctionDecl *afd, void *context) {
auto successorDecl = cast<FuncDecl>(afd);
auto incrementImpl = static_cast<FuncDecl *>(context);
ASTContext &ctx = successorDecl->getASTContext();
auto emptyTupleTy = TupleType::getEmpty(ctx);
auto returnTy = successorDecl->getResultInterfaceType();
auto selfDecl = successorDecl->getImplicitSelfDecl();
auto selfRefExpr = new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(),
/*implicit*/ true);
selfRefExpr->setType(selfDecl->getInterfaceType());
// Create a `__copy` variable.
VarDecl *copyDecl = nullptr;
PatternBindingDecl *patternDecl = nullptr;
std::tie(copyDecl, patternDecl) = SwiftDeclSynthesizer::createVarWithPattern(
successorDecl, ctx.getIdentifier("__copy"), returnTy,
VarDecl::Introducer::Var,
/*isImplicit*/ true, successorDecl->getFormalAccess(),
successorDecl->getFormalAccess());
auto copyRefLValueExpr = new (ctx) DeclRefExpr(copyDecl, DeclNameLoc(),
/*implicit*/ true);
copyRefLValueExpr->setType(LValueType::get(copyDecl->getInterfaceType()));
// Copy `self` to `__copy`.
auto copyAssignExpr = new (ctx) AssignExpr(copyRefLValueExpr, SourceLoc(),
selfRefExpr, /*implicit*/ true);
copyAssignExpr->setType(emptyTupleTy);
// Call `operator++`.
auto incrementExpr = createAccessorImplCallExpr(
incrementImpl, Argument::implicitInOut(ctx, copyRefLValueExpr), {});
auto copyRefRValueExpr = new (ctx) DeclRefExpr(copyDecl, DeclNameLoc(),
/*implicit*/ true);
copyRefRValueExpr->setType(copyDecl->getInterfaceType());
auto *returnStmt = ReturnStmt::createImplicit(ctx, copyRefRValueExpr);
auto body = BraceStmt::create(ctx, SourceLoc(),
{
copyDecl,
patternDecl,
copyAssignExpr,
incrementExpr,
returnStmt,
},
SourceLoc());
return {body, /*isTypeChecked*/ true};
}
FuncDecl *SwiftDeclSynthesizer::makeSuccessorFunc(FuncDecl *incrementFunc) {
auto &ctx = ImporterImpl.SwiftContext;
auto dc = incrementFunc->getDeclContext();
auto returnTy = incrementFunc->getImplicitSelfDecl()->getInterfaceType();
auto nameId = ctx.getIdentifier("successor");
auto *params = ParameterList::createEmpty(ctx);
DeclName name(ctx, DeclBaseName(nameId), params);
auto result = FuncDecl::createImplicit(
ctx, StaticSpellingKind::None, name, SourceLoc(),
/*Async*/ false, /*Throws*/ false, /*ThrownType=*/Type(),
/*GenericParams*/ nullptr, params, returnTy, dc);
result->copyFormalAccessFrom(incrementFunc);
result->setIsDynamic(false);
result->setBodySynthesizer(synthesizeSuccessorFuncBody, incrementFunc);
return result;
}
// MARK: C++ arithmetic operators
static std::pair<BraceStmt *, bool>
synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) {
ASTContext &ctx = afd->getASTContext();
auto funcDecl = cast<FuncDecl>(afd);
auto methodDecl =
static_cast<FuncDecl *>(context); /* Swift version of CXXMethod */
SmallVector<Argument, 8> forwardingArgs;
// We start from +1 since the first param is our lhs. All other params are
// forwarded
for (auto itr = funcDecl->getParameters()->begin() + 1;
itr != funcDecl->getParameters()->end(); itr++) {
auto param = *itr;
auto isInOut = param->isInOut();
auto paramTy = param->getTypeInContext();
Expr *paramRefExpr =
new (ctx) DeclRefExpr(param, DeclNameLoc(), /*Implicit*/ true);
paramRefExpr->setType(isInOut ? LValueType::get(paramTy) : paramTy);
auto arg = isInOut ? Argument::implicitInOut(ctx, paramRefExpr)
: Argument::unlabeled(paramRefExpr);
forwardingArgs.push_back(arg);
}
auto methodExpr =
new (ctx) DeclRefExpr(methodDecl, DeclNameLoc(), /*implicit*/ true);
methodExpr->setType(methodDecl->getInterfaceType());
// Lhs parameter
auto baseParam = funcDecl->getParameters()->front();
auto baseParamTy = baseParam->getTypeInContext();
auto baseIsInOut = baseParam->isInOut();
Expr *baseExpr =
new (ctx) DeclRefExpr(baseParam, DeclNameLoc(), /*implicit*/ true);
baseExpr->setType(baseIsInOut ? LValueType::get(baseParamTy) : baseParamTy);
auto baseArg = baseIsInOut ? Argument::implicitInOut(ctx, baseExpr)
: Argument::unlabeled(baseExpr);
auto dotCallExpr =
DotSyntaxCallExpr::create(ctx, methodExpr, SourceLoc(), baseArg);
dotCallExpr->setType(methodDecl->getMethodInterfaceType());
dotCallExpr->setThrows(nullptr);
auto *argList = ArgumentList::createImplicit(ctx, forwardingArgs);
auto callExpr = CallExpr::createImplicit(ctx, dotCallExpr, argList);
callExpr->setType(funcDecl->getResultInterfaceType());
callExpr->setThrows(nullptr);
auto *returnStmt = ReturnStmt::createImplicit(ctx, callExpr);
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
/*implicit*/ true);
return {body, /*isTypeChecked*/ true};
}
clang::CXXMethodDecl *SwiftDeclSynthesizer::synthesizeCXXForwardingMethod(
const clang::CXXRecordDecl *derivedClass,
const clang::CXXRecordDecl *baseClass, const clang::CXXMethodDecl *method,
ForwardingMethodKind forwardingMethodKind,
ReferenceReturnTypeBehaviorForBaseMethodSynthesis
referenceReturnTypeBehavior,
bool forceConstQualifier) {
auto &clangCtx = ImporterImpl.getClangASTContext();
auto &clangSema = ImporterImpl.getClangSema();
assert(!method->isStatic() ||
method->getNameInfo().getName().getCXXOverloadedOperator() ==
clang::OO_Call);
// Create a new method in the derived class that calls the base method.
clang::DeclarationName name = method->getNameInfo().getName();
if (name.isIdentifier()) {
std::string newName;
llvm::raw_string_ostream os(newName);
os << (forwardingMethodKind == ForwardingMethodKind::Virtual
? "__synthesizedVirtualCall_"
: "__synthesizedBaseCall_")
<< name.getAsIdentifierInfo()->getName();
name = clang::DeclarationName(
&ImporterImpl.getClangPreprocessor().getIdentifierTable().get(
os.str()));
} else if (name.getCXXOverloadedOperator() == clang::OO_Subscript) {
name = clang::DeclarationName(
&ImporterImpl.getClangPreprocessor().getIdentifierTable().get(
(forwardingMethodKind == ForwardingMethodKind::Virtual
? "__synthesizedVirtualCall_operatorSubscript"
: "__synthesizedBaseCall_operatorSubscript")));
} else if (name.getCXXOverloadedOperator() == clang::OO_Star) {
name = clang::DeclarationName(
&ImporterImpl.getClangPreprocessor().getIdentifierTable().get(
(forwardingMethodKind == ForwardingMethodKind::Virtual
? "__synthesizedVirtualCall_operatorStar"
: "__synthesizedBaseCall_operatorStar")));
} else if (name.getCXXOverloadedOperator() == clang::OO_Call) {
assert(forwardingMethodKind != ForwardingMethodKind::Virtual);
name = clang::DeclarationName(
&ImporterImpl.getClangPreprocessor().getIdentifierTable().get(
"__synthesizedBaseCall_operatorCall"));
}
auto methodType = method->getType();
// Check if we need to drop the reference from the return type
// of the new method. This is needed when a synthesized `operator []`
// derived-to-base call is invoked from Swift's subscript getter.
if (referenceReturnTypeBehavior !=
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference) {
if (const auto *fpt = methodType->getAs<clang::FunctionProtoType>()) {
auto retType = fpt->getReturnType();
if (retType->isReferenceType() &&
(referenceReturnTypeBehavior ==
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::
RemoveReference ||
(referenceReturnTypeBehavior ==
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::
RemoveReferenceIfPointer &&
retType->getPointeeType()->isPointerType()))) {
methodType = clangCtx.getFunctionType(retType->getPointeeType(),
fpt->getParamTypes(),
fpt->getExtProtoInfo());
}
}
}
// Check if this method requires an additional `const` qualifier.
// This might needed when a non-const synthesized `operator []`
// derived-to-base call is invoked from Swift's subscript getter.
bool castThisToNonConstThis = false;
if (forceConstQualifier) {
if (const auto *fpt = methodType->getAs<clang::FunctionProtoType>()) {
auto info = fpt->getExtProtoInfo();
if (!info.TypeQuals.hasConst()) {
info.TypeQuals.addConst();
castThisToNonConstThis = true;
methodType = clangCtx.getFunctionType(fpt->getReturnType(),
fpt->getParamTypes(), info);
}
}
}
auto newMethod = clang::CXXMethodDecl::Create(
clangCtx, const_cast<clang::CXXRecordDecl *>(derivedClass),
method->getSourceRange().getBegin(),
clang::DeclarationNameInfo(name, clang::SourceLocation()), methodType,
method->getTypeSourceInfo(),
method->isStatic() ? clang::SC_None : method->getStorageClass(),
method->UsesFPIntrin(), /*isInline=*/true, method->getConstexprKind(),
method->getSourceRange().getEnd());
newMethod->setImplicit();
newMethod->setImplicitlyInline();
newMethod->setAccess(clang::AccessSpecifier::AS_public);
newMethod->addAttr(clang::NoDebugAttr::CreateImplicit(clangCtx));
if (method->hasAttr<clang::CFReturnsRetainedAttr>()) {
// Return an FRT field at +1 if the base method also follows this
// convention.
newMethod->addAttr(clang::CFReturnsRetainedAttr::CreateImplicit(clangCtx));
}
if (auto swiftNameAttr = method->getAttr<clang::SwiftNameAttr>())
newMethod->addAttr(swiftNameAttr->clone(clangCtx));
llvm::SmallVector<clang::ParmVarDecl *, 4> params;
for (size_t i = 0; i < method->getNumParams(); ++i) {
const auto &param = *method->getParamDecl(i);
params.push_back(clang::ParmVarDecl::Create(
clangCtx, newMethod, param.getSourceRange().getBegin(),
param.getLocation(), param.getIdentifier(), param.getType(),
param.getTypeSourceInfo(), param.getStorageClass(),
/*DefExpr=*/nullptr));
}
newMethod->setParams(params);
clang::Sema::SynthesizedFunctionScope scope(clangSema, newMethod);
// Create a new Clang diagnostic pool to capture any diagnostics
// emitted during the construction of the method.
clang::sema::DelayedDiagnosticPool diagPool{
clangSema.DelayedDiagnostics.getCurrentPool()};
auto diagState = clangSema.DelayedDiagnostics.push(diagPool);
// Construct the method's body.
clang::Expr *thisExpr = clang::CXXThisExpr::Create(
clangCtx, clang::SourceLocation(), newMethod->getThisType(),
/*IsImplicit=*/false);
if (castThisToNonConstThis) {
auto baseClassPtr =
clangCtx.getPointerType(clangCtx.getRecordType(derivedClass));
clang::CastKind Kind;
clang::CXXCastPath Path;
clangSema.CheckPointerConversion(thisExpr, baseClassPtr, Kind, Path,
/*IgnoreBaseAccess=*/false,
/*Diagnose=*/true);
auto conv = clangSema.ImpCastExprToType(thisExpr, baseClassPtr, Kind,
clang::VK_PRValue, &Path);
if (!conv.isUsable())
return nullptr;
thisExpr = conv.get();
}
auto memberExprTy =
(method->isStatic() && method->getOverloadedOperator() ==
clang::OverloadedOperatorKind::OO_Call)
? method->getType()
: clangCtx.BoundMemberTy;
auto memberExpr = clangSema.BuildMemberExpr(
thisExpr, /*isArrow=*/true, clang::SourceLocation(),
clang::NestedNameSpecifierLoc(), clang::SourceLocation(),
const_cast<clang::CXXMethodDecl *>(method),
clang::DeclAccessPair::make(const_cast<clang::CXXMethodDecl *>(method),
clang::AS_public),
/*HadMultipleCandidates=*/false, method->getNameInfo(),
memberExprTy, clang::VK_PRValue, clang::OK_Ordinary);
llvm::SmallVector<clang::Expr *, 4> args;
for (size_t i = 0; i < newMethod->getNumParams(); ++i) {
auto *param = newMethod->getParamDecl(i);
auto type = param->getType();
clang::Expr *argExpr = new (clangCtx) clang::DeclRefExpr(
clangCtx, param, false, type.getNonReferenceType(),
clang::ExprValueKind::VK_LValue, clang::SourceLocation());
if (type->isRValueReferenceType()) {
argExpr = clangSema
.BuildCXXNamedCast(
clang::SourceLocation(), clang::tok::kw_static_cast,
clangCtx.getTrivialTypeSourceInfo(type), argExpr,
clang::SourceRange(), clang::SourceRange())
.get();
}
args.push_back(argExpr);
}
auto memberCall = clangSema.BuildCallExpr(
nullptr, memberExpr, clang::SourceLocation(), args,
clang::SourceLocation());
if (!memberCall.isUsable())
return nullptr;
auto returnStmt =
clangSema.BuildReturnStmt(clang::SourceLocation(), memberCall.get())
.get();
// Check if there were any Clang errors during the construction
// of the method body.
clangSema.DelayedDiagnostics.popWithoutEmitting(diagState);
if (!diagPool.empty())
return nullptr;
newMethod->setBody(returnStmt);
return newMethod;
}
FuncDecl *
SwiftDeclSynthesizer::makeOperator(FuncDecl *operatorMethod,
clang::OverloadedOperatorKind opKind) {
assert(opKind != clang::OverloadedOperatorKind::OO_None &&
"expected a C++ operator");
auto &ctx = ImporterImpl.SwiftContext;
auto opName = clang::getOperatorSpelling(opKind);
auto paramList = operatorMethod->getParameters();
auto genericParamList = operatorMethod->getGenericParams();
auto opId = ctx.getIdentifier(opName);
auto parentCtx = operatorMethod->getDeclContext();
auto lhsParam =
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
ctx.getIdentifier("lhs"), parentCtx);
lhsParam->setInterfaceType(
operatorMethod->getDeclContext()->getSelfInterfaceType());
if (operatorMethod->isMutating()) {
// This implicitly makes the parameter indirect.
lhsParam->setSpecifier(ParamSpecifier::InOut);
} else {
lhsParam->setSpecifier(ParamSpecifier::Default);
}
SmallVector<ParamDecl *, 4> newParams;
newParams.push_back(lhsParam);
for (auto param : *paramList) {
auto clonedParam = ParamDecl::clone(ctx, param);
if (clonedParam->getParameterName().empty()) {
clonedParam->setName(ctx.getIdentifier("other"));
}
newParams.push_back(clonedParam);
}
auto oldArgNames = operatorMethod->getName().getArgumentNames();
SmallVector<Identifier, 4> newArgNames;
newArgNames.push_back(Identifier());
for (auto id : oldArgNames) {
newArgNames.push_back(id);
}
auto opDeclName =
DeclName(ctx, opId, {newArgNames.begin(), newArgNames.end()});
auto topLevelStaticFuncDecl = FuncDecl::createImplicit(
ctx, StaticSpellingKind::None, opDeclName, SourceLoc(),
/*Async*/ false, /*Throws*/ false, /*ThrownType=*/Type(),
genericParamList, ParameterList::create(ctx, newParams),
operatorMethod->getResultInterfaceType(), parentCtx);
topLevelStaticFuncDecl->copyFormalAccessFrom(operatorMethod);
topLevelStaticFuncDecl->setIsDynamic(false);
topLevelStaticFuncDecl->setStatic();
topLevelStaticFuncDecl->setBodySynthesizer(synthesizeOperatorMethodBody,
operatorMethod);
// If this is a unary prefix operator (e.g. `!`), add a `prefix` attribute.
size_t numParams = operatorMethod->getParameters()->size();
if (numParams == 0 || (operatorMethod->isStatic() && numParams == 1)) {
topLevelStaticFuncDecl->addAttribute(new (ctx) PrefixAttr(SourceLoc()));
}
return topLevelStaticFuncDecl;
}
// MARK: C++ virtual methods
FuncDecl *SwiftDeclSynthesizer::makeVirtualMethod(
const clang::CXXMethodDecl *clangMethodDecl, StringRef swiftName) {
auto clangDC = clangMethodDecl->getParent();
auto &ctx = ImporterImpl.SwiftContext;
assert(!clangMethodDecl->isStatic() &&
"C++ virtual functions cannot be static");
auto newMethod = synthesizeCXXForwardingMethod(
clangDC, clangDC, clangMethodDecl, ForwardingMethodKind::Virtual,
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
/*forceConstQualifier*/ false);
// If the override has a swift_name different from the base
// method, we ignore the swift_name attribute and instead use the base method's name.
// In this case, swiftName holds the correct derived method name obtained through NameImporter
if (clangMethodDecl->size_overridden_methods() > 0) {
if (auto oldSwiftNameAttr = newMethod->getAttr<clang::SwiftNameAttr>()) {
auto oldSwiftName = oldSwiftNameAttr->getName();
if (swiftName != oldSwiftName) {
ImporterImpl.diagnose(HeaderLoc(oldSwiftNameAttr->getLoc()),
diag::swift_name_attr_ignored,
oldSwiftName);
oldSwiftNameAttr->setName(newMethod->getASTContext(), swiftName);
}
} else {
newMethod->addAttr(clang::SwiftNameAttr::CreateImplicit(
newMethod->getASTContext(), swiftName));
}
}
auto result = dyn_cast_or_null<FuncDecl>(
ctx.getClangModuleLoader()->importDeclDirectly(newMethod));
return result;
}
// MARK: C++ operators
FuncDecl *SwiftDeclSynthesizer::makeInstanceToStaticOperatorCallMethod(
const clang::CXXMethodDecl *clangMethodDecl) {
auto clangDC = clangMethodDecl->getParent();
auto &ctx = ImporterImpl.SwiftContext;
assert(clangMethodDecl->isStatic() && "Expected a static operator");
auto newMethod = synthesizeCXXForwardingMethod(
clangDC, clangDC, clangMethodDecl, ForwardingMethodKind::Base,
ReferenceReturnTypeBehaviorForBaseMethodSynthesis::KeepReference,
/*forceConstQualifier*/ true);
newMethod->addAttr(clang::SwiftNameAttr::CreateImplicit(
clangMethodDecl->getASTContext(), "callAsFunction"));
auto result = dyn_cast_or_null<FuncDecl>(
ctx.getClangModuleLoader()->importDeclDirectly(newMethod));
return result;
}
// MARK: C++ properties
static std::pair<BraceStmt *, bool>
synthesizeComputedGetterFromCXXMethod(AbstractFunctionDecl *afd,
void *context) {
auto accessor = cast<AccessorDecl>(afd);
auto method = static_cast<FuncDecl *>(context);
auto selfArg = createSelfArg(accessor);
auto *getterImplCallExpr = createAccessorImplCallExpr(method, selfArg, {});
auto &ctx = method->getASTContext();
auto *returnStmt = ReturnStmt::createImplicit(ctx, getterImplCallExpr);
auto *body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc());
return {body, /*isTypeChecked*/ true};
}
static std::pair<BraceStmt *, bool>
synthesizeComputedSetterFromCXXMethod(AbstractFunctionDecl *afd,
void *context) {
auto setterDecl = cast<AccessorDecl>(afd);
auto setterImpl = static_cast<FuncDecl *>(context);
auto selfArg = createSelfArg(setterDecl);
DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0);
auto *getterImplCallExpr =
createAccessorImplCallExpr(setterImpl, selfArg, {valueParamRefExpr});
auto body = BraceStmt::create(setterImpl->getASTContext(), SourceLoc(),
{getterImplCallExpr}, SourceLoc());
return {body, /*isTypeChecked*/ true};
}
VarDecl *
SwiftDeclSynthesizer::makeComputedPropertyFromCXXMethods(FuncDecl *getter,
FuncDecl *setter) {
auto &ctx = ImporterImpl.SwiftContext;
auto dc = getter->getDeclContext();
assert(isa<clang::CXXMethodDecl>(getter->getClangDecl()) &&
(!setter || isa<clang::CXXMethodDecl>(setter->getClangDecl())) &&
"Functions passed to makeProperty must be imported C++ method decls.");
CXXMethodBridging bridgingInfo(
cast<clang::CXXMethodDecl>(getter->getClangDecl()));
assert(bridgingInfo.classify() == CXXMethodBridging::Kind::getter);
auto importedName = bridgingInfo.importNameAsCamelCaseName();
auto result =
new (ctx) VarDecl(false, VarDecl::Introducer::Var, getter->getStartLoc(),
ctx.getIdentifier(importedName), dc);
result->setInterfaceType(getter->getResultInterfaceType());
result->copyFormalAccessFrom(getter);
result->setImplInfo(StorageImplInfo::getMutableComputed());
AccessorDecl *getterDecl = AccessorDecl::create(
ctx, getter->getLoc(), getter->getLoc(), AccessorKind::Get, result,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
ParameterList::createEmpty(ctx),
getter->getResultInterfaceType(), dc);
getterDecl->copyFormalAccessFrom(getter);
getterDecl->setImplicit();
getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(true);
getterDecl->setBodySynthesizer(synthesizeComputedGetterFromCXXMethod, getter);
if (getter->isMutating()) {
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
result->setIsGetterMutating(true);
}
AccessorDecl *setterDecl = nullptr;
if (setter) {
auto paramVarDecl =
new (ctx) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(),
ctx.getIdentifier("newValue"), dc);
paramVarDecl->setSpecifier(ParamSpecifier::Default);
paramVarDecl->setInterfaceType(getter->getResultInterfaceType());
auto setterParamList = ParameterList::create(ctx, {paramVarDecl});
setterDecl = AccessorDecl::create(
ctx, setter->getLoc(), setter->getLoc(), AccessorKind::Set, result,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), /*thrownType*/ TypeLoc(),
setterParamList, setter->getResultInterfaceType(), dc);
setterDecl->copyFormalAccessFrom(setter);
setterDecl->setImplicit();
setterDecl->setIsDynamic(false);
setterDecl->setIsTransparent(true);
setterDecl->setBodySynthesizer(synthesizeComputedSetterFromCXXMethod,
setter);
if (setter->isMutating()) {
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
result->setIsSetterMutating(true);
} else {
setterDecl->setSelfAccessKind(SelfAccessKind::NonMutating);
result->setIsSetterMutating(false);
}
}
ImporterImpl.makeComputed(result, getterDecl, setterDecl);
return result;
}
static std::pair<BraceStmt *, bool>
synthesizeDefaultArgumentBody(AbstractFunctionDecl *afd, void *context) {
auto funcDecl = cast<FuncDecl>(afd);
auto clangParam = static_cast<const clang::ParmVarDecl *>(context);
auto clangFuncDecl = cast<clang::FunctionDecl>(clangParam->getDeclContext());
ASTContext &ctx = funcDecl->getASTContext();
clang::ASTContext &clangCtx = clangParam->getASTContext();
clang::Sema &clangSema = ctx.getClangModuleLoader()->getClangSema();
auto clangDeclName = clang::DeclarationName(
&clangCtx.Idents.get(("__cxx" + funcDecl->getNameStr()).str()));
auto clangDeclContext = clangCtx.getTranslationUnitDecl();
// The following also instantiates the default argument if needed.
auto defaultArgCallExpr = clangSema.BuildCXXDefaultArgExpr(
clang::SourceLocation(), const_cast<clang::FunctionDecl *>(clangFuncDecl),
const_cast<clang::ParmVarDecl *>(clangParam));
if (!defaultArgCallExpr.isUsable())
return {nullptr, /*isTypeChecked=*/true};
// The following requires the default argument to be instantiated.
clang::QualType clangParamTy = clangParam->getDefaultArg()->getType();
clang::QualType funcTy = clangCtx.getFunctionType(
clangParamTy, {}, clang::FunctionProtoType::ExtProtoInfo());
// Synthesize `return {default expr};`.
auto defaultArgReturnStmt = clang::ReturnStmt::Create(
clangCtx, clang::SourceLocation(), defaultArgCallExpr.get(), nullptr);
// Synthesize `ParamTy __cxx__defaultArg_XYZ() { return {default expr}; }`.
auto defaultArgFuncDecl = clang::FunctionDecl::Create(
clangCtx, clangDeclContext, clang::SourceLocation(),
clang::SourceLocation(), clangDeclName, funcTy,
clangCtx.getTrivialTypeSourceInfo(clangParamTy),
clang::StorageClass::SC_Static);
defaultArgFuncDecl->setImplicit();
defaultArgFuncDecl->setImplicitlyInline();
defaultArgFuncDecl->setAccess(clang::AccessSpecifier::AS_public);
defaultArgFuncDecl->setBody(defaultArgReturnStmt);
// Import `func __cxx__defaultArg_XYZ() -> ParamTY` into Swift.
auto defaultArgGenerator = dyn_cast_or_null<FuncDecl>(
ctx.getClangModuleLoader()->importDeclDirectly(defaultArgFuncDecl));
if (!defaultArgGenerator)
return {nullptr, /*isTypeChecked=*/true};
auto defaultArgGeneratorRef = new (ctx) DeclRefExpr(
ConcreteDeclRef(defaultArgGenerator), DeclNameLoc(), /*Implicit=*/true);
defaultArgGeneratorRef->setType(defaultArgGenerator->getInterfaceType());
// Synthesize a call to `__cxx__defaultArg_XYZ()`.
auto initCall = CallExpr::createImplicit(
ctx, defaultArgGeneratorRef, ArgumentList::createImplicit(ctx, {}));
initCall->setType(defaultArgGenerator->getResultInterfaceType());
initCall->setThrows(nullptr);
// Synthesize `return __cxx__defaultArg_XYZ()`.
auto *returnStmt = ReturnStmt::createImplicit(ctx, initCall);
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
/*implicit=*/true);
return {body, /*isTypeChecked=*/true};
}
CallExpr *
SwiftDeclSynthesizer::makeDefaultArgument(const clang::ParmVarDecl *param,
const swift::Type &swiftParamTy,
SourceLoc paramLoc) {
assert(param->hasDefaultArg() && "must have a C++ default argument");
ASTContext &ctx = ImporterImpl.SwiftContext;
clang::ASTContext &clangCtx = param->getASTContext();
auto clangFunc =
cast<clang::FunctionDecl>(param->getParentFunctionOrMethod());
if (isa<clang::CXXConstructorDecl>(clangFunc))
// TODO: support default arguments of constructors
// (https://github.com/apple/swift/issues/70124)
return nullptr;
std::string s;
llvm::raw_string_ostream os(s);
std::unique_ptr<clang::ItaniumMangleContext> mangler{
clang::ItaniumMangleContext::create(clangCtx, clangCtx.getDiagnostics())};
os << "__defaultArg_" << param->getFunctionScopeIndex() << "_";
ImporterImpl.getMangledName(mangler.get(), clangFunc, os);
// Synthesize `func __defaultArg_XYZ() -> ParamTy { ... }`.
DeclName funcName(ctx, DeclBaseName(ctx.getIdentifier(s)),
ParameterList::createEmpty(ctx));
auto funcDecl = FuncDecl::createImplicit(
ctx, StaticSpellingKind::None, funcName, paramLoc, false, false, Type(),
{}, ParameterList::createEmpty(ctx), swiftParamTy,
ImporterImpl.ImportedHeaderUnit);
funcDecl->setBodySynthesizer(synthesizeDefaultArgumentBody, (void *)param);
funcDecl->setAccess(AccessLevel::Public);
funcDecl->addAttribute(new (ctx)
AlwaysEmitIntoClientAttr(/*IsImplicit=*/true));
// At this point, the parameter/return types of funcDecl might not be imported
// into Swift completely, meaning that their protocol conformances might not
// be populated yet. Prevent LifetimeDependenceInfoRequest from prematurely
// populating the conformance table for the types involved.
ctx.evaluator.cacheOutput(LifetimeDependenceInfoRequest{funcDecl}, {});
ImporterImpl.defaultArgGenerators[param] = funcDecl;
auto declRefExpr = new (ctx)
DeclRefExpr(ConcreteDeclRef(funcDecl), DeclNameLoc(), /*Implicit*/ true);
declRefExpr->setType(funcDecl->getInterfaceType());
declRefExpr->setFunctionRefInfo(FunctionRefInfo::singleBaseNameApply());
auto callExpr = CallExpr::createImplicit(
ctx, declRefExpr, ArgumentList::forImplicitUnlabeled(ctx, {}));
callExpr->setType(funcDecl->getResultInterfaceType());
callExpr->setThrows(nullptr);
return callExpr;
}
// MARK: C++ foreign reference type constructors
llvm::SmallVector<clang::CXXMethodDecl *, 4>
SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef(
const clang::CXXRecordDecl *cxxRecordDecl) {
if (!cxxRecordDecl->isCompleteDefinition() || cxxRecordDecl->isAbstract())
return {};
clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext();
clang::Sema &clangSema = ImporterImpl.getClangSema();
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
clang::SourceLocation cxxRecordDeclLoc = cxxRecordDecl->getLocation();
llvm::SmallVector<clang::CXXConstructorDecl *, 4> ctorDeclsForSynth;
for (clang::CXXConstructorDecl *ctorDecl : cxxRecordDecl->ctors()) {
if (ctorDecl->isDeleted() || ctorDecl->getAccess() == clang::AS_private ||
ctorDecl->getAccess() == clang::AS_protected ||
ctorDecl->isCopyOrMoveConstructor() || ctorDecl->isVariadic())
continue;
bool hasDefaultArg = !ctorDecl->parameters().empty() &&
ctorDecl->parameters().back()->hasDefaultArg();
// TODO: Add support for default args in ctors for C++ foreign reference
// types.
if (hasDefaultArg)
continue;
ctorDeclsForSynth.push_back(ctorDecl);
}
if (ctorDeclsForSynth.empty())
return {};
clang::FunctionDecl *operatorNew = nullptr;
clang::FunctionDecl *operatorDelete = nullptr;
clang::ImplicitAllocationParameters IAP(clang::AlignedAllocationMode::No);
clang::Sema::SFINAETrap trap(clangSema);
bool findingAllocFuncFailed = clangSema.FindAllocationFunctions(
cxxRecordDeclLoc, clang::SourceRange(),
clang::AllocationFunctionScope::Both,
clang::AllocationFunctionScope::Both, cxxRecordTy, /*IsArray=*/false, IAP,
clang::MultiExprArg(), operatorNew, operatorDelete,
/*Diagnose=*/false);
if (trap.hasErrorOccurred() || findingAllocFuncFailed || !operatorNew ||
operatorNew->isDeleted() ||
operatorNew->getAccess() == clang::AS_private ||
operatorNew->getAccess() == clang::AS_protected)
return {};
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
// Adding `_Nonnull` to the return type of synthesized static factory
bool nullabilityCannotBeAdded =
clangSema.CheckImplicitNullabilityTypeSpecifier(
cxxRecordPtrTy, clang::NullabilityKind::NonNull, cxxRecordDeclLoc,
/*isParam=*/false, /*OverrideExisting=*/true);
assert(!nullabilityCannotBeAdded &&
"Failed to add _Nonnull specifier to synthesized "
"static factory's return type");
clang::IdentifierTable &clangIdents = clangCtx.Idents;
llvm::SmallVector<clang::CXXMethodDecl *, 4> synthesizedFactories;
unsigned int selectedCtorDeclCounter = 0;
for (clang::CXXConstructorDecl *selectedCtorDecl : ctorDeclsForSynth) {
unsigned int ctorParamCount = selectedCtorDecl->getNumParams();
selectedCtorDeclCounter++;
std::string funcName = "__returns_" + cxxRecordDecl->getNameAsString();
if (ctorParamCount > 0)
funcName += "_" + std::to_string(ctorParamCount) + "_params";
funcName += "_" + std::to_string(selectedCtorDeclCounter);
clang::IdentifierInfo *funcNameToSynth = &clangIdents.get(funcName);
auto ctorFunctionProtoTy =
selectedCtorDecl->getType()->getAs<clang::FunctionProtoType>();
clang::ArrayRef<clang::QualType> paramTypes =
ctorFunctionProtoTy->getParamTypes();
clang::FunctionProtoType::ExtProtoInfo EPI;
clang::QualType funcTypeToSynth =
clangCtx.getFunctionType(cxxRecordPtrTy, paramTypes, EPI);
clang::CXXMethodDecl *synthCxxMethodDecl = clang::CXXMethodDecl::Create(
clangCtx, const_cast<clang::CXXRecordDecl *>(cxxRecordDecl),
cxxRecordDeclLoc,
clang::DeclarationNameInfo(funcNameToSynth, cxxRecordDeclLoc),
funcTypeToSynth, clangCtx.getTrivialTypeSourceInfo(funcTypeToSynth),
clang::SC_Static, /*UsesFPIntrin=*/false, /*isInline=*/true,
clang::ConstexprSpecKind::Unspecified, cxxRecordDeclLoc);
assert(
synthCxxMethodDecl &&
"Unable to synthesize static factory for c++ foreign reference type");
synthCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
llvm::SmallVector<clang::ParmVarDecl *, 4> synthParams;
for (unsigned int i = 0; i < ctorParamCount; ++i) {
auto *origParam = selectedCtorDecl->getParamDecl(i);
clang::IdentifierInfo *paramIdent = origParam->getIdentifier();
if (!paramIdent) {
std::string dummyName = "__unnamed_param_" + std::to_string(i);
paramIdent = &clangIdents.get(dummyName);
}
auto *param = clang::ParmVarDecl::Create(
clangCtx, synthCxxMethodDecl, cxxRecordDeclLoc, cxxRecordDeclLoc,
paramIdent, origParam->getType(),
clangCtx.getTrivialTypeSourceInfo(origParam->getType()),
clang::SC_None, /*DefArg=*/nullptr);
param->setIsUsed();
synthParams.push_back(param);
}
synthCxxMethodDecl->setParams(synthParams);
if (!hasImmortalAttrs(cxxRecordDecl)) {
synthCxxMethodDecl->addAttr(
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained"));
}
std::string swiftInitStr = "init(";
for (unsigned i = 0; i < ctorParamCount; ++i) {
auto paramType = selectedCtorDecl->getParamDecl(i)->getType();
if (paramType->isRValueReferenceType()) {
swiftInitStr += "consuming:";
} else {
swiftInitStr += "_:";
}
}
swiftInitStr += ")";
synthCxxMethodDecl->addAttr(
clang::SwiftNameAttr::Create(clangCtx, swiftInitStr));
llvm::SmallVector<clang::Expr *, 4> ctorArgs;
for (auto *param : synthParams) {
clang::QualType paramTy = param->getType();
clang::QualType exprTy = paramTy.getNonReferenceType();
clang::Expr *argExpr = clang::DeclRefExpr::Create(
clangCtx, clang::NestedNameSpecifierLoc(), cxxRecordDeclLoc, param,
/*RefersToEnclosingVariableOrCapture=*/false, cxxRecordDeclLoc,
exprTy, clang::VK_LValue);
if (paramTy->isRValueReferenceType()) {
argExpr = clangSema
.BuildCXXNamedCast(
cxxRecordDeclLoc, clang::tok::kw_static_cast,
clangCtx.getTrivialTypeSourceInfo(paramTy), argExpr,
clang::SourceRange(), clang::SourceRange())
.get();
}
ctorArgs.push_back(argExpr);
}
llvm::SmallVector<clang::Expr *, 4> ctorArgsToAdd;
if (clangSema.CompleteConstructorCall(selectedCtorDecl, cxxRecordTy,
ctorArgs, cxxRecordDeclLoc,
ctorArgsToAdd))
continue;
clang::ExprResult synthCtorExprResult = clangSema.BuildCXXConstructExpr(
cxxRecordDeclLoc, cxxRecordTy, selectedCtorDecl,
/*Elidable=*/false, ctorArgsToAdd,
/*HadMultipleCandidates=*/false,
/*IsListInitialization=*/false,
/*IsStdInitListInitialization=*/false,
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
clang::SourceRange(cxxRecordDeclLoc, cxxRecordDeclLoc));
assert(!synthCtorExprResult.isInvalid() &&
"Unable to synthesize constructor expression for c++ foreign "
"reference type");
clang::Expr *synthCtorExpr = synthCtorExprResult.get();
clang::ExprResult synthNewExprResult = clangSema.BuildCXXNew(
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
clang::SourceRange(cxxRecordDeclLoc, cxxRecordDeclLoc), synthCtorExpr);
assert(
!synthNewExprResult.isInvalid() &&
"Unable to synthesize `new` expression for c++ foreign reference type");
auto *synthNewExpr = cast<clang::CXXNewExpr>(synthNewExprResult.get());
clang::ReturnStmt *synthRetStmt = clang::ReturnStmt::Create(
clangCtx, cxxRecordDeclLoc, synthNewExpr, /*NRVOCandidate=*/nullptr);
assert(synthRetStmt && "Unable to synthesize return statement for "
"static factory of c++ foreign reference type");
clang::CompoundStmt *synthFuncBody = clang::CompoundStmt::Create(
clangCtx, {synthRetStmt}, clang::FPOptionsOverride(), cxxRecordDeclLoc,
cxxRecordDeclLoc);
assert(synthRetStmt && "Unable to synthesize function body for static "
"factory of c++ foreign reference type");
synthCxxMethodDecl->setBody(synthFuncBody);
synthCxxMethodDecl->addAttr(clang::NoDebugAttr::CreateImplicit(clangCtx));
synthCxxMethodDecl->setImplicit();
synthCxxMethodDecl->setImplicitlyInline();
synthesizedFactories.push_back(synthCxxMethodDecl);
}
return synthesizedFactories;
}
static std::pair<BraceStmt *, bool>
synthesizeAvailabilityDomainPredicateBody(AbstractFunctionDecl *afd,
void *context) {
auto clangVarDecl = static_cast<const clang::VarDecl *>(context);
clang::ASTContext &clangCtx = clangVarDecl->getASTContext();
auto domainInfo =
clangCtx.getFeatureAvailInfo(const_cast<clang::VarDecl *>(clangVarDecl));
ASSERT(domainInfo.second.Call);
auto funcDecl = cast<FuncDecl>(afd);
ASTContext &ctx = funcDecl->getASTContext();
// FIXME: The need for an intermediate function to call could be eliminated if
// Clang provided the predicate function decl directly, rather than a call
// expression that must be wrapped in a function.
// Synthesize `return {domain predicate expression}`.
auto clangHelperReturnStmt = clang::ReturnStmt::Create(
clangCtx, clang::SourceLocation(), domainInfo.second.Call, nullptr);
// Synthesize `int __XYZ_isAvailable() { return {predicate expr}; }`.
auto clangDeclName = clang::DeclarationName(
&clangCtx.Idents.get("__" + domainInfo.first.str() + "_isAvailable"));
auto clangDeclContext = clangCtx.getTranslationUnitDecl();
clang::QualType funcTy =
clangCtx.getFunctionType(domainInfo.second.Call->getType(), {},
clang::FunctionProtoType::ExtProtoInfo());
auto clangHelperFuncDecl = clang::FunctionDecl::Create(
clangCtx, clangDeclContext, clang::SourceLocation(),
clang::SourceLocation(), clangDeclName, funcTy,
clangCtx.getTrivialTypeSourceInfo(funcTy),
clang::StorageClass::SC_Static);
clangHelperFuncDecl->setImplicit();
clangHelperFuncDecl->setImplicitlyInline();
clangHelperFuncDecl->setBody(clangHelperReturnStmt);
// Import `func __XYZ_isAvailable() -> Bool` into Swift.
auto helperFuncDecl = dyn_cast_or_null<FuncDecl>(
ctx.getClangModuleLoader()->importDeclDirectly(clangHelperFuncDecl));
if (!helperFuncDecl)
return {nullptr, /*isTypeChecked=*/true};
auto helperFuncRef = new (ctx) DeclRefExpr(ConcreteDeclRef(helperFuncDecl),
DeclNameLoc(), /*Implicit=*/true);
helperFuncRef->setType(helperFuncDecl->getInterfaceType());
// Synthesize `__XYZ_isAvailable()`.
auto helperCall = CallExpr::createImplicit(
ctx, helperFuncRef, ArgumentList::createImplicit(ctx, {}));
helperCall->setType(helperFuncDecl->getResultInterfaceType());
helperCall->setThrows(nullptr);
// Synthesize `__XYZ_isAvailable()._value`.
auto *memberRef =
UnresolvedDotExpr::createImplicit(ctx, helperCall, ctx.Id_value_);
// Synthesize `return __XYZ_isAvailable()._value`.
auto *returnStmt = ReturnStmt::createImplicit(ctx, memberRef);
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(),
/*implicit=*/true);
return {body, /*isTypeChecked=*/false};
}
/// Mark the given declaration as always deprecated for the given reason.
static void markDeprecated(Decl *decl, llvm::Twine message) {
ASTContext &ctx = decl->getASTContext();
decl->addAttribute(AvailableAttr::createUniversallyDeprecated(
ctx, ctx.AllocateCopy(message.str())));
}
static bool copyConstructorIsDefaulted(const clang::CXXRecordDecl *decl) {
auto ctor = llvm::find_if(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
return ctor->isCopyConstructor();
});
assert(ctor != decl->ctor_end());
return ctor->isDefaulted();
}
static bool copyAssignOperatorIsDefaulted(const clang::CXXRecordDecl *decl) {
auto copyAssignOp = llvm::find_if(decl->decls(), [](clang::Decl *member) {
if (auto method = dyn_cast<clang::CXXMethodDecl>(member))
return method->isCopyAssignmentOperator();
return false;
});
assert(copyAssignOp != decl->decls_end());
return cast<clang::CXXMethodDecl>(*copyAssignOp)->isDefaulted();
}
/// Recursively checks that there are no user-provided copy constructors or
/// destructors in any fields or base classes.
/// Does not check C++ records with specific API annotations.
static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) {
// Probably a class template that has not yet been specialized:
if (!decl->getDefinition())
return true;
if ((decl->hasUserDeclaredCopyConstructor() &&
!copyConstructorIsDefaulted(decl)) ||
(decl->hasUserDeclaredCopyAssignment() &&
!copyAssignOperatorIsDefaulted(decl)) ||
(decl->hasUserDeclaredDestructor() && decl->getDestructor() &&
!decl->getDestructor()->isDefaulted()))
return false;
auto checkType = [](clang::QualType t) {
if (auto recordType = dyn_cast<clang::RecordType>(t.getCanonicalType())) {
if (auto cxxRecord =
dyn_cast<clang::CXXRecordDecl>(recordType->getDecl())) {
if (hasImportAsRefAttr(cxxRecord) || hasOwnedValueAttr(cxxRecord) ||
hasUnsafeAPIAttr(cxxRecord))
return true;
if (!isSufficientlyTrivial(cxxRecord))
return false;
}
}
return true;
};
for (auto field : decl->fields()) {
if (!checkType(field->getType()))
return false;
}
for (auto base : decl->bases()) {
if (!checkType(base.getType()))
return false;
}
return true;
}
/// Find an explicitly-provided "destroy" operation specified for the
/// given Clang type and return it.
FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy(
NominalTypeDecl *nominal, const clang::RecordDecl *clangType) {
if (!clangType->hasAttrs())
return nullptr;
llvm::SmallPtrSet<FuncDecl *, 2> matchingDestroyFuncs;
llvm::TinyPtrVector<FuncDecl *> nonMatchingDestroyFuncs;
for (auto attr : clangType->getAttrs()) {
auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr);
if (!swiftAttr)
continue;
auto destroyFuncName = swiftAttr->getAttribute();
if (!destroyFuncName.consume_front("destroy:"))
continue;
auto decls = getValueDeclsForName(nominal, destroyFuncName);
for (auto decl : decls) {
auto func = dyn_cast<FuncDecl>(decl);
if (!func)
continue;
auto params = func->getParameters();
if (params->size() != 1) {
nonMatchingDestroyFuncs.push_back(func);
continue;
}
if (!params->get(0)->getInterfaceType()->isEqual(
nominal->getDeclaredInterfaceType())) {
nonMatchingDestroyFuncs.push_back(func);
continue;
}
matchingDestroyFuncs.insert(func);
}
}
switch (matchingDestroyFuncs.size()) {
case 0:
if (!nonMatchingDestroyFuncs.empty()) {
markDeprecated(
nominal,
"destroy function '" +
nonMatchingDestroyFuncs.front()->getName().getBaseName()
.userFacingName() +
"' must have a single parameter with type '" +
nominal->getDeclaredInterfaceType().getString() + "'");
}
return nullptr;
case 1:
// Handled below.
break;
default: {
auto iter = matchingDestroyFuncs.begin();
auto first = *iter++;
auto second = *iter;
markDeprecated(
nominal,
"multiple destroy operations ('" +
first->getName().getBaseName().userFacingName() +
"' and '" +
second->getName().getBaseName().userFacingName() +
"') provided for type");
return nullptr;
}
}
auto destroyFunc = *matchingDestroyFuncs.begin();
// If this type isn't imported as noncopyable, we can't respect the request
// for a destroy operation.
ASTContext &ctx = ImporterImpl.SwiftContext;
auto valueSemanticsKind = evaluateOrDefault(
ctx.evaluator,
CxxValueSemantics({clangType->getTypeForDecl(), &ImporterImpl}), {});
if (valueSemanticsKind != CxxValueSemanticsKind::Copyable &&
valueSemanticsKind != CxxValueSemanticsKind::MoveOnly)
return nullptr;
auto cxxRecordSemanticsKind = evaluateOrDefault(
ctx.evaluator, CxxRecordSemantics({clangType, ctx, &ImporterImpl}), {});
switch (cxxRecordSemanticsKind) {
case CxxRecordSemanticsKind::Value:
case CxxRecordSemanticsKind::Reference:
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(clangType)) {
if (!isSufficientlyTrivial(cxxRecord)) {
markDeprecated(
nominal,
"destroy operation '" +
destroyFunc->getName().getBaseName().userFacingName() +
"' is not allowed on types with a non-trivial destructor");
return nullptr;
}
}
if (valueSemanticsKind == CxxValueSemanticsKind::MoveOnly)
return destroyFunc;
markDeprecated(
nominal,
"destroy operation '" +
destroyFunc->getName().getBaseName().userFacingName() +
"' is only allowed on non-copyable types; "
"did you mean to use SWIFT_NONCOPYABLE?");
return nullptr;
case CxxRecordSemanticsKind::Iterator:
case CxxRecordSemanticsKind::SwiftClassType:
return nullptr;
}
}
/// Function body synthesizer for a deinit of a noncopyable type, which
/// passes "self" to the given "destroy" function.
static std::pair<BraceStmt *, bool>
synthesizeDeinitBodyForCustomDestroy(
AbstractFunctionDecl *deinitFunc, void *opaqueDestroyFunc) {
auto deinit = cast<DestructorDecl>(deinitFunc);
auto destroyFunc = static_cast<FuncDecl *>(opaqueDestroyFunc);
ASTContext &ctx = deinit->getASTContext();
auto funcRef = new (ctx) DeclRefExpr(
destroyFunc, DeclNameLoc(), /*Implicit=*/true);
auto selfRef = new (ctx) DeclRefExpr(
deinit->getImplicitSelfDecl(), DeclNameLoc(), /*Implicit=*/true);
auto callExpr = CallExpr::createImplicit(
ctx, funcRef,
ArgumentList::createImplicit(
ctx,
{ Argument(SourceLoc(), Identifier(), selfRef)}
)
);
auto braceStmt = BraceStmt::createImplicit(ctx, { ASTNode(callExpr) });
return std::make_pair(braceStmt, /*typechecked=*/false);
}
void SwiftDeclSynthesizer::addExplicitDeinitIfRequired(
NominalTypeDecl *nominal, const clang::RecordDecl *clangType) {
auto destroyFunc = findExplicitDestroy(nominal, clangType);
if (!destroyFunc)
return;
ASTContext &ctx = nominal->getASTContext();
auto destructor = new (ctx) DestructorDecl(SourceLoc(), nominal);
destructor->setSynthesized(true);
destructor->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/true);
destructor->setBodySynthesizer(
synthesizeDeinitBodyForCustomDestroy, destroyFunc);
nominal->addMember(destructor);
}
FuncDecl *SwiftDeclSynthesizer::makeAvailabilityDomainPredicate(
const clang::VarDecl *var) {
ASTContext &ctx = ImporterImpl.SwiftContext;
clang::ASTContext &clangCtx = var->getASTContext();
auto featureInfo =
clangCtx.getFeatureAvailInfo(const_cast<clang::VarDecl *>(var));
// If the decl doesn't represent and availability domain, skip it.
if (featureInfo.first.empty())
return nullptr;
// Only dynamic availability domains require a predicate function.
if (featureInfo.second.Kind != clang::FeatureAvailKind::Dynamic)
return nullptr;
if (!featureInfo.second.Call)
return nullptr;
// Synthesize `func __swift_XYZ_isAvailable() -> Builtin.Int1 { ... }`.
std::string s;
llvm::raw_string_ostream os(s);
os << "__swift_" << featureInfo.first << "_isAvailable";
DeclName funcName(ctx, DeclBaseName(ctx.getIdentifier(s)),
ParameterList::createEmpty(ctx));
auto funcDecl = FuncDecl::createImplicit(
ctx, StaticSpellingKind::None, funcName, SourceLoc(), /*Async=*/false,
/*Throws=*/false, Type(), {}, ParameterList::createEmpty(ctx),
BuiltinIntegerType::get(1, ctx), ImporterImpl.ImportedHeaderUnit);
funcDecl->setBodySynthesizer(synthesizeAvailabilityDomainPredicateBody,
(void *)var);
funcDecl->setAccess(AccessLevel::Public);
funcDecl->addAttribute(new (ctx)
AlwaysEmitIntoClientAttr(/*IsImplicit=*/true));
ImporterImpl.availabilityDomainPredicates[var] = funcDecl;
return funcDecl;
}