Skip to content

Commit 39dfe07

Browse files
committed
IRGen: Treat class layout for classes from other modules a bit more opaquely.
We would miscompile in mixed-language-version projects when a Swift class was compiled for one language version, while using Objective-C-imported types that are only available to that version, and then imported into a Swift module with a different language version that wasn't able to see all of the properties because of incompatible imported types. This manifested in a number of ways: - We assumed we could re-derive the constant field offsets of the class's ivars from the layout, which is wrong if properties are missing, causing accesses to final properties or subclass properties to go to the wrong offsets. - We assumed we could re-derive the instance size and alignment of a class instance in total, causing code to allocate the wrong amount of memory. - We neglected to account for the space that stored properties take up in the field offset vector of the class object, causing us to load vtable entries for following subclass methods from the wrong offsets. Eventually, resilience should reduce our exposure to these kinds of problems. As an incremental step in the right direction, when we look at a class from another module in IRGen, treat it as always variably-sized, so we don't try to hardcode offsets, size, or alignment of its instances. When we import a class, and we're unable to import a stored property, leave behind a new kind of MissingMemberDecl that records the number of field offset vector slots it will take up, so that we lay out subclass objects and compute vtable offsets correctly. Fixes rdar://problem/35330067. A side effect of this is that the RemoteAST library is no longer able to provide fixed field offsets for class ivars. This doesn't appear to impact the lldb test suite, and they will ultimately need to use more abstract access patterns to get ivar offsets from resilient classes (if they aren't already), so I just removed the RemoteAST test cases that tested for class field offsets for now.
1 parent 1d6c7c5 commit 39dfe07

19 files changed

+372
-80
lines changed

include/swift/AST/Decl.h

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,8 +617,9 @@ class alignas(1 << DeclAlignInBits) Decl {
617617
unsigned : NumDeclBits;
618618

619619
unsigned NumberOfVTableEntries : 2;
620+
unsigned NumberOfFieldOffsetVectorEntries : 1;
620621
};
621-
enum { NumMissingMemberDeclBits = NumDeclBits + 2 };
622+
enum { NumMissingMemberDeclBits = NumDeclBits + 3 };
622623
static_assert(NumMissingMemberDeclBits <= 32, "fits in an unsigned");
623624

624625
protected:
@@ -2987,6 +2988,26 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
29872988
ToStoredProperty(skipInaccessible));
29882989
}
29892990

2991+
private:
2992+
/// Predicate used to filter StoredPropertyRange.
2993+
struct ToStoredPropertyOrMissingMemberPlaceholder {
2994+
Optional<Decl *> operator()(Decl *decl) const;
2995+
};
2996+
2997+
public:
2998+
/// A range for iterating the stored member variables of a structure.
2999+
using StoredPropertyOrMissingMemberPlaceholderRange
3000+
= OptionalTransformRange<DeclRange,
3001+
ToStoredPropertyOrMissingMemberPlaceholder>;
3002+
3003+
/// Return a collection of the stored member variables of this type, along
3004+
/// with placeholders for unimportable stored properties.
3005+
StoredPropertyOrMissingMemberPlaceholderRange
3006+
getStoredPropertiesAndMissingMemberPlaceholders() const {
3007+
return StoredPropertyOrMissingMemberPlaceholderRange(getMembers(),
3008+
ToStoredPropertyOrMissingMemberPlaceholder());
3009+
}
3010+
29903011
// Implement isa/cast/dyncast/etc.
29913012
static bool classof(const Decl *D) {
29923013
return D->getKind() >= DeclKind::First_NominalTypeDecl &&
@@ -6305,26 +6326,37 @@ class PostfixOperatorDecl : public OperatorDecl {
63056326
class MissingMemberDecl : public Decl {
63066327
DeclName Name;
63076328

6308-
MissingMemberDecl(DeclContext *DC, DeclName name, unsigned vtableEntries)
6329+
MissingMemberDecl(DeclContext *DC, DeclName name,
6330+
unsigned vtableEntries,
6331+
unsigned fieldOffsetVectorEntries)
63096332
: Decl(DeclKind::MissingMember, DC), Name(name) {
63106333
MissingMemberDeclBits.NumberOfVTableEntries = vtableEntries;
63116334
assert(getNumberOfVTableEntries() == vtableEntries && "not enough bits");
6335+
MissingMemberDeclBits.NumberOfFieldOffsetVectorEntries =
6336+
fieldOffsetVectorEntries;
6337+
assert(getNumberOfFieldOffsetVectorEntries() == fieldOffsetVectorEntries
6338+
&& "not enough bits");
63126339
setImplicit();
63136340
}
63146341
public:
63156342
static MissingMemberDecl *
63166343
forMethod(ASTContext &ctx, DeclContext *DC, DeclName name,
63176344
bool hasNormalVTableEntry) {
63186345
assert(!name || name.isCompoundName());
6319-
return new (ctx) MissingMemberDecl(DC, name, hasNormalVTableEntry);
6346+
return new (ctx) MissingMemberDecl(DC, name, hasNormalVTableEntry, 0);
63206347
}
63216348

63226349
static MissingMemberDecl *
63236350
forInitializer(ASTContext &ctx, DeclContext *DC, DeclName name,
63246351
bool hasNormalVTableEntry,
63256352
bool hasAllocatingVTableEntry) {
63266353
unsigned entries = hasNormalVTableEntry + hasAllocatingVTableEntry;
6327-
return new (ctx) MissingMemberDecl(DC, name, entries);
6354+
return new (ctx) MissingMemberDecl(DC, name, entries, 0);
6355+
}
6356+
6357+
static MissingMemberDecl *
6358+
forStoredProperty(ASTContext &ctx, DeclContext *DC, DeclName name) {
6359+
return new (ctx) MissingMemberDecl(DC, name, 0, 1);
63286360
}
63296361

63306362
DeclName getFullName() const {
@@ -6335,6 +6367,10 @@ class MissingMemberDecl : public Decl {
63356367
return MissingMemberDeclBits.NumberOfVTableEntries;
63366368
}
63376369

6370+
unsigned getNumberOfFieldOffsetVectorEntries() const {
6371+
return MissingMemberDeclBits.NumberOfFieldOffsetVectorEntries;
6372+
}
6373+
63386374
SourceLoc getLoc() const {
63396375
return SourceLoc();
63406376
}
@@ -6369,6 +6405,21 @@ NominalTypeDecl::ToStoredProperty::operator()(Decl *decl) const {
63696405
return None;
63706406
}
63716407

6408+
inline Optional<Decl *>
6409+
NominalTypeDecl::ToStoredPropertyOrMissingMemberPlaceholder
6410+
::operator()(Decl *decl) const {
6411+
if (auto var = dyn_cast<VarDecl>(decl)) {
6412+
if (!var->isStatic() && var->hasStorage())
6413+
return var;
6414+
}
6415+
if (auto missing = dyn_cast<MissingMemberDecl>(decl)) {
6416+
if (missing->getNumberOfFieldOffsetVectorEntries() > 0)
6417+
return missing;
6418+
}
6419+
6420+
return None;
6421+
}
6422+
63726423
inline void
63736424
AbstractStorageDecl::overwriteSetterAccess(AccessLevel accessLevel) {
63746425
GetSetInfo.setInt(accessLevel);

lib/IRGen/ClassMetadataVisitor.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,23 @@ template <class Impl> class ClassMetadataVisitor
127127
// But we currently always give classes field-offset vectors,
128128
// whether they need them or not.
129129
asImpl().noteStartOfFieldOffsets(theClass);
130-
for (auto field : theClass->getStoredProperties()) {
130+
for (auto field :
131+
theClass->getStoredPropertiesAndMissingMemberPlaceholders()) {
131132
addFieldEntries(field);
132133
}
133134
asImpl().noteEndOfFieldOffsets(theClass);
134135
}
135136

136137
private:
137-
void addFieldEntries(VarDecl *field) {
138-
asImpl().addFieldOffset(field);
138+
void addFieldEntries(Decl *field) {
139+
if (auto var = dyn_cast<VarDecl>(field)) {
140+
asImpl().addFieldOffset(var);
141+
return;
142+
}
143+
if (auto placeholder = dyn_cast<MissingMemberDecl>(field)) {
144+
asImpl().addFieldOffsetPlaceholders(placeholder);
145+
return;
146+
}
139147
}
140148
};
141149

@@ -171,6 +179,12 @@ class ClassMetadataScanner : public ClassMetadataVisitor<Impl> {
171179
}
172180
void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {}
173181
void addFieldOffset(VarDecl *var) { addPointer(); }
182+
void addFieldOffsetPlaceholders(MissingMemberDecl *mmd) {
183+
for (unsigned i = 0, e = mmd->getNumberOfFieldOffsetVectorEntries();
184+
i < e; ++i) {
185+
addPointer();
186+
}
187+
}
174188
void addGenericArgument(CanType argument, ClassDecl *forClass) {
175189
addPointer();
176190
}

lib/IRGen/GenClass.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,19 +194,19 @@ namespace {
194194
{
195195
// Start by adding a heap header.
196196
switch (refcounting) {
197-
case swift::irgen::ReferenceCounting::Native:
197+
case ReferenceCounting::Native:
198198
// For native classes, place a full object header.
199199
addHeapHeader();
200200
break;
201-
case swift::irgen::ReferenceCounting::ObjC:
201+
case ReferenceCounting::ObjC:
202202
// For ObjC-inheriting classes, we don't reliably know the size of the
203203
// base class, but NSObject only has an `isa` pointer at most.
204204
addNSObjectHeader();
205205
break;
206-
case swift::irgen::ReferenceCounting::Block:
207-
case swift::irgen::ReferenceCounting::Unknown:
208-
case swift::irgen::ReferenceCounting::Bridge:
209-
case swift::irgen::ReferenceCounting::Error:
206+
case ReferenceCounting::Block:
207+
case ReferenceCounting::Unknown:
208+
case ReferenceCounting::Bridge:
209+
case ReferenceCounting::Error:
210210
llvm_unreachable("not a class refcounting kind");
211211
}
212212

@@ -317,6 +317,12 @@ namespace {
317317
NumInherited = Elements.size();
318318
}
319319
}
320+
321+
// If this class was imported from another module, assume that we may
322+
// not know its exact layout.
323+
if (theClass->getModuleContext() != IGM.getSwiftModule()) {
324+
ClassHasFixedSize = false;
325+
}
320326

321327
// Access strategies should be set by the abstract class layout,
322328
// not using the concrete type information we have.

lib/IRGen/GenMeta.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,6 +3355,16 @@ namespace {
33553355
}
33563356
}
33573357
}
3358+
3359+
void addFieldOffsetPlaceholders(MissingMemberDecl *placeholder) {
3360+
for (unsigned i = 0,
3361+
e = placeholder->getNumberOfFieldOffsetVectorEntries();
3362+
i < e; ++i) {
3363+
// Emit placeholder values for some number of stored properties we
3364+
// know exist but aren't able to reference directly.
3365+
B.addInt(IGM.SizeTy, 0);
3366+
}
3367+
}
33583368

33593369
void addMethod(SILDeclRef fn) {
33603370
// Find the vtable entry.
@@ -3386,8 +3396,9 @@ namespace {
33863396
B.addBitCast(IGM.getDeletedMethodErrorFn(), IGM.FunctionPtrTy);
33873397
}
33883398

3389-
void addPlaceholder(MissingMemberDecl *) {
3390-
llvm_unreachable("cannot generate metadata with placeholders in it");
3399+
void addPlaceholder(MissingMemberDecl *m) {
3400+
assert(m->getNumberOfVTableEntries() == 0
3401+
&& "cannot generate metadata with placeholders in it");
33913402
}
33923403

33933404
void addMethodOverride(SILDeclRef baseRef, SILDeclRef declRef) {}
@@ -4044,7 +4055,7 @@ irgen::emitClassFragileInstanceSizeAndAlignMask(IRGenFunction &IGF,
40444055
// FIXME: The below checks should capture this property already, but
40454056
// resilient class metadata layout is not fully implemented yet.
40464057
auto expansion = IGF.IGM.getResilienceExpansionForLayout(theClass);
4047-
if (IGF.IGM.isResilient(theClass, expansion)) {
4058+
if (theClass->getParentModule() != IGF.IGM.getSwiftModule()) {
40484059
return emitClassResilientInstanceSizeAndAlignMask(IGF, theClass, metadata);
40494060
}
40504061

lib/SILGen/SILGenType.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,9 @@ class SILGenVTable : public SILVTableVisitor<SILGenVTable> {
274274
}
275275
}
276276

277-
void addPlaceholder(MissingMemberDecl *) {
278-
llvm_unreachable("Should not be emitting class with missing members");
277+
void addPlaceholder(MissingMemberDecl *m) {
278+
assert(m->getNumberOfVTableEntries() == 0
279+
&& "Should not be emitting class with missing members");
279280
}
280281
};
281282

lib/Serialization/Deserialization.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2872,8 +2872,30 @@ ModuleFile::getDeclCheckedImpl(DeclID DID, Optional<DeclContext *> ForcedContext
28722872
for (TypeID dependencyID : dependencyIDs) {
28732873
auto dependency = getTypeChecked(dependencyID);
28742874
if (!dependency) {
2875+
// Stored properties in classes still impact class object layout because
2876+
// their offset is computed and stored in the field offset vector.
2877+
DeclDeserializationError::Flags flags;
2878+
2879+
if (!isStatic) {
2880+
switch ((StorageKind)storageKind) {
2881+
case StorageKind::Stored:
2882+
case StorageKind::StoredWithObservers:
2883+
case StorageKind::StoredWithTrivialAccessors:
2884+
flags |= DeclDeserializationError::Flag::NeedsFieldOffsetVectorEntry;
2885+
break;
2886+
2887+
case StorageKind::Addressed:
2888+
case StorageKind::AddressedWithObservers:
2889+
case StorageKind::AddressedWithTrivialAccessors:
2890+
case StorageKind::Computed:
2891+
case StorageKind::ComputedWithMutableAddress:
2892+
case StorageKind::InheritedWithObservers:
2893+
break;
2894+
}
2895+
}
2896+
28752897
return llvm::make_error<TypeError>(
2876-
name, takeErrorInfo(dependency.takeError()));
2898+
name, takeErrorInfo(dependency.takeError()), flags);
28772899
}
28782900
}
28792901

@@ -4690,6 +4712,9 @@ Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context,
46904712
} else if (error.needsVTableEntry()) {
46914713
suppliedMissingMember = MissingMemberDecl::forMethod(
46924714
context, containingClass, error.getName(), error.needsVTableEntry());
4715+
} else if (error.needsFieldOffsetVectorEntry()) {
4716+
suppliedMissingMember = MissingMemberDecl::forStoredProperty(
4717+
context, containingClass, error.getName());
46934718
}
46944719
// FIXME: Handle other kinds of missing members: properties,
46954720
// subscripts, and methods that don't need vtable entries.

lib/Serialization/DeserializationErrors.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ class DeclDeserializationError : public llvm::ErrorInfoBase {
216216
DesignatedInitializer = 1 << 0,
217217
NeedsVTableEntry = 1 << 1,
218218
NeedsAllocatingVTableEntry = 1 << 2,
219+
NeedsFieldOffsetVectorEntry = 1 << 3,
219220
};
220221
using Flags = OptionSet<Flag>;
221222

@@ -237,6 +238,9 @@ class DeclDeserializationError : public llvm::ErrorInfoBase {
237238
bool needsAllocatingVTableEntry() const {
238239
return flags.contains(Flag::NeedsAllocatingVTableEntry);
239240
}
241+
bool needsFieldOffsetVectorEntry() const {
242+
return flags.contains(Flag::NeedsFieldOffsetVectorEntry);
243+
}
240244

241245
bool isA(const void *const ClassID) const override {
242246
return ClassID == classID() || ErrorInfoBase::isA(ClassID);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Name: ObjCStuff
2+
SwiftVersions:
3+
- Version: 4
4+
Typedefs:
5+
- Name: OJCCloudButt
6+
SwiftName: OJCCloud.Butt
7+
- Version: 3
8+
Typedefs:
9+
- Name: OJCCloudButt
10+
SwiftWrapper: none
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import Foundation;
2+
3+
@interface OJCCloud: NSObject
4+
@end
5+
6+
typedef int OJCCloudButt __attribute__((swift_wrapper(struct)));
7+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Foundation
2+
import ObjCStuff
3+
4+
open class ButtHolder {
5+
public final var x: Int
6+
public final var y: [OJCCloud.Butt: String]
7+
@NSManaged public var magic: [OJCCloud.Butt: String]
8+
public final var z: String
9+
10+
open func virtual() {}
11+
12+
public init() { fatalError() }
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module ObjCStuff {
2+
header "ObjCStuff.h"
3+
export *
4+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: %target-swift-frontend -emit-module -o %t/UsingObjCStuff.swiftmodule -module-name UsingObjCStuff -I %t -I %S/Inputs/mixed_mode -swift-version 4 %S/Inputs/mixed_mode/UsingObjCStuff.swift
4+
// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 3 %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
5+
// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 4 %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
6+
7+
// REQUIRES: objc_interop
8+
9+
import Swift
10+
import UsingObjCStuff
11+
12+
class SubButtHolder: ButtHolder {
13+
var w: Double = 0
14+
}
15+
16+
sil_vtable SubButtHolder {}
17+
18+
// CHECK-LABEL: define {{.*}} @getHolder
19+
sil @getHolder: $@convention(thin) () -> @owned ButtHolder {
20+
entry:
21+
// We should load the dimensions of the class instance from metadata, not try
22+
// to hardcode constants.
23+
// CHECK: [[METADATA:%.*]] = call %swift.type* @_T014UsingObjCStuff10ButtHolderCMa()
24+
// CHECK-64: [[SIZE32:%.*]] = load i32
25+
// CHECK-64: [[SIZE:%.*]] = zext i32 [[SIZE32]] to
26+
// CHECK-32: [[SIZE:%.*]] = load i32
27+
// CHECK: [[ALIGN16:%.*]] = load i16
28+
// CHECK: [[ALIGN:%.*]] = zext i16 [[ALIGN16]] to [[WORD:i32|i64]]
29+
// CHECK: call noalias %swift.refcounted* @swift_rt_swift_allocObject(%swift.type* [[METADATA]], [[WORD]] [[SIZE]], [[WORD]] [[ALIGN]])
30+
%x = alloc_ref $ButtHolder
31+
return %x : $ButtHolder
32+
}

0 commit comments

Comments
 (0)