Skip to content

Serialize SIL for default argument generators in Swift 4 mode #11937

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ class alignas(1 << DeclAlignInBits) Decl {
unsigned BodyKind : 3;

/// Number of curried parameter lists.
unsigned NumParameterLists : 6;
unsigned NumParameterLists : 5;

/// Whether we are overridden later.
unsigned Overridden : 1;
Expand All @@ -380,6 +380,9 @@ class alignas(1 << DeclAlignInBits) Decl {

/// Whether NeedsNewVTableEntry is valid.
unsigned HasComputedNeedsNewVTableEntry : 1;

/// The ResilienceExpansion to use for default arguments.
unsigned DefaultArgumentResilienceExpansion : 1;
};
enum { NumAbstractFunctionDeclBits = NumValueDeclBits + 13 };
static_assert(NumAbstractFunctionDeclBits <= 32, "fits in an unsigned");
Expand Down Expand Up @@ -4859,6 +4862,8 @@ class AbstractFunctionDecl : public ValueDecl, public GenericContext {
AbstractFunctionDeclBits.Throws = Throws;
AbstractFunctionDeclBits.NeedsNewVTableEntry = false;
AbstractFunctionDeclBits.HasComputedNeedsNewVTableEntry = false;
AbstractFunctionDeclBits.DefaultArgumentResilienceExpansion =
unsigned(ResilienceExpansion::Maximal);

// Verify no bitfield truncation.
assert(AbstractFunctionDeclBits.NumParameterLists == NumParameterLists);
Expand Down Expand Up @@ -5076,6 +5081,21 @@ class AbstractFunctionDecl : public ValueDecl, public GenericContext {
/// Resolved during type checking
void setIsOverridden() { AbstractFunctionDeclBits.Overridden = true; }

/// The ResilienceExpansion for default arguments.
///
/// In Swift 4 mode, default argument expressions are serialized, and must
/// obey the restrictions imposed upon inlineable function bodies.
ResilienceExpansion getDefaultArgumentResilienceExpansion() const {
return ResilienceExpansion(
AbstractFunctionDeclBits.DefaultArgumentResilienceExpansion);
}

/// Set the ResilienceExpansion for default arguments.
void setDefaultArgumentResilienceExpansion(ResilienceExpansion expansion) {
AbstractFunctionDeclBits.DefaultArgumentResilienceExpansion =
unsigned(expansion);
}

/// Set information about the foreign error convention used by this
/// declaration.
void setForeignErrorConvention(const ForeignErrorConvention &convention);
Expand Down
14 changes: 2 additions & 12 deletions include/swift/AST/Initializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,26 +154,16 @@ class DefaultArgumentInitializer : public Initializer {
public:
explicit DefaultArgumentInitializer(DeclContext *parent, unsigned index)
: Initializer(InitializerKind::DefaultArgument, parent) {
SpareBits = (unsigned(ResilienceExpansion::Maximal) | index << 1);
SpareBits = index;
}

unsigned getIndex() const { return SpareBits >> 1; }

ResilienceExpansion getResilienceExpansion() const {
return ResilienceExpansion(SpareBits & 1);
}
unsigned getIndex() const { return SpareBits; }

/// Change the parent of this context. This is necessary because
/// the function signature is parsed before the function
/// declaration/expression itself is built.
void changeFunction(AbstractFunctionDecl *parent);

/// Change the resilience expansion of this context, necessary
/// for the same reason as above.
void changeResilienceExpansion(ResilienceExpansion expansion) {
SpareBits = (SpareBits & ~1) | unsigned(expansion);
}

static bool classof(const DeclContext *DC) {
if (auto init = dyn_cast<Initializer>(DC))
return classof(init);
Expand Down
11 changes: 10 additions & 1 deletion include/swift/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const uint16_t VERSION_MAJOR = 0;
/// in source control, you should also update the comment to briefly
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
const uint16_t VERSION_MINOR = 365; // KeyPathInst operands
const uint16_t VERSION_MINOR = 366; // Last change: default argument resilience expansion

using DeclID = PointerEmbeddedInt<unsigned, 31>;
using DeclIDField = BCFixed<31>;
Expand Down Expand Up @@ -388,6 +388,13 @@ enum class EnumElementRawValueKind : uint8_t {
/// TODO: Float, string, char, etc.
};

// These IDs must \em not be renumbered or reordered without incrementing
// VERSION_MAJOR.
enum class ResilienceExpansion : uint8_t {
Minimal = 0,
Maximal,
};

using EnumElementRawValueKindField = BCFixed<4>;

/// The various types of blocks that can occur within a serialized Swift
Expand Down Expand Up @@ -893,6 +900,7 @@ namespace decls_block {
DeclIDField, // overridden decl
AccessLevelField, // access level
BCFixed<1>, // requires a new vtable slot
BCFixed<1>, // default argument resilience expansion
BCFixed<1>, // 'required' but overridden is not (used for recovery)
BCVBR<5>, // number of parameter name components
BCArray<IdentifierIDField> // name components,
Expand Down Expand Up @@ -957,6 +965,7 @@ namespace decls_block {
AddressorKindField, // addressor kind
AccessLevelField, // access level
BCFixed<1>, // requires a new vtable slot
BCFixed<1>, // default argument resilience expansion
BCArray<IdentifierIDField> // name components,
// followed by TypeID dependencies
// The record is trailed by:
Expand Down
6 changes: 4 additions & 2 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,10 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
for (const auto *dc = this; dc->isLocalContext(); dc = dc->getParent()) {
// Default argument initializer contexts have their resilience expansion
// set when they're type checked.
if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(dc))
return DAI->getResilienceExpansion();
if (auto *DAI = dyn_cast<DefaultArgumentInitializer>(dc)) {
return cast<AbstractFunctionDecl>(dc->getParent())
->getDefaultArgumentResilienceExpansion();
}

if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc)) {
// If the function is a nested function, we will serialize its body if
Expand Down
15 changes: 15 additions & 0 deletions lib/SIL/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "swift/AST/AnyFunctionRef.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/SIL/SILLinkage.h"
Expand Down Expand Up @@ -538,6 +540,19 @@ IsSerialized_t SILDeclRef::isSerialized() const {
dc = closure->getLocalContext();
else {
auto *d = getDecl();

// Default argument generators are serialized if the function was
// type-checked in Swift 4 mode.
if (kind == SILDeclRef::Kind::DefaultArgGenerator) {
auto *afd = cast<AbstractFunctionDecl>(d);
switch (afd->getDefaultArgumentResilienceExpansion()) {
case ResilienceExpansion::Minimal:
return IsSerialized;
case ResilienceExpansion::Maximal:
return IsNotSerialized;
}
}

dc = getDecl()->getInnermostDeclContext();

// Enum element constructors are serialized if the enum is
Expand Down
23 changes: 10 additions & 13 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,11 +649,10 @@ emitMarkFunctionEscapeForTopLevelCodeGlobals(SILLocation loc,

void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
// Emit any default argument generators.
{
auto paramLists = AFD->getParameterLists();
if (AFD->getDeclContext()->isTypeContext())
paramLists = paramLists.slice(1);
emitDefaultArgGenerators(AFD, paramLists);
if (!isa<DestructorDecl>(AFD)) {
unsigned paramListIndex = AFD->getDeclContext()->isTypeContext() ? 1 : 0;
auto *paramList = AFD->getParameterLists()[paramListIndex];
emitDefaultArgGenerators(AFD, paramList);
}

// If this is a function at global scope, it may close over a global variable.
Expand Down Expand Up @@ -1015,15 +1014,13 @@ void SILGenModule::emitGlobalGetter(VarDecl *global,
}

void SILGenModule::emitDefaultArgGenerators(SILDeclRef::Loc decl,
ArrayRef<ParameterList*> paramLists) {
ParameterList *paramList) {
unsigned index = 0;
for (auto paramList : paramLists) {
for (auto param : *paramList) {
if (auto defaultArg = param->getDefaultValue())
emitDefaultArgGenerator(SILDeclRef::getDefaultArgGenerator(decl, index),
defaultArg, param->getDefaultArgumentKind());
++index;
}
for (auto param : *paramList) {
if (auto defaultArg = param->getDefaultValue())
emitDefaultArgGenerator(SILDeclRef::getDefaultArgGenerator(decl, index),
defaultArg, param->getDefaultArgumentKind());
++index;
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/SILGen/SILGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
/// Emits the stored property initializer for the given pattern.
void emitStoredPropertyInitialization(PatternBindingDecl *pd, unsigned i);

/// Emits the default argument generator for the given function.
/// Emits default argument generators for the given parameter list.
void emitDefaultArgGenerators(SILDeclRef::Loc decl,
ArrayRef<ParameterList*> paramLists);
ParameterList *paramList);

/// Emits the curry thunk between two uncurry levels of a function.
void emitCurryThunk(SILDeclRef thunk);
Expand Down
39 changes: 20 additions & 19 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1279,14 +1279,6 @@ static void checkDefaultArguments(TypeChecker &tc,
ParameterList *params,
unsigned &nextArgIndex,
AbstractFunctionDecl *func) {
// In Swift 4 mode, default argument bodies are inlined into the
// caller.
auto expansion = func->getResilienceExpansion();
if (!tc.Context.isSwiftVersion3() &&
func->getFormalAccessScope(/*useDC=*/nullptr,
/*respectVersionedAttr=*/true).isPublic())
expansion = ResilienceExpansion::Minimal;

for (auto &param : *params) {
++nextArgIndex;
if (!param->getDefaultValue() || !param->hasType() ||
Expand All @@ -1296,9 +1288,6 @@ static void checkDefaultArguments(TypeChecker &tc,
Expr *e = param->getDefaultValue();
auto initContext = param->getDefaultArgumentInitContext();

cast<DefaultArgumentInitializer>(initContext)
->changeResilienceExpansion(expansion);

// Type-check the initializer, then flag that we did so.
auto resultTy = tc.typeCheckExpression(
e, initContext, TypeLoc::withoutLoc(param->getType()),
Expand All @@ -1317,6 +1306,24 @@ static void checkDefaultArguments(TypeChecker &tc,
}
}

/// Check the default arguments that occur within this pattern.
static void checkDefaultArguments(TypeChecker &tc,
AbstractFunctionDecl *func) {
// In Swift 4 mode, default argument bodies are inlined into the
// caller.
auto expansion = func->getResilienceExpansion();
if (!tc.Context.isSwiftVersion3() &&
func->getFormalAccessScope(/*useDC=*/nullptr,
/*respectVersionedAttr=*/true).isPublic())
expansion = ResilienceExpansion::Minimal;

func->setDefaultArgumentResilienceExpansion(expansion);

unsigned nextArgIndex = 0;
for (auto paramList : func->getParameterLists())
checkDefaultArguments(tc, paramList, nextArgIndex, func);
}

bool TypeChecker::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD,
SourceLoc EndTypeCheckLoc) {
validateDecl(AFD);
Expand Down Expand Up @@ -1358,10 +1365,7 @@ bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
// named function or an anonymous func expression.
bool TypeChecker::typeCheckFunctionBodyUntil(FuncDecl *FD,
SourceLoc EndTypeCheckLoc) {
// Check the default argument definitions.
unsigned nextArgIndex = 0;
for (auto paramList : FD->getParameterLists())
checkDefaultArguments(*this, paramList, nextArgIndex, FD);
checkDefaultArguments(*this, FD);

// Clang imported inline functions do not have a Swift body to
// typecheck.
Expand Down Expand Up @@ -1464,10 +1468,7 @@ static bool isKnownEndOfConstructor(ASTNode N) {

bool TypeChecker::typeCheckConstructorBodyUntil(ConstructorDecl *ctor,
SourceLoc EndTypeCheckLoc) {
// Check the default argument definitions.
unsigned nextArgIndex = 0;
for (auto paramList : ctor->getParameterLists())
checkDefaultArguments(*this, paramList, nextArgIndex, ctor);
checkDefaultArguments(*this, ctor);

BraceStmt *body = ctor->getBody();
if (!body)
Expand Down
35 changes: 35 additions & 0 deletions lib/Serialization/Deserialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,17 @@ getActualSelfAccessKind(uint8_t raw) {
return None;
}

static
Optional<swift::ResilienceExpansion> getActualResilienceExpansion(uint8_t raw) {
switch (serialization::ResilienceExpansion(raw)) {
case serialization::ResilienceExpansion::Minimal:
return swift::ResilienceExpansion::Minimal;
case serialization::ResilienceExpansion::Maximal:
return swift::ResilienceExpansion::Maximal;
}
return None;
}

void ModuleFile::configureStorage(AbstractStorageDecl *decl,
unsigned rawStorageKind,
serialization::DeclID getter,
Expand Down Expand Up @@ -2570,6 +2581,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
TypeID interfaceID;
DeclID overriddenID;
bool needsNewVTableEntry, firstTimeRequired;
uint8_t rawDefaultArgumentResilienceExpansion;
unsigned numArgNames;
ArrayRef<uint64_t> argNameAndDependencyIDs;

Expand All @@ -2581,6 +2593,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
overriddenID,
rawAccessLevel,
needsNewVTableEntry,
rawDefaultArgumentResilienceExpansion,
firstTimeRequired,
numArgNames,
argNameAndDependencyIDs);
Expand Down Expand Up @@ -2687,6 +2700,16 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
if (auto overriddenCtor = cast_or_null<ConstructorDecl>(overridden.get()))
ctor->setOverriddenDecl(overriddenCtor);
ctor->setNeedsNewVTableEntry(needsNewVTableEntry);

if (auto defaultArgumentResilienceExpansion = getActualResilienceExpansion(
rawDefaultArgumentResilienceExpansion)) {
ctor->setDefaultArgumentResilienceExpansion(
*defaultArgumentResilienceExpansion);
} else {
error();
return nullptr;
}

break;
}

Expand Down Expand Up @@ -2825,6 +2848,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
DeclID overriddenID;
DeclID accessorStorageDeclID;
bool needsNewVTableEntry;
uint8_t rawDefaultArgumentResilienceExpansion;
ArrayRef<uint64_t> nameAndDependencyIDs;

decls_block::FuncLayout::readRecord(scratch, contextID, isImplicit,
Expand All @@ -2837,6 +2861,7 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
numNameComponentsBiased,
rawAddressorKind, rawAccessLevel,
needsNewVTableEntry,
rawDefaultArgumentResilienceExpansion,
nameAndDependencyIDs);

// Resolve the name ids.
Expand Down Expand Up @@ -2974,6 +2999,16 @@ ModuleFile::getDeclChecked(DeclID DID, Optional<DeclContext *> ForcedContext) {
fn->setImplicit();
fn->setDynamicSelf(hasDynamicSelf);
fn->setNeedsNewVTableEntry(needsNewVTableEntry);

if (auto defaultArgumentResilienceExpansion = getActualResilienceExpansion(
rawDefaultArgumentResilienceExpansion)) {
fn->setDefaultArgumentResilienceExpansion(
*defaultArgumentResilienceExpansion);
} else {
error();
return nullptr;
}

break;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ static SILDeclRef getSILDeclRef(ModuleFile *MF,
"Expect 5 numbers for SILDeclRef");
SILDeclRef DRef(cast<ValueDecl>(MF->getDecl(ListOfValues[NextIdx])),
(SILDeclRef::Kind)ListOfValues[NextIdx+1],
(ResilienceExpansion)ListOfValues[NextIdx+2],
(swift::ResilienceExpansion)ListOfValues[NextIdx+2],
/*isCurried=*/false, ListOfValues[NextIdx+4] > 0);
if (ListOfValues[NextIdx+3] < DRef.getUncurryLevel())
DRef = DRef.asCurried();
Expand Down
Loading