mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We synthesize a Swift function that only calls a C++ funtion that produces the default argument. We produce this function for each module that imports the C++ function with the default argument. This symbol needs to be public as it is created in the context of the Clang module and used from the Swift module. To avoid the linker errors, we always emit this function into the client which is also the right thing to do as the whole body is a single function call. rdar://138513915
2729 lines
108 KiB
C++
2729 lines
108 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 "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/Expr.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/ClangImporter/CXXMethodBridging.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,
|
|
DeclRefExpr *keyRefExpr = nullptr) {
|
|
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;
|
|
if (keyRefExpr) {
|
|
argList = ArgumentList::forImplicitUnlabeled(ctx, {keyRefExpr});
|
|
} else {
|
|
argList = ArgumentList::forImplicitUnlabeled(ctx, {});
|
|
}
|
|
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->getFormalAccess());
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true));
|
|
var->getAttrs().add(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->getAttrs().add(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->getAttrs().add(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->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true));
|
|
|
|
auto setterDecl =
|
|
makeFieldSetterDecl(ImporterImpl, importedUnionDecl, importedFieldDecl);
|
|
setterDecl->setBodySynthesizer(synthesizeUnionFieldSetterBody,
|
|
importedFieldDecl);
|
|
setterDecl->getAttrs().add(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->getAttrs().add(new (C) TransparentAttr(/*implicit*/ true));
|
|
|
|
auto setterDecl =
|
|
makeFieldSetterDecl(ImporterImpl, importedStructDecl, importedFieldDecl);
|
|
setterDecl->getAttrs().add(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->getAttrs().add(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->getAttrs().add(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);
|
|
DeclRefExpr *keyRefExpr = getterDecl->getParameters()->size() == 0
|
|
? nullptr
|
|
: createParamRefExpr(getterDecl, 0);
|
|
|
|
Type elementTy = getterDecl->getResultInterfaceType();
|
|
|
|
auto *getterImplCallExpr =
|
|
createAccessorImplCallExpr(getterImpl, selfArg, keyRefExpr);
|
|
|
|
// 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);
|
|
// For a subscript this decl will have two parameters, for a pointee property
|
|
// it will only have one.
|
|
DeclRefExpr *keyParamRefExpr = setterDecl->getParameters()->size() == 1
|
|
? nullptr
|
|
: createParamRefExpr(setterDecl, 1);
|
|
|
|
Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType();
|
|
|
|
auto *setterImplCallExpr =
|
|
createAccessorImplCallExpr(setterImpl, selfArg, keyParamRefExpr);
|
|
|
|
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, nullptr);
|
|
|
|
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;
|
|
|
|
// 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;
|
|
|
|
assert(getterImpl->getParameters()->size() == 1 &&
|
|
"subscript can only have 1 parameter");
|
|
auto bodyParam = ParamDecl::clone(ctx, getterImpl->getParameters()->get(0));
|
|
// If the subscript parameter is unnamed, give it a name to make sure SILGen
|
|
// creates a variable for it.
|
|
if (bodyParam->getName().empty())
|
|
bodyParam->setName(ctx.getIdentifier("__index"));
|
|
|
|
auto bodyParams = ParameterList::create(ctx, bodyParam);
|
|
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);
|
|
|
|
auto setterParamList =
|
|
ParameterList::create(ctx, {paramVarDecl, bodyParams->get(0)});
|
|
|
|
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();
|
|
|
|
// 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;
|
|
bool useAddress =
|
|
rawElementTy->getAnyPointerElementType() && isNoncopyable;
|
|
|
|
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));
|
|
}
|
|
|
|
llvm::SmallVector<clang::ParmVarDecl *, 4> params;
|
|
for (size_t i = 0; i < method->getNumParams(); ++i) {
|
|
const auto ¶m = *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();
|
|
if (type->isReferenceType())
|
|
type = type->getPointeeType();
|
|
args.push_back(new (clangCtx) clang::DeclRefExpr(
|
|
clangCtx, param, false, type, clang::ExprValueKind::VK_LValue,
|
|
clang::SourceLocation()));
|
|
}
|
|
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->getAttrs().add(new (ctx) PrefixAttr(SourceLoc()));
|
|
}
|
|
|
|
return topLevelStaticFuncDecl;
|
|
}
|
|
|
|
// MARK: C++ virtual methods
|
|
|
|
FuncDecl *SwiftDeclSynthesizer::makeVirtualMethod(
|
|
const clang::CXXMethodDecl *clangMethodDecl) {
|
|
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);
|
|
|
|
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");
|
|
if (!param->getIdentifier())
|
|
// Work around an assertion failure in CXXNameMangler::mangleUnqualifiedName
|
|
// when mangling std::__fs::filesystem::path::format.
|
|
return nullptr;
|
|
|
|
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->getAttrs().add(new (ctx)
|
|
AlwaysEmitIntoClientAttr(/*IsImplicit=*/true));
|
|
|
|
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->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;
|
|
bool passAlignment = false;
|
|
clang::Sema::SFINAETrap trap(clangSema);
|
|
bool findingAllocFuncFailed = clangSema.FindAllocationFunctions(
|
|
cxxRecordDeclLoc, clang::SourceRange(), clang::Sema::AFS_Both,
|
|
clang::Sema::AFS_Both, cxxRecordTy, /*IsArray=*/false, passAlignment,
|
|
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;
|
|
}
|