Skip to content
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
6 changes: 4 additions & 2 deletions SwiftCompilerSources/Sources/AST/Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,12 @@ final public class ParamDecl: VarDecl {
final public class SubscriptDecl: AbstractStorageDecl, GenericContext {}

public class AbstractFunctionDecl: ValueDecl, GenericContext {
public var isOverridden: Bool { bridged.AbstractFunction_isOverridden() }
final public var isOverridden: Bool { bridged.AbstractFunction_isOverridden() }
}

final public class ConstructorDecl: AbstractFunctionDecl {}
final public class ConstructorDecl: AbstractFunctionDecl {
public var isInheritable: Bool { bridged.Constructor_isInheritable() }
}

final public class DestructorDecl: AbstractFunctionDecl {
final public var isIsolated: Bool { bridged.Destructor_isIsolated() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private struct VTableSpecializer {
}

private func specializeEntries(of vTable: VTable, _ notifyNewFunction: (Function) -> ()) -> [VTable.Entry] {
return vTable.entries.map { entry in
return vTable.entries.compactMap { entry in
if !entry.implementation.isGeneric {
return entry
}
Expand All @@ -95,6 +95,14 @@ private struct VTableSpecializer {
let specializedMethod = context.specialize(function: entry.implementation, for: methodSubs,
convertIndirectToDirect: true, isMandatory: true)
else {
if let constructor = entry.methodDecl.decl as? ConstructorDecl,
!constructor.isInheritable
{
// For some reason, SILGen is putting constructors in the vtable, though they are never
// called through the vtable.
// Dropping those vtable entries allows using constructors with generic arguments.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of working around it here, is it hard to fix SILGen to not put them in the vtable? IRGen must have a similar workaround too then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of working around it here, is it hard to fix SILGen to not put them in the vtable?

I don't know. We (= someone who knows SILGen) can do this as a follow-up.

IRGen must have a similar workaround too then.

No, in regular SIL it's totally fine to have a generic function in a vtable. And in embedded the compiler aborted with an error in this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution seems brittle. Why not update NeedsNewVTableEntryRequest::evaluate to suppress generic initializers from the vtable in the first place? There's no way to specialize these appropriately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution seems brittle.

No. It does not change the vtable layout. It just marks the constructor as dead. Also, we are doing this is-generic check on the call site, too (i.e. for class_method instructions) which is the double check that we are not calling such a "dead" constructor.

Why not update NeedsNewVTableEntryRequest::evaluate to suppress generic initializers from the vtable in the first place?

I tried this but did run into assertion failures in SILGen later. It's probably a more involved thing to change the vtable layout.

return nil
}
return entry
}
notifyNewFunction(specializedMethod)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ struct BridgedDeclObj {
BRIDGED_INLINE bool ProtocolDecl_requiresClass() const;
BRIDGED_INLINE bool ProtocolDecl_isMarkerProtocol() const;
BRIDGED_INLINE bool AbstractFunction_isOverridden() const;
BRIDGED_INLINE bool Constructor_isInheritable() const;
BRIDGED_INLINE bool Destructor_isIsolated() const;
BRIDGED_INLINE bool EnumElementDecl_hasAssociatedValues() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedParameterList
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/ASTBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ bool BridgedDeclObj::AbstractFunction_isOverridden() const {
return getAs<swift::AbstractFunctionDecl>()->isOverridden();
}

bool BridgedDeclObj::Constructor_isInheritable() const {
return getAs<swift::ConstructorDecl>()->isInheritable();
}

bool BridgedDeclObj::Destructor_isIsolated() const {
auto dd = getAs<swift::DestructorDecl>();
auto ai = swift::getActorIsolation(dd);
Expand Down
17 changes: 8 additions & 9 deletions lib/SIL/IR/SILVTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,14 @@ void SILVTable::updateVTableCache(const Entry &entry) {
void SILVTable::replaceEntries(ArrayRef<Entry> newEntries) {
auto entries = getMutableEntries();
ASSERT(newEntries.size() <= entries.size());
for (unsigned i = 0; i < entries.size(); ++i) {
entries[i].getImplementation()->decrementRefCount();
if (i < newEntries.size()) {
entries[i] = newEntries[i];
entries[i].getImplementation()->incrementRefCount();
updateVTableCache(entries[i]);
} else {
removeFromVTableCache(entries[i]);
}
for (Entry &entry : getMutableEntries()) {
entry.getImplementation()->decrementRefCount();
removeFromVTableCache(entry);
}
for (unsigned i = 0; i < newEntries.size(); ++i) {
entries[i] = newEntries[i];
entries[i].getImplementation()->incrementRefCount();
updateVTableCache(entries[i]);
}
NumEntries = newEntries.size();
}
Expand Down
49 changes: 40 additions & 9 deletions test/embedded/classes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,57 @@
// REQUIRES: optimized_stdlib
// REQUIRES: swift_feature_Embedded

class MyClass {
public class MyClass {
init() { print("MyClass.init") }
deinit { print("MyClass.deinit") }
func foo() { print("MyClass.foo") }
}

class MySubClass: MyClass {
var x = 27
public class MySubClass: MyClass {
var x: Int

override init() {
self.x = 27
print("MySubClass.init")
}

public init(p: some P) {
self.x = p.get()
super.init()
print("MySubClass.init")
}

override init() { print("MySubClass.init") }
deinit { print("MySubClass.deinit") }
override func foo() { print("MySubClass.foo") }

override func foo() { print("MySubClass.foo: \(x)") }

func printX() {
print(x)
}
}

class MySubSubClass: MySubClass {
override init() { print("MySubSubClass.init") }
public protocol P {
func get() -> Int
}

struct S: P {
let i: Int

func get() -> Int { i }
}

public class MySubSubClass: MySubClass {
override init() {
print("MySubSubClass.init")
super.init()
}

deinit { print("MySubSubClass.deinit") }

override func foo() { print("MySubSubClass.foo") }
}

class OtherSubClass: MyClass {}
public class OtherSubClass: MyClass {}

func testCasting(_ title: StaticString, _ c: MyClass) {
print(title, terminator: "")
Expand Down Expand Up @@ -81,10 +107,15 @@ struct Main {
o.1!.foo()
o.2!.foo()
// CHECK: MyClass.foo
// CHECK: MySubClass.foo
// CHECK: MySubClass.foo: 27
// CHECK: MySubSubClass.foo
print("")

print("4b") // CHECK: 4b
o.1 = MySubClass(p: S(i: 42))
o.1!.foo()
// CHECK: MySubClass.foo: 42

print("5") // CHECK: 5
o.0 = nil
// CHECK: MyClass.deinit
Expand Down