Skip to content

Minor improvements to SIL verifier #21504

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 9 commits into from
Jan 2, 2019
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
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4099,6 +4099,9 @@ ERROR(inlinable_decl_not_public,
"but %0 is %select{private|fileprivate|internal|%error|%error}1",
(DeclBaseName, AccessLevel))

ERROR(inlinable_resilient_deinit,
none, "deinitializer can only be '@inlinable' if the class is '@_fixed_layout'", ())

//------------------------------------------------------------------------------
// MARK: @_specialize diagnostics
//------------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions lib/SIL/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,11 @@ bool SILDeclRef::isTransparent() const {

/// True if the function should have its body serialized.
IsSerialized_t SILDeclRef::isSerialized() const {
// Native-to-foreign thunks are only referenced from the Objective-C
// method table.
if (isForeign)
return IsNotSerialized;

DeclContext *dc;
if (auto closure = getAbstractClosureExpr())
dc = closure->getLocalContext();
Expand Down
12 changes: 9 additions & 3 deletions lib/SIL/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,16 @@ bool SILFunction::hasValidLinkageForFragileRef() const {
if (hasValidLinkageForFragileInline())
return true;

// If the containing module has been serialized
if (getModule().isSerialized()) {
// If the containing module has been serialized already, we no longer
// enforce any invariants.
if (getModule().isSerialized())
return true;
}

// If the function has a subclass scope that limits its visibility outside
// the module despite its linkage, we cannot reference it.
if (getClassSubclassScope() == SubclassScope::Resilient &&
isAvailableExternally())
return false;

// Otherwise, only public functions can be referenced.
return hasPublicVisibility(getLinkage());
Expand Down
107 changes: 86 additions & 21 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ namespace {

/// Verify invariants on a key path component.
void verifyKeyPathComponent(SILModule &M,
ResilienceExpansion expansion,
llvm::function_ref<void(bool, StringRef)> require,
CanType &baseTy,
CanType leafTy,
Expand Down Expand Up @@ -211,13 +212,21 @@ void verifyKeyPathComponent(SILModule &M,
switch (auto kind = component.getKind()) {
case KeyPathPatternComponent::Kind::StoredProperty: {
auto property = component.getStoredPropertyDecl();
if (expansion == ResilienceExpansion::Minimal) {
require(property->getEffectiveAccess() >= AccessLevel::Public,
"Key path in serialized function cannot reference non-public "
"property");
}

auto fieldTy = baseTy->getTypeOfMember(M.getSwiftModule(), property)
->getReferenceStorageReferent()
->getCanonicalType();
require(fieldTy == componentTy,
"property decl should be a member of the base with the same type "
"as the component");
require(property->hasStorage(), "property must be stored");
require(!property->isResilient(M.getSwiftModule(), expansion),
"cannot access storage of resilient property");
auto propertyTy = loweredBaseTy.getFieldType(property, M);
require(propertyTy.getObjectType()
== loweredComponentTy.getObjectType(),
Expand Down Expand Up @@ -247,6 +256,12 @@ void verifyKeyPathComponent(SILModule &M,
// Getter should be <Sig...> @convention(thin) (@in_guaranteed Base) -> @out Result
{
auto getter = component.getComputedPropertyGetter();
if (expansion == ResilienceExpansion::Minimal) {
require(getter->hasValidLinkageForFragileRef(),
"Key path in serialized function should not reference "
"less visible getters");
}

auto substGetterType = getter->getLoweredFunctionType()
->substGenericArgs(M, patternSubs);
require(substGetterType->getRepresentation() ==
Expand Down Expand Up @@ -286,6 +301,12 @@ void verifyKeyPathComponent(SILModule &M,
// <Sig...> @convention(thin) (@in_guaranteed Result, @in Base) -> ()

auto setter = component.getComputedPropertySetter();
if (expansion == ResilienceExpansion::Minimal) {
require(setter->hasValidLinkageForFragileRef(),
"Key path in serialized function should not reference "
"less visible setters");
}

auto substSetterType = setter->getLoweredFunctionType()
->substGenericArgs(M, patternSubs);

Expand Down Expand Up @@ -1456,8 +1477,13 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
}

void checkAllocGlobalInst(AllocGlobalInst *AGI) {
SILGlobalVariable *RefG = AGI->getReferencedGlobal();
if (auto *VD = RefG->getDecl()) {
require(!VD->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient global");
}
if (F.isSerialized()) {
SILGlobalVariable *RefG = AGI->getReferencedGlobal();
require(RefG->isSerialized()
|| hasPublicVisibility(RefG->getLinkage()),
"alloc_global inside fragile function cannot "
Expand All @@ -1469,6 +1495,11 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
SILGlobalVariable *RefG = GAI->getReferencedGlobal();
require(GAI->getType().getObjectType() == RefG->getLoweredType(),
"global_addr/value must be the type of the variable it references");
if (auto *VD = RefG->getDecl()) {
require(!VD->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient global");
}
if (F.isSerialized()) {
require(RefG->isSerialized()
|| hasPublicVisibility(RefG->getLinkage()),
Expand Down Expand Up @@ -2041,6 +2072,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
require(!structDecl->hasUnreferenceableStorage(),
"Cannot build a struct with unreferenceable storage from elements "
"using StructInst");
require(!structDecl->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient struct");
require(SI->getType().isObject(),
"StructInst must produce an object");

Expand Down Expand Up @@ -2232,8 +2266,14 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
void checkDeallocRefInst(DeallocRefInst *DI) {
require(DI->getOperand()->getType().isObject(),
"Operand of dealloc_ref must be object");
require(DI->getOperand()->getType().getClassOrBoundGenericClass(),
"Operand of dealloc_ref must be of class type");
auto *cd = DI->getOperand()->getType().getClassOrBoundGenericClass();
require(cd, "Operand of dealloc_ref must be of class type");

if (!DI->canAllocOnStack()) {
require(!cd->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot directly deallocate resilient class");
}
}
void checkDeallocPartialRefInst(DeallocPartialRefInst *DPRI) {
require(DPRI->getInstance()->getType().isObject(),
Expand All @@ -2247,6 +2287,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
->getInstanceType()->getClassOrBoundGenericClass();
require(class2,
"Second operand of dealloc_partial_ref must be a class metatype");
require(!class2->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot directly deallocate resilient class");
while (class1 != class2) {
class1 = class1->getSuperclassDecl();
require(class1, "First operand not superclass of second instance type");
Expand Down Expand Up @@ -2343,6 +2386,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"result of struct_extract cannot be address");
StructDecl *sd = operandTy.getStructOrBoundGenericStruct();
require(sd, "must struct_extract from struct");
require(!sd->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient struct");
require(!EI->getField()->isStatic(),
"cannot get address of static property with struct_element_addr");
require(EI->getField()->hasStorage(),
Expand Down Expand Up @@ -2384,6 +2430,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
"must derive struct_element_addr from address");
StructDecl *sd = operandTy.getStructOrBoundGenericStruct();
require(sd, "struct_element_addr operand must be struct address");
require(!sd->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient struct");
require(EI->getType().isAddress(),
"result of struct_element_addr must be address");
require(!EI->getField()->isStatic(),
Expand Down Expand Up @@ -2413,6 +2462,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
SILType operandTy = EI->getOperand()->getType();
ClassDecl *cd = operandTy.getClassOrBoundGenericClass();
require(cd, "ref_element_addr operand must be a class instance");
require(!cd->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient class");

require(EI->getField()->getDeclContext() == cd,
"ref_element_addr field must be a member of the class");
Expand All @@ -2433,8 +2485,21 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
SILType operandTy = RTAI->getOperand()->getType();
ClassDecl *cd = operandTy.getClassOrBoundGenericClass();
require(cd, "ref_tail_addr operand must be a class instance");
require(!cd->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient class");
require(cd, "ref_tail_addr operand must be a class instance");
}


void checkDestructureStructInst(DestructureStructInst *DSI) {
SILType operandTy = DSI->getOperand()->getType();
StructDecl *sd = operandTy.getStructOrBoundGenericStruct();
require(sd, "must struct_extract from struct");
require(!sd->isResilient(F.getModule().getSwiftModule(),
F.getResilienceExpansion()),
"cannot access storage of resilient struct");
}

SILType getMethodSelfType(CanSILFunctionType ft) {
return fnConv.getSILType(ft->getParameters().back());
}
Expand Down Expand Up @@ -3867,8 +3932,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {

// Find the set of enum elements for the type so we can verify
// exhaustiveness.
// FIXME: We also need to consider if the enum is resilient, in which case
// we're never guaranteed to be exhaustive.
llvm::DenseSet<EnumElementDecl*> unswitchedElts;
uDecl->getAllElements(unswitchedElts);

Expand Down Expand Up @@ -3954,8 +4017,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {

// Find the set of enum elements for the type so we can verify
// exhaustiveness.
// FIXME: We also need to consider if the enum is resilient, in which case
// we're never guaranteed to be exhaustive.
llvm::DenseSet<EnumElementDecl*> unswitchedElts;
uDecl->getAllElements(unswitchedElts);

Expand All @@ -3979,9 +4040,12 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
}

// If the switch is non-exhaustive, we require a default.
require(unswitchedElts.empty() || SOI->hasDefault(),
"nonexhaustive switch_enum_addr must have a default "
"destination");
bool isExhaustive =
uDecl->isEffectivelyExhaustive(F.getModule().getSwiftModule(),
F.getResilienceExpansion());
require((isExhaustive && unswitchedElts.empty()) || SOI->hasDefault(),
"nonexhaustive switch_enum_addr must have a default destination");

if (SOI->hasDefault())
require(SOI->getDefaultBB()->args_empty(),
"switch_enum_addr default destination must take "
Expand Down Expand Up @@ -4242,7 +4306,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
break;
}

verifyKeyPathComponent(F.getModule(),
verifyKeyPathComponent(F.getModule(), F.getResilienceExpansion(),
[&](bool reqt, StringRef message) { _require(reqt, message); },
baseTy,
leafTy,
Expand Down Expand Up @@ -4879,15 +4943,16 @@ void SILProperty::verify(const SILModule &M) const {

if (auto &component = getComponent()) {
verifyKeyPathComponent(const_cast<SILModule&>(M),
require,
baseTy,
leafTy,
*component,
{},
canSig,
subs,
/*property descriptor*/true,
hasIndices);
ResilienceExpansion::Maximal,
require,
baseTy,
leafTy,
*component,
{},
canSig,
subs,
/*property descriptor*/true,
hasIndices);
// verifyKeyPathComponent updates baseTy to be the projected type of the
// component, which should be the same as the type of the declared storage
require(baseTy == leafTy,
Expand Down
10 changes: 10 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,16 @@ void AttributeChecker::visitInlinableAttr(InlinableAttr *attr) {
access);
return;
}

// @inlinable cannot be applied to deinitializers in resilient classes.
if (auto *DD = dyn_cast<DestructorDecl>(D)) {
if (auto *CD = dyn_cast<ClassDecl>(DD->getDeclContext())) {
if (CD->isResilient()) {
diagnoseAndRemoveAttr(attr, diag::inlinable_resilient_deinit);
return;
}
}
}
}

void AttributeChecker::visitOptimizeAttr(OptimizeAttr *attr) {
Expand Down
2 changes: 1 addition & 1 deletion test/ClangImporter/static_inline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// RUN: %FileCheck < %t/static_inline.sil %s
// RUN: %target-swift-frontend -parse-as-library -module-name=static_inline -O -emit-ir %t/static_inline.sil -enable-objc-interop -import-objc-header %S/Inputs/static_inline.h | %FileCheck --check-prefix=CHECK-IR %s

// CHECK: sil shared [serializable] [clang c_inline_func] @c_inline_func : $@convention(c) (Int32) -> Int32
// CHECK: sil shared [clang c_inline_func] @c_inline_func : $@convention(c) (Int32) -> Int32

// CHECK-IR-LABEL: define{{.*}} i32 @"$s13static_inline6testit1xs5Int32VAE_tF"(i32)
// CHECK-IR: = add {{.*}}, 27
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/class_resilience.sil
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import resilient_class
sil @allocResilientOutsideParent : $@convention(thin) () -> () {
bb0:
%c = alloc_ref $ResilientOutsideParent
dealloc_ref %c : $ResilientOutsideParent
strong_release %c : $ResilientOutsideParent
%result = tuple ()
return %result : $()
}
9 changes: 3 additions & 6 deletions test/ParseableInterface/ObjC.swiftinterface
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@ public class SomeClassInlinable {
@inlinable get { return 0 }
@inlinable set {}
}
@objc @inlinable deinit {
print("bye")
}
@objc deinit
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jrose-apple @objc is a no-op on a deinit isn't it? Should we warn and remove the attribute?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is how it gets printed right now, so we have to handle reading it back in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, so it would be a warning at best. I just think it might confuse people ("do I need to @objc my deinit if my class is @objc, or otherwise some weird edge case won't work?" "No, it actually does nothing")

Copy link
Contributor

@jrose-apple jrose-apple Dec 21, 2018

Choose a reason for hiding this comment

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

*shrug* A warning sounds fine. I'd also be happy if we (additionally) didn't print it here.

}

public class SomeNSObject : NSObject {
@objc init?(_: Any)
@objc func foo()
Expand All @@ -53,7 +52,5 @@ public class SomeNSObjectInlinable : NSObject {
@inlinable get { return 0 }
@inlinable set {}
}
@objc @inlinable deinit {
print("bye")
}
@objc deinit
}
2 changes: 1 addition & 1 deletion test/SIL/Serialization/boxes.sil
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// First parse this and then emit a *.sib. Then read in the *.sib, then recreate
// RUN: %empty-directory(%t)
// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name boxes
// RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.2.sib -module-name boxes
// RUN: %target-sil-opt %t/tmp.sib -emit-sib -o %t/tmp.2.sib -module-name boxes
// RUN: %target-sil-opt %t/tmp.2.sib -module-name boxes | %FileCheck %s

sil_stage canonical
Expand Down
Loading