//===--- Verifier.cpp - Verification of Swift SIL Code --------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 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 // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-verifier" #include "swift/AST/ASTContext.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Types.h" #include "swift/Basic/Range.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/DebugUtils.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/DynamicCasts.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/MemoryLifetime.h" #include "swift/SIL/PostOrder.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILDebugScope.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILModule.h" #include "swift/SIL/SILOpenedArchetypesTracker.h" #include "swift/SIL/SILVTable.h" #include "swift/SIL/SILVTableVisitor.h" #include "swift/SIL/SILVisitor.h" #include "swift/SIL/TypeLowering.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; using Lowering::AbstractionPattern; // This flag is used only to check that sil-combine can properly // remove any code after unreachable, thus bringing SIL into // its canonical form which may get temporarily broken during // intermediate transformations. static llvm::cl::opt SkipUnreachableMustBeLastErrors( "verify-skip-unreachable-must-be-last", llvm::cl::init(false)); // This flag controls the default behaviour when hitting a verification // failure (abort/exit). static llvm::cl::opt AbortOnFailure( "verify-abort-on-failure", llvm::cl::init(true)); static llvm::cl::opt VerifyDIHoles( "verify-di-holes", llvm::cl::init(true)); static llvm::cl::opt SkipConvertEscapeToNoescapeAttributes( "verify-skip-convert-escape-to-noescape-attributes", llvm::cl::init(false)); // The verifier is basically all assertions, so don't compile it with NDEBUG to // prevent release builds from triggering spurious unused variable warnings. //===----------------------------------------------------------------------===// // SILVerifier //===----------------------------------------------------------------------===// /// Returns true if A is an opened existential type or is equal to an /// archetype from F's generic context. static bool isArchetypeValidInFunction(ArchetypeType *A, const SILFunction *F) { auto root = dyn_cast(A->getRoot()); if (!root) return true; if (isa(A->getRoot())) return true; if (isa(A->getRoot())) return true; // Ok, we have a primary archetype, make sure it is in the nested generic // environment of our caller. if (auto *genericEnv = F->getGenericEnvironment()) if (root->getGenericEnvironment() == genericEnv) return true; return false; } namespace { /// Metaprogramming-friendly base class. template class SILVerifierBase : public SILInstructionVisitor { public: // visitCLASS calls visitPARENT and checkCLASS. // checkCLASS does nothing by default. #define INST(CLASS, PARENT) \ void visit##CLASS(CLASS *I) { \ static_cast(this)->visit##PARENT(I); \ static_cast(this)->check##CLASS(I); \ } \ void check##CLASS(CLASS *I) {} #include "swift/SIL/SILNodes.def" void visitSILInstruction(SILInstruction *I) { static_cast(this)->checkSILInstruction(I); } void checkSILInstruction(SILInstruction *I) {} }; } // end anonymous namespace namespace { /// Verify invariants on a key path component. void verifyKeyPathComponent(SILModule &M, ResilienceExpansion expansion, llvm::function_ref require, CanType &baseTy, CanType leafTy, const KeyPathPatternComponent &component, ArrayRef operands, CanGenericSignature patternSig, SubstitutionMap patternSubs, bool forPropertyDescriptor, bool hasIndices) { auto &C = M.getASTContext(); auto typeExpansionContext = TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(expansion); auto opaque = AbstractionPattern::getOpaque(); auto loweredBaseTy = M.Types.getLoweredType(opaque, baseTy, typeExpansionContext); auto componentTy = component.getComponentType().subst(patternSubs) ->getCanonicalType(); auto loweredComponentTy = M.Types.getLoweredType(opaque, componentTy, typeExpansionContext); auto checkIndexEqualsAndHash = [&]{ if (!component.getSubscriptIndices().empty()) { // Equals should be // @convention(thin) (RawPointer, RawPointer) -> Bool { auto equals = component.getSubscriptIndexEquals(); require(equals, "key path pattern with indexes must have equals " "operator"); auto substEqualsType = equals->getLoweredFunctionType() ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substEqualsType->getParameters().size() == 2, "must have two arguments"); for (unsigned i = 0; i < 2; ++i) { auto param = substEqualsType->getParameters()[i]; require(param.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); require(param.getInterfaceType()->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); } require(substEqualsType->getResults().size() == 1, "must have one result"); require(substEqualsType->getResults()[0].getConvention() == ResultConvention::Unowned, "result should be unowned"); require(substEqualsType->getResults()[0].getInterfaceType() ->getAnyNominal() == C.getBoolDecl(), "result should be Bool"); } { // Hash should be // @convention(thin) (RawPointer) -> Int auto hash = component.getSubscriptIndexHash(); require(hash, "key path pattern with indexes must have hash " "operator"); auto substHashType = hash->getLoweredFunctionType() ->substGenericArgs(M, patternSubs, TypeExpansionContext::minimal()); require(substHashType->getParameters().size() == 1, "must have two arguments"); auto param = substHashType->getParameters()[0]; require(param.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); require(param.getInterfaceType()->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); require(substHashType->getResults().size() == 1, "must have one result"); require(substHashType->getResults()[0].getConvention() == ResultConvention::Unowned, "result should be unowned"); require(substHashType->getResults()[0].getInterfaceType() ->getAnyNominal() == C.getIntDecl(), "result should be Int"); } } else { require(!component.getSubscriptIndexEquals() && !component.getSubscriptIndexHash(), "component without indexes must not have equals/hash"); } }; 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, typeExpansionContext); require(propertyTy.getObjectType() == loweredComponentTy.getObjectType(), "component type should match the maximal abstraction of the " "formal type"); break; } case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: { if (forPropertyDescriptor) { require(component.getSubscriptIndices().empty() && !component.getSubscriptIndexEquals() && !component.getSubscriptIndexHash(), "property descriptor should not have index information"); require(component.getExternalDecl() == nullptr && component.getExternalSubstitutions().empty(), "property descriptor should not refer to another external decl"); } else { require(hasIndices == !component.getSubscriptIndices().empty(), "component for subscript should have indices"); } auto normalArgConvention = ParameterConvention::Indirect_In_Guaranteed; // Getter should be @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, TypeExpansionContext::minimal()); require(substGetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, "getter should be a thin function"); require(substGetterType->getNumParameters() == 1 + hasIndices, "getter should have one parameter"); auto baseParam = substGetterType->getParameters()[0]; require(baseParam.getConvention() == normalArgConvention, "getter base parameter should have normal arg convention"); require(baseParam.getArgumentType(M, substGetterType) == loweredBaseTy.getASTType(), "getter base parameter should match base of component"); if (hasIndices) { auto indicesParam = substGetterType->getParameters()[1]; require(indicesParam.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); require(indicesParam.getArgumentType(M, substGetterType)->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); } require(substGetterType->getNumResults() == 1, "getter should have one result"); auto result = substGetterType->getResults()[0]; require(result.getConvention() == ResultConvention::Indirect, "getter result should be @out"); require(result.getReturnValueType(M, substGetterType) == loweredComponentTy.getASTType(), "getter result should match the maximal abstraction of the " "formal component type"); } if (kind == KeyPathPatternComponent::Kind::SettableProperty) { // Setter should be // @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, TypeExpansionContext::minimal()); require(substSetterType->getRepresentation() == SILFunctionTypeRepresentation::Thin, "setter should be a thin function"); require(substSetterType->getNumParameters() == 2 + hasIndices, "setter should have two parameters"); auto newValueParam = substSetterType->getParameters()[0]; // TODO: This should probably be unconditionally +1 when we // can represent that. require(newValueParam.getConvention() == normalArgConvention, "setter value parameter should havee normal arg convention"); auto baseParam = substSetterType->getParameters()[1]; require(baseParam.getConvention() == normalArgConvention || baseParam.getConvention() == ParameterConvention::Indirect_Inout, "setter base parameter should be normal arg convention " "or @inout"); if (hasIndices) { auto indicesParam = substSetterType->getParameters()[2]; require(indicesParam.getConvention() == ParameterConvention::Direct_Unowned, "indices pointer should be trivial"); require(indicesParam.getArgumentType(M, substSetterType)->getAnyNominal() == C.getUnsafeRawPointerDecl(), "indices pointer should be an UnsafeRawPointer"); } require(newValueParam.getArgumentType(M, substSetterType) == loweredComponentTy.getASTType(), "setter value should match the maximal abstraction of the " "formal component type"); require(substSetterType->getNumResults() == 0, "setter should have no results"); } if (!forPropertyDescriptor) { for (auto &index : component.getSubscriptIndices()) { auto opIndex = index.Operand; auto contextType = index.LoweredType.subst(M, patternSubs); require(contextType == operands[opIndex].get()->getType(), "operand must match type required by pattern"); SILType loweredType = index.LoweredType; require( loweredType.isLoweringOf(typeExpansionContext, M, index.FormalType), "pattern index formal type doesn't match lowered type"); } checkIndexEqualsAndHash(); } break; } case KeyPathPatternComponent::Kind::OptionalChain: { require(baseTy->getOptionalObjectType()->isEqual(componentTy), "chaining component should unwrap optional"); require((bool)leafTy->getOptionalObjectType(), "key path with chaining component should have optional " "result"); break; } case KeyPathPatternComponent::Kind::OptionalForce: { require(baseTy->getOptionalObjectType()->isEqual(componentTy), "forcing component should unwrap optional"); break; } case KeyPathPatternComponent::Kind::OptionalWrap: { require(componentTy->getOptionalObjectType()->isEqual(baseTy), "wrapping component should wrap optional"); break; } case KeyPathPatternComponent::Kind::TupleElement: { require(loweredBaseTy.is(), "invalid baseTy, should have been a TupleType"); auto tupleTy = loweredBaseTy.castTo(); auto eltIdx = component.getTupleIndex(); require(eltIdx < tupleTy->getNumElements(), "invalid element index, greater than # of tuple elements"); auto eltTy = tupleTy.getElementType(eltIdx) .getReferenceStorageReferent(); require(eltTy == componentTy, "tuple element type should match the type of the component"); break; } } baseTy = componentTy; } /// Check if according to the SIL language model this memory /must only/ be used /// immutably. Today this is only applied to in_guaranteed arguments and /// open_existential_addr. We should expand it as needed. struct ImmutableAddressUseVerifier { SmallVector worklist; bool isConsumingOrMutatingArgumentConvention(SILArgumentConvention conv) { switch (conv) { case SILArgumentConvention::Indirect_In_Guaranteed: return false; case SILArgumentConvention::Indirect_InoutAliasable: // DISCUSSION: We do not consider inout_aliasable to be "truly mutating" // since today it is just used as a way to mark a captured argument and // not that something truly has mutating semantics. The reason why this // is safe is that the typechecker guarantees that if our value was // immutable, then the use in the closure must be immutable as well. // // TODO: Remove this in favor of using Inout and In_Guaranteed. return false; case SILArgumentConvention::Indirect_Out: case SILArgumentConvention::Indirect_In: case SILArgumentConvention::Indirect_In_Constant: case SILArgumentConvention::Indirect_Inout: return true; case SILArgumentConvention::Direct_Unowned: case SILArgumentConvention::Direct_Guaranteed: case SILArgumentConvention::Direct_Owned: case SILArgumentConvention::Direct_Deallocating: assert(conv.isIndirectConvention() && "Expect an indirect convention"); return true; // return something "conservative". } llvm_unreachable("covered switch isn't covered?!"); } bool isConsumingOrMutatingApplyUse(Operand *use) { ApplySite apply(use->getUser()); assert(apply && "Not an apply instruction kind"); auto conv = apply.getArgumentConvention(*use); return isConsumingOrMutatingArgumentConvention(conv); } bool isConsumingOrMutatingYieldUse(Operand *use) { // For now, just say that it is non-consuming for now. auto *yield = cast(use->getUser()); auto conv = yield->getArgumentConventionForOperand(*use); return isConsumingOrMutatingArgumentConvention(conv); } // A "copy_addr %src [take] to *" is consuming on "%src". // A "copy_addr * to * %dst" is mutating on "%dst". bool isConsumingOrMutatingCopyAddrUse(Operand *use) { auto *copyAddr = cast(use->getUser()); if (copyAddr->getDest() == use->get()) return true; if (copyAddr->getSrc() == use->get() && copyAddr->isTakeOfSrc() == IsTake) return true; return false; } bool isCastToNonConsuming(UncheckedAddrCastInst *i) { // Check if any of our uses are consuming. If none of them are consuming, we // are good to go. return llvm::none_of(i->getUses(), [&](Operand *use) -> bool { auto *inst = use->getUser(); switch (inst->getKind()) { default: return false; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::PartialApplyInst: case SILInstructionKind::BeginApplyInst: return isConsumingOrMutatingApplyUse(use); } }); } bool isMutatingOrConsuming(SILValue address) { llvm::copy(address->getUses(), std::back_inserter(worklist)); while (!worklist.empty()) { auto *use = worklist.pop_back_val(); auto *inst = use->getUser(); if (inst->isTypeDependentOperand(*use)) continue; // TODO: Can this switch be restructured so break -> error, continue -> // next iteration, return -> return the final result. switch (inst->getKind()) { case SILInstructionKind::BuiltinInst: { // If we are processing a polymorphic builtin that takes an address, // skip the builtin. This is because the builtin must be specialized to // a non-memory reading builtin that works on trivial object values // before the diagnostic passes end (or be DCEed) or we emit a // diagnostic. if (auto builtinKind = cast(inst)->getBuiltinKind()) { if (isPolymorphicBuiltin(*builtinKind)) { break; } } // Otherwise this is a builtin that we are not expecting to see, so bail // and assert. llvm::errs() << "Unhandled, unexpected builtin instruction: " << *inst; llvm_unreachable("invoking standard assertion failure"); break; } case SILInstructionKind::MarkDependenceInst: case SILInstructionKind::LoadBorrowInst: case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::ExistentialMetatypeInst: case SILInstructionKind::ValueMetatypeInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::KeyPathInst: case SILInstructionKind::SwitchEnumAddrInst: break; case SILInstructionKind::AddressToPointerInst: // We assume that the user is attempting to do something unsafe since we // are converting to a raw pointer. So just ignore this use. // // TODO: Can we do better? break; case SILInstructionKind::BranchInst: case SILInstructionKind::CondBranchInst: // We do not analyze through branches and cond_br instructions and just // assume correctness. This is so that we can avoid having to analyze // through phi loops and since we want to remove address phis (meaning // that this eventually would never be able to happen). Once that // changes happens, we should remove this code and just error below. break; case SILInstructionKind::ApplyInst: case SILInstructionKind::TryApplyInst: case SILInstructionKind::PartialApplyInst: case SILInstructionKind::BeginApplyInst: if (isConsumingOrMutatingApplyUse(use)) return true; break; case SILInstructionKind::YieldInst: if (isConsumingOrMutatingYieldUse(use)) return true; break; case SILInstructionKind::CopyAddrInst: if (isConsumingOrMutatingCopyAddrUse(use)) return true; else break; case SILInstructionKind::DestroyAddrInst: return true; case SILInstructionKind::UncheckedAddrCastInst: { if (isCastToNonConsuming(cast(inst))) { break; } return true; } case SILInstructionKind::CheckedCastAddrBranchInst: switch (cast(inst)->getConsumptionKind()) { case CastConsumptionKind::BorrowAlways: llvm_unreachable("checked_cast_addr_br cannot have BorrowAlways"); case CastConsumptionKind::CopyOnSuccess: break; case CastConsumptionKind::TakeAlways: case CastConsumptionKind::TakeOnSuccess: return true; } break; case SILInstructionKind::LoadInst: // A 'non-taking' value load is harmless. if (cast(inst)->getOwnershipQualifier() == LoadOwnershipQualifier::Take) return true; break; #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Load##Name##Inst: \ if (cast(inst)->isTake()) \ return true; \ break; #include "swift/AST/ReferenceStorage.def" case SILInstructionKind::OpenExistentialAddrInst: // If we have a mutable use, return true. Otherwise fallthrough since we // want to look through immutable uses. if (cast(inst)->getAccessKind() != OpenedExistentialAccess::Immutable) return true; LLVM_FALLTHROUGH; case SILInstructionKind::StructElementAddrInst: case SILInstructionKind::TupleElementAddrInst: case SILInstructionKind::IndexAddrInst: case SILInstructionKind::TailAddrInst: case SILInstructionKind::IndexRawPointerInst: // Add these to our worklist. for (auto result : inst->getResults()) { llvm::copy(result->getUses(), std::back_inserter(worklist)); } break; case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { auto type = cast(inst)->getOperand()->getType(); if (type.getOptionalObjectType()) { for (auto result : inst->getResults()) { llvm::copy(result->getUses(), std::back_inserter(worklist)); } break; } llvm::errs() << "Unhandled, unexpected instruction: " << *inst; llvm_unreachable("invoking standard assertion failure"); break; } default: llvm::errs() << "Unhandled, unexpected instruction: " << *inst; llvm_unreachable("invoking standard assertion failure"); break; } } return false; } }; /// The SIL verifier walks over a SIL function / basic block / instruction, /// checking and enforcing its invariants. class SILVerifier : public SILVerifierBase { ModuleDecl *M; const SILFunction &F; SILFunctionConventions fnConv; Lowering::TypeConverter &TC; SILOpenedArchetypesTracker OpenedArchetypes; SmallVector DebugVars; const SILInstruction *CurInstruction = nullptr; const SILArgument *CurArgument = nullptr; std::unique_ptr Dominance; // Used for dominance checking within a basic block. llvm::DenseMap InstNumbers; DeadEndBlocks DEBlocks; bool SingleFunction = true; SILVerifier(const SILVerifier&) = delete; void operator=(const SILVerifier&) = delete; public: bool isSILOwnershipEnabled() const { return F.getModule().getOptions().VerifySILOwnership; } void _require(bool condition, const Twine &complaint, const std::function &extraContext = nullptr) { if (condition) return; llvm::dbgs() << "SIL verification failed: " << complaint << "\n"; if (extraContext) extraContext(); if (CurInstruction) { llvm::dbgs() << "Verifying instruction:\n"; CurInstruction->printInContext(llvm::dbgs()); } else if (CurArgument) { llvm::dbgs() << "Verifying argument:\n"; CurArgument->printInContext(llvm::dbgs()); } llvm::dbgs() << "In function:\n"; F.print(llvm::dbgs()); // We abort by default because we want to always crash in // the debugger. if (AbortOnFailure) abort(); else exit(1); } #define require(condition, complaint) \ _require(bool(condition), complaint ": " #condition) template typename CanTypeWrapperTraits::type _requireObjectType(SILType type, const Twine &valueDescription, const char *typeName) { _require(type.isObject(), valueDescription + " must be an object"); auto result = type.getAs(); _require(bool(result), valueDescription + " must have type " + typeName); return result; } template typename CanTypeWrapperTraits::type _requireObjectType(SILValue value, const Twine &valueDescription, const char *typeName) { return _requireObjectType(value->getType(), valueDescription, typeName); } #define requireObjectType(type, value, valueDescription) \ _requireObjectType(value, valueDescription, #type) template typename CanTypeWrapperTraits::type _requireAddressType(SILType type, const Twine &valueDescription, const char *typeName) { _require(type.isAddress(), valueDescription + " must be an address"); auto result = type.getAs(); _require(bool(result), valueDescription + " must have type " + typeName); return result; } template typename CanTypeWrapperTraits::type _requireAddressType(SILValue value, const Twine &valueDescription, const char *typeName) { return _requireAddressType(value->getType(), valueDescription, typeName); } #define requireAddressType(type, value, valueDescription) \ _requireAddressType(value, valueDescription, #type) template typename CanTypeWrapperTraits::type _forbidObjectType(SILType type, const Twine &valueDescription, const char *typeName) { _require(type.isObject(), valueDescription + " must be an object"); auto result = type.getAs(); _require(!bool(result), valueDescription + " must not have type " + typeName); return result; } template typename CanTypeWrapperTraits::type _forbidObjectType(SILValue value, const Twine &valueDescription, const char *typeName) { return _forbidObjectType(value->getType(), valueDescription, typeName); } #define forbidObjectType(type, value, valueDescription) \ _forbidObjectType(value, valueDescription, #type) // Require that the operand is a non-optional, non-unowned reference-counted // type. void requireReferenceValue(SILValue value, const Twine &valueDescription) { require(value->getType().isObject(), valueDescription +" must be an object"); require(value->getType().isReferenceCounted(F.getModule()), valueDescription + " must have reference semantics"); forbidObjectType(UnownedStorageType, value, valueDescription); } // Require that the operand is a reference-counted type, or an Optional // thereof. void requireReferenceOrOptionalReferenceValue(SILValue value, const Twine &valueDescription) { require(value->getType().isObject(), valueDescription +" must be an object"); auto objectTy = value->getType().unwrapOptionalType(); require(objectTy.isReferenceCounted(F.getModule()), valueDescription + " must have reference semantics"); } // Require that the operand is a type that supports reference storage // modifiers. void requireReferenceStorageCapableValue(SILValue value, const Twine &valueDescription) { requireReferenceOrOptionalReferenceValue(value, valueDescription); require(!value->getType().is(), valueDescription + " cannot apply to a function type"); } /// Assert that two types are equal. void requireSameType(Type type1, Type type2, const Twine &complaint) { _require(type1->isEqual(type2), complaint, [&] { llvm::dbgs() << " " << type1 << "\n " << type2 << '\n'; }); } /// Assert that two types are equal. void requireSameType(SILType type1, SILType type2, const Twine &complaint) { _require(type1 == type2, complaint, [&] { llvm::dbgs() << " " << type1 << "\n " << type2 << '\n'; }); } /// Require two function types to be ABI-compatible. void requireABICompatibleFunctionTypes(CanSILFunctionType type1, CanSILFunctionType type2, const Twine &what, SILFunction &inFunction) { auto complain = [=](const char *msg) -> std::function { return [=]{ llvm::dbgs() << " " << msg << '\n' << " " << type1 << "\n " << type2 << '\n'; }; }; auto complainBy = [=](std::function msg) -> std::function { return [=]{ msg(); llvm::dbgs() << '\n'; llvm::dbgs() << " " << type1 << "\n " << type2 << '\n'; }; }; // If we didn't have a failure, return. auto Result = type1->isABICompatibleWith(type2, inFunction); if (Result.isCompatible()) return; if (!Result.hasPayload()) { _require(false, what, complain(Result.getMessage().data())); } else { _require(false, what, complainBy([=] { llvm::dbgs() << " " << Result.getMessage().data() << ".\nParameter: " << Result.getPayload(); })); } } void requireSameFunctionComponents(CanSILFunctionType type1, CanSILFunctionType type2, const Twine &what) { require(type1->getNumResults() == type2->getNumResults(), "results of " + what + " do not match in count"); for (auto i : indices(type1->getResults())) { require(type1->getResults()[i] == type2->getResults()[i], "result " + Twine(i) + " of " + what + " do not match"); } require(type1->getParameters().size() == type2->getParameters().size(), "inputs of " + what + " do not match in count"); for (auto i : indices(type1->getParameters())) { require(type1->getParameters()[i] == type2->getParameters()[i], "input " + Twine(i) + " of " + what + " do not match"); } } static unsigned numInstsInFunction(const SILFunction &F) { unsigned numInsts = 0; for (auto &BB : F) { numInsts += std::distance(BB.begin(), BB.end()); } return numInsts; } SILVerifier(const SILFunction &F, bool SingleFunction = true) : M(F.getModule().getSwiftModule()), F(F), fnConv(F.getConventionsInContext()), TC(F.getModule().Types), OpenedArchetypes(&F), Dominance(nullptr), InstNumbers(numInstsInFunction(F)), DEBlocks(&F), SingleFunction(SingleFunction) { if (F.isExternalDeclaration()) return; // Check to make sure that all blocks are well formed. If not, the // SILVerifier object will explode trying to compute dominance info. unsigned InstIdx = 0; for (auto &BB : F) { require(!BB.empty(), "Basic blocks cannot be empty"); require(isa(BB.back()), "Basic blocks must end with a terminator instruction"); for (auto &I : BB) InstNumbers[&I] = InstIdx++; } Dominance.reset(new DominanceInfo(const_cast(&F))); auto *DebugScope = F.getDebugScope(); require(DebugScope, "All SIL functions must have a debug scope"); require(DebugScope->Parent.get() == &F, "Scope of SIL function points to different function"); } // Checks dominance between two instructions. // This does not use DominanceInfo.properlyDominates, because for large basic // blocks it would result in quadratic behavior. bool properlyDominates(SILInstruction *a, SILInstruction *b) { auto aBlock = a->getParent(), bBlock = b->getParent(); // If the blocks are different, it's as easy as whether A's block // dominates B's block. if (aBlock != bBlock) return Dominance->properlyDominates(aBlock, bBlock); return InstNumbers[a] < InstNumbers[b]; } // FIXME: For sanity, address-type block args should be prohibited at all SIL // stages. However, the optimizer currently breaks the invariant in three // places: // 1. Normal Simplify CFG during conditional branch simplification // (sneaky jump threading). // 2. Simplify CFG via Jump Threading. // 3. Loop Rotation. // // bool prohibitAddressBlockArgs() { // If this function was deserialized from canonical SIL, this invariant may // already have been violated regardless of this module's SIL stage or // exclusivity enforcement level. Presumably, access markers were already // removed prior to serialization. if (F.wasDeserializedCanonical()) return false; SILModule &M = F.getModule(); return M.getStage() == SILStage::Raw; } void visitSILPhiArgument(SILPhiArgument *arg) { // Verify that the `isPhiArgument` property is sound: // - Phi arguments come from branches. // - Non-phi arguments have a single predecessor. if (arg->isPhiArgument()) { for (SILBasicBlock *predBB : arg->getParent()->getPredecessorBlocks()) { auto *TI = predBB->getTerminator(); // FIXME: when critical edges are removed, only allow BranchInst. require(isa (TI) || isa(TI), "All phi argument inputs must be from branches."); } } else { } if (arg->isPhiArgument() && prohibitAddressBlockArgs()) { // As a property of well-formed SIL, we disallow address-type block // arguments. Supporting them would prevent reliably reasoning about the // underlying storage of memory access. This reasoning is important for // diagnosing violations of memory access rules and supporting future // optimizations such as bitfield packing. Address-type block arguments // also create unnecessary complexity for SIL optimization passes that // need to reason about memory aliasing. require(!arg->getType().isAddress(), "Block arguments cannot be addresses"); } } void visitSILArgument(SILArgument *arg) { CurArgument = arg; checkLegalType(arg->getFunction(), arg, nullptr); checkValueBaseOwnership(arg); if (auto *phiArg = dyn_cast(arg)) { if (phiArg->isPhiArgument()) visitSILPhiArgument(phiArg); else { // A non-phi BlockArgument must have a single predecessor unless it is // unreachable. require(arg->getParent()->pred_empty() || arg->getParent()->getSinglePredecessorBlock(), "Non-branch terminator must have a unique successor."); } return; } // If we are not in lowered SIL and have an in_guaranteed function argument, // verify that we do not mutate or consume it. auto *fArg = cast(arg); if (fArg->getModule().getStage() == SILStage::Lowered || !fArg->getType().isAddress() || !fArg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) return; require(!ImmutableAddressUseVerifier().isMutatingOrConsuming(fArg), "Found mutating or consuming use of an in_guaranteed parameter?!"); } void visitSILInstruction(SILInstruction *I) { CurInstruction = I; OpenedArchetypes.registerOpenedArchetypes(I); checkSILInstruction(I); // Check the SILLLocation attached to the instruction. checkInstructionsSILLocation(I); // Check ownership. SILFunction *F = I->getFunction(); assert(F && "Expected value base with parent function"); for (auto result : I->getResults()) { checkLegalType(F, result, I); checkValueBaseOwnership(result); } } void checkValueBaseOwnership(ValueBase *V) { // If ownership is not enabled, bail. if (!isSILOwnershipEnabled()) return; SILFunction *F = V->getFunction(); assert(F && "Expected value base with parent function"); // If we do not have qualified ownership, then do not verify value base // ownership. if (!F->hasOwnership()) return; SILValue(V).verifyOwnership(&DEBlocks); } void checkSILInstruction(SILInstruction *I) { const SILBasicBlock *BB = I->getParent(); require(BB, "Instruction with null parent"); // Check that non-terminators look ok. if (!isa(I)) { require(!BB->empty(), "Can't be in a parent block if it is empty"); if (!I->isStaticInitializerInst()) { require(&*BB->rbegin() != I, "Non-terminators cannot be the last in a block"); } } else { // Skip the check for UnreachableInst, if explicitly asked to do so. if (!isa(I) || !SkipUnreachableMustBeLastErrors) require(&*BB->rbegin() == I, "Terminator must be the last in block"); } // Verify that all of our uses are in this function. for (auto result : I->getResults()) { for (Operand *use : result->getUses()) { auto user = use->getUser(); require(user, "instruction user is null?"); require(isa(user), "instruction used by non-instruction"); auto userI = cast(user); require(userI->getParent(), "instruction used by unparented instruction"); if (I->isStaticInitializerInst()) { require(userI->getParent() == BB, "instruction used by instruction not in same static initializer"); } else { require(userI->getFunction() == &F, "instruction used by instruction in different function"); } auto operands = userI->getAllOperands(); require(operands.begin() <= use && use <= operands.end(), "use doesn't actually belong to instruction it claims to"); } } // Verify some basis structural stuff about an instruction's operands. for (auto &operand : I->getAllOperands()) { require(operand.get(), "instruction has null operand"); if (auto *valueI = operand.get()->getDefiningInstruction()) { require(valueI->getParent(), "instruction uses value of unparented instruction"); if (I->isStaticInitializerInst()) { require(valueI->getParent() == BB, "instruction uses value which is not in same static initializer"); } else { require(valueI->getFunction() == &F, "instruction uses value of instruction from another function"); require(properlyDominates(valueI, I), "instruction isn't dominated by its operand"); } } if (auto *valueBBA = dyn_cast(operand.get())) { require(!I->isStaticInitializerInst(), "static initializer inst cannot refer to SILArgument"); require(valueBBA->getParent(), "instruction uses value of unparented instruction"); require(valueBBA->getFunction() == &F, "bb argument value from another function"); require(Dominance->dominates(valueBBA->getParent(), I->getParent()), "instruction isn't dominated by its bb argument operand"); } require(operand.getUser() == I, "instruction's operand's owner isn't the instruction"); require(isInValueUses(&operand), "operand value isn't used by operand"); if (I->isTypeDependentOperand(operand)) { require(isa(I), "opened archetype operand should refer to a SILInstruction"); } // Make sure that if operand is generic that its primary archetypes match // the function context. checkLegalType(I->getFunction(), operand.get(), I); } // TODO: There should be a use of an opened archetype inside the instruction for // each opened archetype operand of the instruction. } void checkInstructionsSILLocation(SILInstruction *I) { // Check the debug scope. auto *DS = I->getDebugScope(); if (DS && !maybeScopeless(*I)) require(DS, "instruction has a location, but no scope"); require(!DS || DS->getParentFunction() == I->getFunction(), "debug scope of instruction belongs to a different function"); // Check the location kind. SILLocation L = I->getLoc(); SILLocation::LocationKind LocKind = L.getKind(); SILInstructionKind InstKind = I->getKind(); // Check that there is at most one debug variable defined // for each argument slot. This catches SIL transformations // that accidentally remove inline information (stored in the SILDebugScope) // from debug-variable-carrying instructions. if (!DS->InlinedCallSite) { Optional VarInfo; if (auto *DI = dyn_cast(I)) VarInfo = DI->getVarInfo(); else if (auto *DI = dyn_cast(I)) VarInfo = DI->getVarInfo(); else if (auto *DI = dyn_cast(I)) VarInfo = DI->getVarInfo(); else if (auto *DI = dyn_cast(I)) VarInfo = DI->getVarInfo(); if (VarInfo) if (unsigned ArgNo = VarInfo->ArgNo) { // It is a function argument. if (ArgNo < DebugVars.size() && !DebugVars[ArgNo].empty() && !VarInfo->Name.empty()) { require( DebugVars[ArgNo] == VarInfo->Name, "Scope contains conflicting debug variables for one function " "argument"); } else { // Reserve enough space. while (DebugVars.size() <= ArgNo) { DebugVars.push_back(StringRef()); } } DebugVars[ArgNo] = VarInfo->Name; } } // Regular locations are allowed on all instructions. if (LocKind == SILLocation::RegularKind) return; if (LocKind == SILLocation::ReturnKind || LocKind == SILLocation::ImplicitReturnKind) require(InstKind == SILInstructionKind::BranchInst || InstKind == SILInstructionKind::ReturnInst || InstKind == SILInstructionKind::UnreachableInst, "return locations are only allowed on branch and return instructions"); if (LocKind == SILLocation::ArtificialUnreachableKind) require(InstKind == SILInstructionKind::UnreachableInst, "artificial locations are only allowed on Unreachable instructions"); } /// Check that the types of this value producer are all legal in the function /// context in which it exists. void checkLegalType(SILFunction *F, ValueBase *value, SILInstruction *I) { SILType type = value->getType(); if (type.is()) { require(isLegalSILTokenProducer(value), "SIL tokens can only be produced as the results of specific " "instructions"); return; } checkLegalType(F, type, I); } static bool isLegalSILTokenProducer(SILValue value) { if (auto beginApply = dyn_cast(value)) return beginApply->isTokenResult(); // Add more token cases here as they arise. return false; } /// Check that the given type is a legal SIL value type. void checkLegalType(SILFunction *F, SILType type, SILInstruction *I) { checkLegalSILType(F, type.getASTType(), I); } /// Check that the given type is a legal SIL value type. void checkLegalSILType(SILFunction *F, CanType rvalueType, SILInstruction *I) { // These types should have been removed by lowering. require(!isa(rvalueType), "l-value types are not legal in SIL"); require(!isa(rvalueType), "AST function types are not legal in SIL"); // Tuples should have had their element lowered. if (auto tuple = dyn_cast(rvalueType)) { for (auto eltTy : tuple.getElementTypes()) { checkLegalSILType(F, eltTy, I); } return; } // Optionals should have had their objects lowered. if (auto objectType = rvalueType.getOptionalObjectType()) { return checkLegalSILType(F, objectType, I); } // Metatypes should have explicit representations. if (auto metatype = dyn_cast(rvalueType)) { require(metatype->hasRepresentation(), "metatypes in SIL must have a representation");; // fallthrough for archetype check } rvalueType.visit([&](CanType t) { auto A = dyn_cast(t); if (!A) return; require(isArchetypeValidInFunction(A, F), "Operand is of an ArchetypeType that does not exist in the " "Caller's generic param list."); if (auto OpenedA = getOpenedArchetypeOf(A)) { auto Def = OpenedArchetypes.getOpenedArchetypeDef(OpenedA); require (Def, "Opened archetype should be registered in SILFunction"); require(I == nullptr || Def == I || properlyDominates(cast(Def), I), "Use of an opened archetype should be dominated by a " "definition of this opened archetype"); } }); } /// Check that this operand appears in the use-chain of the value it uses. static bool isInValueUses(const Operand *operand) { for (auto use : operand->get()->getUses()) if (use == operand) return true; return false; } /// \return True if all of the users of the AllocStack instruction \p ASI are /// inside the same basic block. static bool isSingleBlockUsage(AllocStackInst *ASI, DominanceInfo *Dominance){ SILBasicBlock *BB = ASI->getParent(); for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) if (UI->getUser()->getParent() != BB && Dominance->isReachableFromEntry(UI->getUser()->getParent())) return false; return true; } void checkAllocStackInst(AllocStackInst *AI) { require(AI->getType().isAddress(), "result of alloc_stack must be an address type"); verifyOpenedArchetype(AI, AI->getElementType().getASTType()); // There used to be a check if all uses of ASI are inside the alloc-dealloc // range. But apparently it can be the case that ASI has uses after the // dealloc_stack. This can come up if the source contains a // withUnsafePointer where the pointer escapes. // It's illegal code but the compiler should not crash on it. } void checkAllocRefBase(AllocRefInstBase *ARI) { requireReferenceValue(ARI, "Result of alloc_ref"); verifyOpenedArchetype(ARI, ARI->getType().getASTType()); auto Types = ARI->getTailAllocatedTypes(); auto Counts = ARI->getTailAllocatedCounts(); unsigned NumTypes = Types.size(); require(NumTypes == Counts.size(), "Mismatching types and counts"); require(NumTypes == 0 || !ARI->isObjC(), "Can't tail allocate with ObjC class"); for (unsigned Idx = 0; Idx < NumTypes; ++Idx) { verifyOpenedArchetype(ARI, Types[Idx].getASTType()); require(Counts[Idx].get()->getType().is(), "count needs integer type"); } } void checkAllocRefInst(AllocRefInst *AI) { require(AI->isObjC() || AI->getType().getClassOrBoundGenericClass(), "alloc_ref must allocate class"); checkAllocRefBase(AI); } void checkAllocRefDynamicInst(AllocRefDynamicInst *ARDI) { SILValue Metadata = ARDI->getMetatypeOperand(); require(Metadata->getType().is(), "operand of alloc_ref_dynamic must be of metatype type"); auto metaTy = Metadata->getType().castTo(); require(metaTy->hasRepresentation(), "operand of alloc_ref_dynamic must have a metatype representation"); if (ARDI->isObjC()) { require(metaTy->getRepresentation() == MetatypeRepresentation::ObjC, "alloc_ref_dynamic @objc requires operand of ObjC metatype"); } else { require(metaTy->getRepresentation() == MetatypeRepresentation::Thick, "alloc_ref_dynamic requires operand of thick metatype"); } checkAllocRefBase(ARDI); } /// Check the substitutions passed to an apply or partial_apply. CanSILFunctionType checkApplySubstitutions(SubstitutionMap subs, SILType calleeTy) { auto fnTy = requireObjectType(SILFunctionType, calleeTy, "callee operand"); // If there are substitutions, verify them and apply them to the callee. if (!subs.hasAnySubstitutableParams()) { require(!fnTy->isPolymorphic(), "callee of apply without substitutions must not be polymorphic"); return fnTy; } require(fnTy->isPolymorphic(), "callee of apply with substitutions must be polymorphic"); // Each archetype occurring in the substitutions list should belong to the // current function. for (auto replacementType : subs.getReplacementTypes()) { replacementType->getCanonicalType().visit([&](CanType t) { auto A = dyn_cast(t); if (!A) return; require(isArchetypeValidInFunction(A, &F), "Replacement type of a substitution contains an ArchetypeType " "that does not exist in the Caller's generic param list."); }); } if (subs.getGenericSignature()->getCanonicalSignature() != fnTy->getInvocationGenericSignature()->getCanonicalSignature()) { llvm::dbgs() << "substitution map's generic signature: "; subs.getGenericSignature()->print(llvm::dbgs()); llvm::dbgs() << "\n"; llvm::dbgs() << "callee's generic signature: "; fnTy->getInvocationGenericSignature()->print(llvm::dbgs()); llvm::dbgs() << "\n"; require(false, "Substitution map does not match callee in apply instruction"); } // Apply the substitutions. return fnTy->substGenericArgs(F.getModule(), subs, F.getTypeExpansionContext()); } /// Check that for each opened archetype or dynamic self type in substitutions /// or the calle type, there is a type dependent operand. void checkApplyTypeDependentArguments(ApplySite AS) { SILInstruction *AI = AS.getInstruction(); llvm::DenseSet FoundOpenedArchetypes; unsigned hasDynamicSelf = 0; // Function to collect opened archetypes in FoundOpenedArchetypes and set // hasDynamicSelf. auto HandleType = [&](CanType Ty) { if (Ty->isOpenedExistential()) { auto A = cast(Ty); require(isArchetypeValidInFunction(A, AI->getFunction()), "Archetype to be substituted must be valid in function."); // Collect all opened archetypes used in the substitutions list. FoundOpenedArchetypes.insert(A); // Also check that they are properly tracked inside the current // function. auto Def = OpenedArchetypes.getOpenedArchetypeDef(A); require(Def, "Opened archetype should be registered in SILFunction"); require(Def == AI || properlyDominates(cast(Def), AI), "Use of an opened archetype should be dominated by a " "definition of this opened archetype"); } if (Ty->hasDynamicSelfType()) { hasDynamicSelf = 1; } }; // Search for opened archetypes and dynamic self. for (auto Replacement : AS.getSubstitutionMap().getReplacementTypes()) { Replacement->getCanonicalType().visit(HandleType); } AS.getSubstCalleeType().visit(HandleType); require(FoundOpenedArchetypes.size() + hasDynamicSelf == AI->getTypeDependentOperands().size(), "Number of opened archetypes and dynamic self in the substitutions " "list should match the number of type dependent operands"); for (auto &Op : AI->getTypeDependentOperands()) { auto V = Op.get(); if (isa(V)) { require(hasDynamicSelf, "dynamic self operand without dynamic self type"); require(AI->getFunction()->hasSelfMetadataParam(), "self metadata operand in function without self metadata param"); require((ValueBase *)V == AI->getFunction()->getSelfMetadataArgument(), "wrong self metadata operand"); } else { require(isa(V), "opened archetype operand should refer to a SIL instruction"); auto Archetype = getOpenedArchetypeOf(cast(V)); require(Archetype, "opened archetype operand should define an opened archetype"); require(FoundOpenedArchetypes.count(Archetype), "opened archetype operand does not correspond to any opened " "archetype from the substitutions list"); } } } void checkFullApplySite(FullApplySite site) { checkApplyTypeDependentArguments(site); // Then make sure that we have a type that can be substituted for the // callee. auto substTy = checkApplySubstitutions(site.getSubstitutionMap(), site.getCallee()->getType()); require(site.getOrigCalleeType()->getRepresentation() == site.getSubstCalleeType()->getRepresentation(), "calling convention difference between types"); require(!site.getSubstCalleeType()->isPolymorphic(), "substituted callee type should not be generic"); requireSameType(SILType::getPrimitiveObjectType(substTy), SILType::getPrimitiveObjectType(site.getSubstCalleeType()), "substituted callee type does not match substitutions"); // Check that the arguments and result match. SILFunctionConventions substConv(substTy, F.getModule()); //require(site.getArguments().size() == substTy->getNumSILArguments(), require(site.getNumArguments() == substConv.getNumSILArguments(), "apply doesn't have right number of arguments for function"); for (size_t i = 0, size = site.getNumArguments(); i < size; ++i) { requireSameType(site.getArguments()[i]->getType(), substConv.getSILArgumentType(i), "operand of 'apply' doesn't match function input type"); } } void checkApplyInst(ApplyInst *AI) { checkFullApplySite(AI); SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule()); requireSameType( AI->getType(), calleeConv.getSILResultType(), "type of apply instruction doesn't match function result type"); if (AI->isNonThrowing()) { require(calleeConv.funcTy->hasErrorResult(), "nothrow flag used for callee without error result"); } else { require(!calleeConv.funcTy->hasErrorResult(), "apply instruction cannot call function with error result"); } require(!calleeConv.funcTy->isCoroutine(), "cannot call coroutine with normal apply"); // Check that if the apply is of a noreturn callee, make sure that an // unreachable is the next instruction. if (AI->getModule().getStage() == SILStage::Raw || !AI->isCalleeNoReturn()) return; require(isa(std::next(SILBasicBlock::iterator(AI))), "No return apply without an unreachable as a next instruction."); } void checkTryApplyInst(TryApplyInst *AI) { checkFullApplySite(AI); SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule()); require(!calleeConv.funcTy->isCoroutine(), "cannot call coroutine with normal apply"); auto normalBB = AI->getNormalBB(); require(normalBB->args_size() == 1, "normal destination of try_apply must take one argument"); requireSameType((*normalBB->args_begin())->getType(), calleeConv.getSILResultType(), "normal destination of try_apply must take argument " "of normal result type"); auto errorBB = AI->getErrorBB(); require(calleeConv.funcTy->hasErrorResult(), "try_apply must call function with error result"); require(errorBB->args_size() == 1, "error destination of try_apply must take one argument"); requireSameType((*errorBB->args_begin())->getType(), calleeConv.getSILErrorType(), "error destination of try_apply must take argument " "of error result type"); } void checkBeginApplyInst(BeginApplyInst *AI) { checkFullApplySite(AI); SILFunctionConventions calleeConv(AI->getSubstCalleeType(), F.getModule()); auto yieldResults = AI->getYieldedValues(); auto yields = calleeConv.getYields(); require(yields.size() == yieldResults.size(), "length mismatch in callee yields vs. begin_apply results"); for (auto i : indices(yields)) { requireSameType( yieldResults[i]->getType(), calleeConv.getSILType(yields[i]), "callee yield type does not match begin_apply result type"); } if (AI->isNonThrowing()) { require(calleeConv.funcTy->hasErrorResult(), "nothrow flag used for callee without error result"); } else { require(!calleeConv.funcTy->hasErrorResult(), "begin_apply instruction cannot call function with error result"); } require(calleeConv.funcTy->getCoroutineKind() == SILCoroutineKind::YieldOnce, "must call yield_once coroutine with begin_apply"); } void checkAbortApplyInst(AbortApplyInst *AI) { require(isa(AI->getOperand()) && cast(AI->getOperand())->isTokenResult(), "operand of abort_apply must be a begin_apply"); } void checkEndApplyInst(EndApplyInst *AI) { require(isa(AI->getOperand()) && cast(AI->getOperand())->isTokenResult(), "operand of end_apply must be a begin_apply"); } void verifyLLVMIntrinsic(BuiltinInst *BI, llvm::Intrinsic::ID ID) { // Certain llvm intrinsic require constant values as their operands. // Consequently, these must not be phi nodes (aka. basic block arguments). switch (ID) { default: break; case llvm::Intrinsic::ctlz: // llvm.ctlz case llvm::Intrinsic::cttz: // llvm.cttz break; case llvm::Intrinsic::memcpy: case llvm::Intrinsic::memmove: case llvm::Intrinsic::memset: require(!isa(BI->getArguments()[3]), "isvolatile argument of memory intrinsics must be an integer " "literal"); break; case llvm::Intrinsic::lifetime_start: case llvm::Intrinsic::lifetime_end: case llvm::Intrinsic::invariant_start: require(!isa(BI->getArguments()[0]), "size argument of memory use markers must be an integer literal"); break; case llvm::Intrinsic::invariant_end: require(!isa(BI->getArguments()[1]), "llvm.invariant.end parameter #2 must be an integer literal"); break; } } void checkPartialApplyInst(PartialApplyInst *PAI) { auto resultInfo = requireObjectType(SILFunctionType, PAI, "result of partial_apply"); verifySILFunctionType(resultInfo); require(resultInfo->getExtInfo().hasContext(), "result of closure cannot have a thin function type"); checkApplyTypeDependentArguments(PAI); auto substTy = checkApplySubstitutions(PAI->getSubstitutionMap(), PAI->getCallee()->getType()); require(!PAI->getSubstCalleeType()->isPolymorphic(), "substituted callee type should not be generic"); requireSameType(SILType::getPrimitiveObjectType(substTy), SILType::getPrimitiveObjectType(PAI->getSubstCalleeType()), "substituted callee type does not match substitutions"); // The arguments must match the suffix of the original function's input // types. require(PAI->getArguments().size() + resultInfo->getParameters().size() == substTy->getParameters().size(), "result of partial_apply should take as many inputs as were not " "applied by the instruction"); SILFunctionConventions substConv(substTy, F.getModule()); unsigned appliedArgStartIdx = substConv.getNumSILArguments() - PAI->getNumArguments(); for (unsigned i = 0, size = PAI->getArguments().size(); i < size; ++i) { requireSameType( PAI->getArguments()[i]->getType(), substConv.getSILArgumentType(appliedArgStartIdx + i), "applied argument types do not match suffix of function type's " "inputs"); } // The arguments to the result function type must match the prefix of the // original function's input types. for (unsigned i = 0, size = resultInfo->getParameters().size(); i < size; ++i) { require(resultInfo->getParameters()[i] == substTy->getParameters()[i], "inputs to result function type do not match unapplied inputs " "of original function"); } require(resultInfo->getNumResults() == substTy->getNumResults(), "applied results do not agree in count with function type"); for (unsigned i = 0, size = resultInfo->getNumResults(); i < size; ++i) { auto originalResult = resultInfo->getResults()[i]; auto expectedResult = substTy->getResults()[i]; // The "returns inner pointer" convention doesn't survive through a // partial application, since the thunk takes responsibility for // lifetime-extending 'self'. if (expectedResult.getConvention() == ResultConvention::UnownedInnerPointer) { expectedResult = SILResultInfo( expectedResult.getReturnValueType(F.getModule(), substTy), ResultConvention::Unowned); require(originalResult == expectedResult, "result type of result function type for partially applied " "@unowned_inner_pointer function should have @unowned" "convention"); // The "autoreleased" convention doesn't survive through a // partial application, since the thunk takes responsibility for // retaining the return value. } else if (expectedResult.getConvention() == ResultConvention::Autoreleased) { expectedResult = SILResultInfo( expectedResult.getReturnValueType(F.getModule(), substTy), ResultConvention::Owned); require(originalResult == expectedResult, "result type of result function type for partially applied " "@autoreleased function should have @owned convention"); } else { require(originalResult == expectedResult, "result type of result function type does not match original " "function"); } } // TODO: Impose additional constraints when partial_apply when the // -disable-sil-partial-apply flag is enabled. We want to reduce // partial_apply to being only a means of associating a closure invocation // function with its context. // // When we reach that point, we should be able to more deeply redesign // PartialApplyInst to simplify the representation to carry a single // argument. if (PAI->getModule().getOptions().DisableSILPartialApply) { // Should only be one context argument. require(PAI->getArguments().size() == 1, "partial_apply should have a single context argument"); // Callee should already have the thin convention, and result should be // thick. require(resultInfo->getRepresentation() == SILFunctionTypeRepresentation::Thick, "partial_apply result should have thick convention"); require(PAI->getCallee()->getType().castTo() ->getRepresentation() == SILFunctionTypeRepresentation::Thin, "partial_apply callee should have thin convention"); // TODO: Check that generic signature matches box's generic signature, // once we have boxes with generic signatures. require(!PAI->getCalleeFunction()->getGenericEnvironment(), "partial_apply context must capture generic environment for " "callee"); // Result's callee convention should match context argument's convention. require(substTy->getParameters().back().getConvention() == resultInfo->getCalleeConvention(), "partial_apply context argument must have the same convention " "as the resulting function's callee convention"); auto isSwiftRefcounted = [](SILType t) -> bool { if (t.is()) return true; if (t.getASTType() == t.getASTContext().TheNativeObjectType) return true; if (auto clas = t.getClassOrBoundGenericClass()) // Must be a class defined in Swift. return clas->hasKnownSwiftImplementation(); return false; }; // The context argument must be a swift-refcounted box or class. require(isSwiftRefcounted(PAI->getArguments().front()->getType()), "partial_apply context argument must be swift-refcounted"); } } void checkBuiltinInst(BuiltinInst *BI) { // Check for special constraints on llvm intrinsics. if (BI->getIntrinsicInfo().ID != llvm::Intrinsic::not_intrinsic) { verifyLLVMIntrinsic(BI, BI->getIntrinsicInfo().ID); return; } } void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) { auto fnType = requireObjectType(SILFunctionType, FRI, "result of function_ref"); require(!fnType->getExtInfo().hasContext(), "function_ref should have a context-free function result"); // Note: in SingleFunction mode, we relax some of these checks because // we may not have linked everything yet. SILFunction *RefF = FRI->getInitiallyReferencedFunction(); if (isa(FRI)) require( !RefF->isDynamicallyReplaceable(), "function_ref cannot reference a [dynamically_replaceable] function"); else if (isa(FRI)) { require(!RefF->isDynamicallyReplaceable(), "previous_function_ref cannot reference a " "[dynamically_replaceable] function"); require(RefF->getDynamicallyReplacedFunction(), "previous_function_ref must reference a " "[dynamic_replacement_for:...] function"); } else if (isa(FRI)) require(RefF->isDynamicallyReplaceable(), "dynamic_function_ref must reference a " "[dynamically_replaceable] function"); // In canonical SIL, direct reference to a shared_external declaration // is an error; we should have deserialized a body. In raw SIL, we may // not have deserialized the body yet. if (F.getModule().getStage() >= SILStage::Canonical) { if (RefF->isExternalDeclaration()) { require(SingleFunction || !hasSharedVisibility(RefF->getLinkage()) || RefF->hasForeignBody(), "external declarations of SILFunctions with shared visibility is " "not allowed"); } } // A direct reference to a non-public or shared but not fragile function // from a fragile function is an error. if (F.isSerialized()) { require((SingleFunction && RefF->isExternalDeclaration()) || RefF->hasValidLinkageForFragileRef(), "function_ref inside fragile function cannot " "reference a private or hidden symbol"); } verifySILFunctionType(fnType); } void checkFunctionRefInst(FunctionRefInst *FRI) { checkFunctionRefBaseInst(FRI); } void checkDynamicFunctionRefInst(DynamicFunctionRefInst *FRI) { checkFunctionRefBaseInst(FRI); } void checkPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI) { checkFunctionRefBaseInst(FRI); } 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()) { require(RefG->isSerialized() || hasPublicVisibility(RefG->getLinkage()), "alloc_global inside fragile function cannot " "reference a private or hidden symbol"); } } void checkGlobalAccessInst(GlobalAccessInst *GAI) { SILGlobalVariable *RefG = GAI->getReferencedGlobal(); requireSameType( GAI->getType().getObjectType(), RefG->getLoweredTypeInContext(F.getTypeExpansionContext()), "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()), "global_addr/value inside fragile function cannot " "reference a private or hidden symbol"); } } void checkGlobalAddrInst(GlobalAddrInst *GAI) { require(GAI->getType().isAddress(), "global_addr must have an address result type"); require(!GAI->getReferencedGlobal()->isInitializedObject(), "global_addr cannot refer to a statically initialized object"); checkGlobalAccessInst(GAI); } void checkGlobalValueInst(GlobalValueInst *GVI) { require(GVI->getType().isObject(), "global_value must have an address result type"); checkGlobalAccessInst(GVI); } void checkObjectInst(ObjectInst *) { require(false, "object instruction is only allowed in a static initializer"); } void checkIntegerLiteralInst(IntegerLiteralInst *ILI) { require(ILI->getType().is(), "invalid integer literal type"); } void checkLoadInst(LoadInst *LI) { require(LI->getType().isObject(), "Result of load must be an object"); require(!fnConv.useLoweredAddresses() || LI->getType().isLoadable(*LI->getFunction()), "Load must have a loadable type"); require(LI->getOperand()->getType().isAddress(), "Load operand must be an address"); requireSameType(LI->getOperand()->getType().getObjectType(), LI->getType(), "Load operand type and result type mismatch"); // Ownership semantic checks. switch (LI->getOwnershipQualifier()) { case LoadOwnershipQualifier::Unqualified: // We should not see loads with unqualified ownership when SILOwnership is // enabled. require(!F.hasOwnership(), "Load with unqualified ownership in a qualified function"); break; case LoadOwnershipQualifier::Copy: case LoadOwnershipQualifier::Take: require(F.hasOwnership(), "Load with qualified ownership in an unqualified function"); // TODO: Could probably make this a bit stricter. require(!LI->getType().isTrivial(*LI->getFunction()), "load [copy] or load [take] can only be applied to non-trivial " "types"); break; case LoadOwnershipQualifier::Trivial: require(F.hasOwnership(), "Load with qualified ownership in an unqualified function"); require(LI->getType().isTrivial(*LI->getFunction()), "A load with trivial ownership must load a trivial type"); break; } } void checkLoadBorrowInst(LoadBorrowInst *LBI) { require( F.hasOwnership(), "Inst with qualified ownership in a function that is not qualified"); require(LBI->getType().isObject(), "Result of load must be an object"); require(!fnConv.useLoweredAddresses() || LBI->getType().isLoadable(*LBI->getFunction()), "Load must have a loadable type"); require(LBI->getOperand()->getType().isAddress(), "Load operand must be an address"); requireSameType(LBI->getOperand()->getType().getObjectType(), LBI->getType(), "Load operand type and result type mismatch"); } void checkEndBorrowInst(EndBorrowInst *EBI) { require( F.hasOwnership(), "Inst with qualified ownership in a function that is not qualified"); } template void checkAccessEnforcement(AI *AccessInst) { if (AccessInst->getModule().getStage() != SILStage::Raw) { require(AccessInst->getEnforcement() != SILAccessEnforcement::Unknown, "access must have known enforcement outside raw stage"); } } void checkBeginAccessInst(BeginAccessInst *BAI) { requireSameType(BAI->getType(), BAI->getSource()->getType(), "result must be same type as operand"); require(BAI->getType().isAddress(), "begin_access operand must have address type"); checkAccessEnforcement(BAI); switch (BAI->getAccessKind()) { case SILAccessKind::Init: case SILAccessKind::Deinit: require(BAI->getEnforcement() == SILAccessEnforcement::Static, "init/deinit accesses cannot use non-static enforcement"); break; case SILAccessKind::Read: case SILAccessKind::Modify: break; } // Verify that all formal accesses patterns are recognized as part of a // whitelist. The presence of an unknown pattern means that analysis will // silently fail, and the compiler may be introducing undefined behavior // with no other way to detect it. // // For example, AccessEnforcementWMO runs very late in the // pipeline and assumes valid storage for all dynamic Read/Modify access. It // also requires that Unidentified access fit a whitelist on known // non-internal globals or class properties. // // First check that findAccessedStorage returns without asserting. For // Unsafe enforcement, that is sufficient. For any other enforcement // level also require that it returns a valid AccessedStorage object. // Unsafe enforcement is used for some unrecognizable access patterns, // like debugger variables. The compiler never cares about the source of // those accesses. findAccessedStorage(BAI->getSource()); // FIXME: rdar://57291811 - the following check for valid storage will be // reenabled shortly. A fix is planned. In the meantime, the possiblity that // a real miscompilation could be caused by this failure is insignificant. // I will probably enable a much broader SILVerification of address-type // block arguments first to ensure we never hit this check again. /* AccessedStorage storage = findAccessedStorage(BAI->getSource()); if (BAI->getEnforcement() != SILAccessEnforcement::Unsafe) require(storage, "Unknown formal access pattern"); */ } void checkEndAccessInst(EndAccessInst *EAI) { auto BAI = dyn_cast(EAI->getOperand()); require(BAI != nullptr, "operand of end_access must be a begin_access"); if (EAI->isAborting()) { require(BAI->getAccessKind() == SILAccessKind::Init || BAI->getAccessKind() == SILAccessKind::Deinit, "aborting access must apply to init or deinit"); } } void checkBeginUnpairedAccessInst(BeginUnpairedAccessInst *BUAI) { require(BUAI->getEnforcement() != SILAccessEnforcement::Unknown, "unpaired access can never use unknown enforcement"); require(BUAI->getSource()->getType().isAddress(), "address operand must have address type"); requireAddressType(BuiltinUnsafeValueBufferType, BUAI->getBuffer(), "scratch buffer operand"); checkAccessEnforcement(BUAI); switch (BUAI->getAccessKind()) { case SILAccessKind::Init: case SILAccessKind::Deinit: require(BUAI->getEnforcement() == SILAccessEnforcement::Static, "init/deinit accesses cannot use non-static enforcement"); break; case SILAccessKind::Read: case SILAccessKind::Modify: break; } // First check that findAccessedStorage never asserts. AccessedStorage storage = findAccessedStorage(BUAI->getSource()); // Only allow Unsafe and Builtin access to have invalid storage. if (BUAI->getEnforcement() != SILAccessEnforcement::Unsafe && !BUAI->isFromBuiltin()) { require(storage, "Unknown formal access pattern"); } } void checkEndUnpairedAccessInst(EndUnpairedAccessInst *I) { require(I->getEnforcement() != SILAccessEnforcement::Unknown, "unpaired access can never use unknown enforcement"); requireAddressType(BuiltinUnsafeValueBufferType, I->getBuffer(), "scratch buffer operand"); checkAccessEnforcement(I); } void checkStoreInst(StoreInst *SI) { require(SI->getSrc()->getType().isObject(), "Can't store from an address source"); require(!fnConv.useLoweredAddresses() || SI->getSrc()->getType().isLoadable(*SI->getFunction()), "Can't store a non loadable type"); require(SI->getDest()->getType().isAddress(), "Must store to an address dest"); requireSameType(SI->getDest()->getType().getObjectType(), SI->getSrc()->getType(), "Store operand type and dest type mismatch"); // Perform ownership checks. switch (SI->getOwnershipQualifier()) { case StoreOwnershipQualifier::Unqualified: // We should not see loads with unqualified ownership when SILOwnership is // enabled. require(!F.hasOwnership(), "Qualified store in function with unqualified ownership?!"); break; case StoreOwnershipQualifier::Init: case StoreOwnershipQualifier::Assign: require( F.hasOwnership(), "Inst with qualified ownership in a function that is not qualified"); // TODO: Could probably make this a bit stricter. require(!SI->getSrc()->getType().isTrivial(*SI->getFunction()), "store [init] or store [assign] can only be applied to " "non-trivial types"); break; case StoreOwnershipQualifier::Trivial: { require( F.hasOwnership(), "Inst with qualified ownership in a function that is not qualified"); SILValue Src = SI->getSrc(); require(Src->getType().isTrivial(*SI->getFunction()) || Src.getOwnershipKind() == ValueOwnershipKind::None, "A store with trivial ownership must store a type with trivial " "ownership"); break; } } } void checkAssignInst(AssignInst *AI) { SILValue Src = AI->getSrc(), Dest = AI->getDest(); require(AI->getModule().getStage() == SILStage::Raw, "assign instruction can only exist in raw SIL"); require(Src->getType().isObject(), "Can't assign from an address source"); require(Dest->getType().isAddress(), "Must store to an address dest"); requireSameType(Dest->getType().getObjectType(), Src->getType(), "Store operand type and dest type mismatch"); } // Usually the assign_by_wrapper initializer or setter has a single argument // (the value). But in case of a tuple, the initializer/setter expect the // tuple elements as separate arguments. void checkAssignByWrapperArgsRecursively(SILType ty, SILFunctionConventions &conv, unsigned &argIdx) { if (auto tupleTy = ty.getAs()) { for (Type et : tupleTy->getElementTypes()) { SILType elTy = SILType::getPrimitiveType(CanType(et), ty.getCategory()); checkAssignByWrapperArgsRecursively(elTy, conv, argIdx); } return; } require(argIdx < conv.getNumSILArguments(), "initializer or setter has too few arguments"); SILType argTy = conv.getSILArgumentType(argIdx++); if (ty.isAddress() && argTy.isObject()) ty = ty.getObjectType(); requireSameType(ty, argTy, "wrong argument type of initializer or setter"); } void checkAssignByWrapperArgs(SILType ty, SILFunctionConventions &conv) { unsigned argIdx = conv.getSILArgIndexOfFirstParam(); checkAssignByWrapperArgsRecursively(ty, conv, argIdx); require(argIdx == conv.getNumSILArguments(), "initializer or setter has too many arguments"); } void checkAssignByWrapperInst(AssignByWrapperInst *AI) { SILValue Src = AI->getSrc(), Dest = AI->getDest(); require(AI->getModule().getStage() == SILStage::Raw, "assign instruction can only exist in raw SIL"); require(Dest->getType().isAddress(), "Must store to an address dest"); SILValue initFn = AI->getInitializer(); CanSILFunctionType initTy = initFn->getType().castTo(); SILFunctionConventions initConv(initTy, AI->getModule()); checkAssignByWrapperArgs(Src->getType(), initConv); switch (initConv.getNumIndirectSILResults()) { case 0: require(initConv.getNumDirectSILResults() == 1, "wrong number of init function results"); requireSameType(Dest->getType().getObjectType(), *initConv.getDirectSILResultTypes().begin(), "wrong init function result type"); break; case 1: require(initConv.getNumDirectSILResults() == 0, "wrong number of init function results"); requireSameType(Dest->getType(), *initConv.getIndirectSILResultTypes().begin(), "wrong indirect init function result type"); break; default: require(false, "wrong number of indirect init function results"); } SILValue setterFn = AI->getSetter(); CanSILFunctionType setterTy = setterFn->getType().castTo(); SILFunctionConventions setterConv(setterTy, AI->getModule()); require(setterConv.getNumIndirectSILResults() == 0, "set function has indirect results"); checkAssignByWrapperArgs(Src->getType(), setterConv); } #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ void checkLoad##Name##Inst(Load##Name##Inst *LWI) { \ require(LWI->getType().isObject(), "Result of load must be an object"); \ auto isOptional = bool(LWI->getType().getOptionalObjectType()); \ auto optionality = optionalityOf(ReferenceOwnership::Name); \ if (optionality == ReferenceOwnershipOptionality::Required) \ require(isOptional, "Optionality mismatch"); \ if (optionality == ReferenceOwnershipOptionality::Disallowed) \ require(!isOptional, "Optionality mismatch"); \ auto PointerType = LWI->getOperand()->getType(); \ auto PointerRVType = PointerType.getASTType(); \ require(PointerType.isAddress() && PointerRVType->is(), \ "load_" #name " operand must be a " #name " address"); \ requireSameType( \ PointerRVType->getReferenceStorageReferent()->getCanonicalType(), \ LWI->getType().getASTType(), \ "Load operand type and result type mismatch"); \ } \ void checkStore##Name##Inst(Store##Name##Inst *SWI) { \ auto SrcTy = SWI->getSrc()->getType(); \ require(SrcTy.isObject(), "Can't store from an address source"); \ auto isOptional = bool(SrcTy.getOptionalObjectType()); \ auto optionality = optionalityOf(ReferenceOwnership::Name); \ if (optionality == ReferenceOwnershipOptionality::Required) \ require(isOptional, "Optionality mismatch"); \ if (optionality == ReferenceOwnershipOptionality::Disallowed) \ require(!isOptional, "Optionality mismatch"); \ auto PointerType = SWI->getDest()->getType(); \ auto PointerRVType = PointerType.getASTType(); \ require(PointerType.isAddress() && PointerRVType->is(), \ "store_" #name " address operand must be a " #name " address"); \ requireSameType( \ PointerRVType->getReferenceStorageReferent()->getCanonicalType(), \ SrcTy.getASTType(), "Store operand type and dest type mismatch"); \ } #define LOADABLE_REF_STORAGE_HELPER(Name, name) \ void checkRefTo##Name##Inst(RefTo##Name##Inst *I) { \ requireReferenceStorageCapableValue(I->getOperand(), \ "Operand of ref_to_" #name); \ auto operandType = I->getOperand()->getType().getASTType(); \ auto resultType = \ requireObjectType(Name##StorageType, I, "Result of ref_to_" #name); \ requireSameType(resultType.getReferentType(), operandType, \ "Result of ref_to_" #name " does not have the " \ "operand's type as its referent type"); \ } \ void check##Name##ToRefInst(Name##ToRefInst *I) { \ auto operandType = requireObjectType(Name##StorageType, I->getOperand(), \ "Operand of " #name "_to_ref"); \ requireReferenceStorageCapableValue(I, "Result of " #name "_to_ref"); \ auto resultType = I->getType().getASTType(); \ requireSameType(operandType.getReferentType(), resultType, \ "Operand of " #name "_to_ref does not have the " \ "operand's type as its referent type"); \ } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, name, closure) \ void checkStrongRetain##Name##Inst(StrongRetain##Name##Inst *RI) { \ auto ty = requireObjectType(Name##StorageType, RI->getOperand(), \ "Operand of strong_retain_" #name); \ closure(); \ (void)ty; \ require(!F.hasOwnership(), "strong_retain_" #name " is only in " \ "functions with unqualified " \ "ownership"); \ } \ void check##Name##RetainInst(Name##RetainInst *RI) { \ auto ty = requireObjectType(Name##StorageType, RI->getOperand(), \ "Operand of " #name "_retain"); \ closure(); \ (void)ty; \ require(!F.hasOwnership(), \ #name "_retain is only in functions with unqualified ownership"); \ } \ void check##Name##ReleaseInst(Name##ReleaseInst *RI) { \ auto ty = requireObjectType(Name##StorageType, RI->getOperand(), \ "Operand of " #name "_release"); \ closure(); \ (void)ty; \ require(!F.hasOwnership(), \ #name "_release is only in functions with unqualified ownership"); \ } \ void StrongcheckCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \ auto ty = requireObjectType(Name##StorageType, I->getOperand(), \ "Operand of " #name "_retain"); \ closure(); \ (void)ty; \ /* *NOTE* We allow copy_##name##_value to be used throughout the entire */ \ /* pipeline even though it is a higher level instruction. */ \ } #define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ LOADABLE_REF_STORAGE_HELPER(Name, name) \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, name, []{}) #define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ LOADABLE_REF_STORAGE_HELPER(Name, name) \ NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, "...") \ ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, name, [&]{ \ require(ty->isLoadable(ResilienceExpansion::Maximal), \ "'" #name "' type must be loadable"); \ }) #define UNCHECKED_REF_STORAGE(Name, name, ...) \ LOADABLE_REF_STORAGE_HELPER(Name, name) \ void checkStrongCopy##Name##ValueInst(StrongCopy##Name##ValueInst *I) { \ auto ty = requireObjectType(Name##StorageType, I->getOperand(), \ "Operand of " #name "_retain"); \ (void)ty; \ /* *NOTE* We allow copy_##name##_value to be used throughout the entire */ \ /* pipeline even though it is a higher level instruction. */ \ } #include "swift/AST/ReferenceStorage.def" #undef LOADABLE_REF_STORAGE_HELPER #undef ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER void checkMarkUninitializedInst(MarkUninitializedInst *MU) { SILValue Src = MU->getOperand(); require(MU->getModule().getStage() == SILStage::Raw, "mark_uninitialized instruction can only exist in raw SIL"); require(Src->getType().isAddress() || Src->getType().getClassOrBoundGenericClass() || Src->getType().getAs(), "mark_uninitialized must be an address, class, or box type"); requireSameType(Src->getType(), MU->getType(), "operand and result type mismatch"); // FIXME: When the work to force MUI to be on Allocations/SILArguments // complete, turn on this assertion. require(isa(Src) || isa(Src) // TODO: Should we support SILUndef on mark_uninitialized? We // currently have a test that verifies this behavior, but it seems // like this would always be a bug due to the implications around // the code in DI. This just bakes in the current behavior. || isa(Src) // We allow SILArguments to support the case of initializing // initializers. In such a case, the underlying case is allocated // outside by the allocating initializer and we pass in the to be // initialized value as a SILArgument. || isa(Src) // FIXME: We only support pointer to address here to not break LLDB. It is // important that long term we get rid of this since this is a situation // where LLDB is breaking SILGen/DI invariants by not creating a new // independent stack location for the pointer to address. || isa(Src), "Mark Uninitialized must be applied to a storage location"); } void checkMarkFunctionEscapeInst(MarkFunctionEscapeInst *MFE) { require(MFE->getModule().getStage() == SILStage::Raw, "mark_function_escape instruction can only exist in raw SIL"); for (auto Elt : MFE->getElements()) require(Elt->getType().isAddress(), "MFE must refer to variable addrs"); } void checkCopyAddrInst(CopyAddrInst *SI) { require(SI->getSrc()->getType().isAddress(), "Src value should be lvalue"); require(SI->getDest()->getType().isAddress(), "Dest address should be lvalue"); requireSameType(SI->getDest()->getType(), SI->getSrc()->getType(), "Store operand type and dest type mismatch"); require(F.isTypeABIAccessible(SI->getDest()->getType()), "cannot directly copy type with inaccessible ABI"); } void checkRetainValueInst(RetainValueInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); require(!F.hasOwnership(), "retain_value is only in functions with unqualified ownership"); } void checkRetainValueAddrInst(RetainValueAddrInst *I) { require(I->getOperand()->getType().isAddress(), "Source value should be an address value"); require(!F.hasOwnership(), "retain_value is only in functions with unqualified ownership"); } void checkCopyValueInst(CopyValueInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); require(!I->getOperand()->getType().isTrivial(*I->getFunction()), "Source value should be non-trivial"); require(!fnConv.useLoweredAddresses() || F.hasOwnership(), "copy_value is only valid in functions with qualified " "ownership"); } void checkDestroyValueInst(DestroyValueInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); require(!I->getOperand()->getType().isTrivial(*I->getFunction()), "Source value should be non-trivial"); require(!fnConv.useLoweredAddresses() || F.hasOwnership(), "destroy_value is only valid in functions with qualified " "ownership"); } void checkReleaseValueInst(ReleaseValueInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); require(!F.hasOwnership(), "release_value is only in functions with unqualified ownership"); } void checkReleaseValueAddrInst(ReleaseValueAddrInst *I) { require(I->getOperand()->getType().isAddress(), "Source value should be an address value"); require(!F.hasOwnership(), "release_value is only in functions with unqualified ownership"); } void checkAutoreleaseValueInst(AutoreleaseValueInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); // TODO: This instruction could in principle be generalized. require(I->getOperand()->getType().hasRetainablePointerRepresentation(), "Source value must be a reference type or optional thereof"); } void checkSetDeallocatingInst(SetDeallocatingInst *I) { require(I->getOperand()->getType().isObject(), "Source value should be an object value"); require(I->getOperand()->getType().hasRetainablePointerRepresentation(), "Source value must be a reference type"); } void checkCopyBlockInst(CopyBlockInst *I) { require(I->getOperand()->getType().isBlockPointerCompatible(), "operand of copy_block should be a block"); requireSameType(I->getOperand()->getType(), I->getType(), "result of copy_block should be same type as operand"); } void checkCopyBlockInst(CopyBlockWithoutEscapingInst *I) { require(I->getBlock()->getType().isBlockPointerCompatible(), "operand of copy_block should be a block"); requireSameType(I->getBlock()->getType(), I->getType(), "result of copy_block should be same type as operand"); auto FnTy = requireObjectType(SILFunctionType, I->getClosure(), "copy_block_without_escaping operand"); require(!FnTy->isNoEscape(), "closure parameter must not be a @noescape closure"); } void checkAllocValueBufferInst(AllocValueBufferInst *I) { require(I->getOperand()->getType().isAddress(), "Operand value should be an address"); require(I->getOperand()->getType().is(), "Operand value should be a Builtin.UnsafeValueBuffer"); verifyOpenedArchetype(I, I->getValueType().getASTType()); } void checkProjectValueBufferInst(ProjectValueBufferInst *I) { require(I->getOperand()->getType().isAddress(), "Operand value should be an address"); require(I->getOperand()->getType().is(), "Operand value should be a Builtin.UnsafeValueBuffer"); } void checkProjectBoxInst(ProjectBoxInst *I) { require(I->getOperand()->getType().isObject(), "project_box operand should be a value"); auto boxTy = I->getOperand()->getType().getAs(); require(boxTy, "project_box operand should be a @box type"); requireSameType(I->getType(), getSILBoxFieldType(F.getTypeExpansionContext(), boxTy, F.getModule().Types, I->getFieldIndex()), "project_box result should be address of boxed type"); // If we have a mark_uninitialized as a user, the mark_uninitialized must be // our only user. This is a requirement that is asserted by allocbox to // stack. This check just embeds the requirement into the IR. require(I->hasOneUse() || none_of(I->getUses(), [](Operand *Op) -> bool { return isa(Op->getUser()); }), "project_box with more than one user when a user is a " "mark_uninitialized"); } void checkProjectExistentialBoxInst(ProjectExistentialBoxInst *PEBI) { SILType operandType = PEBI->getOperand()->getType(); require(operandType.isObject(), "project_existential_box operand must not be address"); require(operandType.canUseExistentialRepresentation( ExistentialRepresentation::Boxed), "project_existential_box operand must be boxed existential"); require(PEBI->getType().isAddress(), "project_existential_box result must be an address"); if (auto *AEBI = dyn_cast(PEBI->getOperand())) { // The lowered type must be the properly-abstracted form of the AST type. SILType exType = AEBI->getExistentialType(); auto archetype = OpenedArchetypeType::get(exType.getASTType()); auto loweredTy = F.getLoweredType(Lowering::AbstractionPattern(archetype), AEBI->getFormalConcreteType()) .getAddressType(); requireSameType(loweredTy, PEBI->getType(), "project_existential_box result should be the lowered " "concrete type of its alloc_existential_box"); } } void checkDeallocValueBufferInst(DeallocValueBufferInst *I) { require(I->getOperand()->getType().isAddress(), "Operand value should be an address"); require(I->getOperand()->getType().is(), "Operand value should be a Builtin.UnsafeValueBuffer"); } void checkStructInst(StructInst *SI) { auto *structDecl = SI->getType().getStructOrBoundGenericStruct(); require(structDecl, "StructInst must return a struct"); 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"); SILType structTy = SI->getType(); auto opi = SI->getElements().begin(), opEnd = SI->getElements().end(); for (VarDecl *field : structDecl->getStoredProperties()) { require(opi != opEnd, "number of struct operands does not match number of stored " "member variables of struct"); SILType loweredType = structTy.getFieldType(field, F.getModule(), F.getTypeExpansionContext()); if (SI->getModule().getStage() != SILStage::Lowered) { requireSameType((*opi)->getType(), loweredType, "struct operand type does not match field type"); } ++opi; } } void checkEnumInst(EnumInst *UI) { EnumDecl *ud = UI->getType().getEnumOrBoundGenericEnum(); require(ud, "EnumInst must return an enum"); require(UI->getElement()->getParentEnum() == ud, "EnumInst case must be a case of the result enum type"); require(UI->getType().isObject(), "EnumInst must produce an object"); require(UI->hasOperand() == UI->getElement()->hasAssociatedValues(), "EnumInst must take an argument iff the element does"); if (UI->getElement()->hasAssociatedValues()) { require(UI->getOperand()->getType().isObject(), "EnumInst operand must be an object"); SILType caseTy = UI->getType().getEnumElementType( UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { requireSameType(caseTy, UI->getOperand()->getType(), "EnumInst operand type does not match type of case"); } } } void checkInitEnumDataAddrInst(InitEnumDataAddrInst *UI) { EnumDecl *ud = UI->getOperand()->getType().getEnumOrBoundGenericEnum(); require(ud, "InitEnumDataAddrInst must take an enum operand"); require(UI->getElement()->getParentEnum() == ud, "InitEnumDataAddrInst case must be a case of the enum operand type"); require(UI->getElement()->hasAssociatedValues(), "InitEnumDataAddrInst case must have a data type"); require(UI->getOperand()->getType().isAddress(), "InitEnumDataAddrInst must take an address operand"); require(UI->getType().isAddress(), "InitEnumDataAddrInst must produce an address"); SILType caseTy = UI->getOperand()->getType().getEnumElementType( UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { requireSameType( caseTy, UI->getType(), "InitEnumDataAddrInst result does not match type of enum case"); } } void checkUncheckedEnumDataInst(UncheckedEnumDataInst *UI) { EnumDecl *ud = UI->getOperand()->getType().getEnumOrBoundGenericEnum(); require(ud, "UncheckedEnumData must take an enum operand"); require(UI->getElement()->getParentEnum() == ud, "UncheckedEnumData case must be a case of the enum operand type"); require(UI->getElement()->getArgumentInterfaceType(), "UncheckedEnumData case must have a data type"); require(UI->getOperand()->getType().isObject(), "UncheckedEnumData must take an address operand"); require(UI->getType().isObject(), "UncheckedEnumData must produce an address"); SILType caseTy = UI->getOperand()->getType().getEnumElementType( UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { requireSameType( caseTy, UI->getType(), "UncheckedEnumData result does not match type of enum case"); } } void checkUncheckedTakeEnumDataAddrInst(UncheckedTakeEnumDataAddrInst *UI) { EnumDecl *ud = UI->getOperand()->getType().getEnumOrBoundGenericEnum(); require(ud, "UncheckedTakeEnumDataAddrInst must take an enum operand"); require(UI->getElement()->getParentEnum() == ud, "UncheckedTakeEnumDataAddrInst case must be a case of the enum operand type"); require(UI->getElement()->getArgumentInterfaceType(), "UncheckedTakeEnumDataAddrInst case must have a data type"); require(UI->getOperand()->getType().isAddress(), "UncheckedTakeEnumDataAddrInst must take an address operand"); require(UI->getType().isAddress(), "UncheckedTakeEnumDataAddrInst must produce an address"); SILType caseTy = UI->getOperand()->getType().getEnumElementType( UI->getElement(), F.getModule(), F.getTypeExpansionContext()); if (UI->getModule().getStage() != SILStage::Lowered) { requireSameType(caseTy, UI->getType(), "UncheckedTakeEnumDataAddrInst result " "does not match type of enum case"); } } void checkInjectEnumAddrInst(InjectEnumAddrInst *IUAI) { require(IUAI->getOperand()->getType().is() || IUAI->getOperand()->getType().is(), "InjectEnumAddrInst must take an enum operand"); require(IUAI->getElement()->getParentEnum() == IUAI->getOperand()->getType().getEnumOrBoundGenericEnum(), "InjectEnumAddrInst case must be a case of the enum operand type"); require(IUAI->getOperand()->getType().isAddress(), "InjectEnumAddrInst must take an address operand"); } void checkTupleInst(TupleInst *TI) { CanTupleType ResTy = requireObjectType(TupleType, TI, "Result of tuple"); require(TI->getElements().size() == ResTy->getNumElements(), "Tuple field count mismatch!"); if (TI->getModule().getStage() != SILStage::Lowered) { for (size_t i = 0, size = TI->getElements().size(); i < size; ++i) { requireSameType(TI->getElement(i)->getType().getASTType(), ResTy.getElementType(i), "Tuple element arguments do not match tuple type!"); } } } // Is a SIL type a potential lowering of a formal type? bool isLoweringOf(SILType loweredType, CanType formalType) { return loweredType.isLoweringOf(F.getTypeExpansionContext(), F.getModule(), formalType); } void checkMetatypeInst(MetatypeInst *MI) { require(MI->getType().is(), "metatype instruction must be of metatype type"); auto MetaTy = MI->getType().castTo(); require(MetaTy->hasRepresentation(), "metatype instruction must have a metatype representation"); verifyOpenedArchetype(MI, MetaTy.getInstanceType()); } void checkValueMetatypeInst(ValueMetatypeInst *MI) { require(MI->getType().is(), "value_metatype instruction must be of metatype type"); require(MI->getType().castTo()->hasRepresentation(), "value_metatype instruction must have a metatype representation"); auto formalInstanceTy = MI->getType().castTo().getInstanceType(); require(isLoweringOf(MI->getOperand()->getType(), formalInstanceTy), "value_metatype result must be formal metatype of " "lowered operand type"); } void checkExistentialMetatypeInst(ExistentialMetatypeInst *MI) { require(MI->getType().is(), "existential_metatype instruction must be of metatype type"); require(MI->getType().castTo()->hasRepresentation(), "value_metatype instruction must have a metatype representation"); require(MI->getOperand()->getType().isAnyExistentialType(), "existential_metatype operand must be of protocol type"); auto formalInstanceTy = MI->getType().castTo().getInstanceType(); require(isLoweringOf(MI->getOperand()->getType(), formalInstanceTy), "existential_metatype result must be formal metatype of " "lowered operand type"); } void checkStrongRetainInst(StrongRetainInst *RI) { requireReferenceValue(RI->getOperand(), "Operand of strong_retain"); require(!F.hasOwnership(), "strong_retain is only in functions with unqualified ownership"); } void checkStrongReleaseInst(StrongReleaseInst *RI) { requireReferenceValue(RI->getOperand(), "Operand of release"); require(!F.hasOwnership(), "strong_release is only in functions with unqualified ownership"); } void checkDeallocStackInst(DeallocStackInst *DI) { require(isa(DI->getOperand()) || isa(DI->getOperand()) || (isa(DI->getOperand()) && cast(DI->getOperand())->isOnStack()), "Operand of dealloc_stack must be an alloc_stack or partial_apply " "[stack]"); } void checkDeallocRefInst(DeallocRefInst *DI) { require(DI->getOperand()->getType().isObject(), "Operand of dealloc_ref must be object"); 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(), "First operand of dealloc_partial_ref must be object"); auto class1 = DPRI->getInstance()->getType().getClassOrBoundGenericClass(); require(class1, "First operand of dealloc_partial_ref must be of class type"); require(DPRI->getMetatype()->getType().is(), "Second operand of dealloc_partial_ref must be a metatype"); auto class2 = DPRI->getMetatype()->getType().castTo() ->getInstanceType()->getClassOrBoundGenericClass(); require(class2, "Second operand of dealloc_partial_ref must be a class metatype"); require(class2->isSuperclassOf(class1), "First operand not superclass of second instance type"); } void checkAllocBoxInst(AllocBoxInst *AI) { auto boxTy = AI->getType().getAs(); require(boxTy, "alloc_box must have a @box type"); require(AI->getType().isObject(), "result of alloc_box must be an object"); for (unsigned field : indices(AI->getBoxType()->getLayout()->getFields())) { verifyOpenedArchetype(AI, getSILBoxFieldLoweredType( F.getTypeExpansionContext(), AI->getBoxType(), F.getModule().Types, field)); } // An alloc_box with a mark_uninitialized user can not have any other users. require(none_of(AI->getUses(), [](Operand *Op) -> bool { return isa(Op->getUser()); }) || AI->hasOneUse(), "An alloc_box with a mark_uninitialized user can not have any " "other users."); } void checkDeallocBoxInst(DeallocBoxInst *DI) { auto boxTy = DI->getOperand()->getType().getAs(); require(boxTy, "operand must be a @box type"); require(DI->getOperand()->getType().isObject(), "operand must be an object"); } void checkDestroyAddrInst(DestroyAddrInst *DI) { require(DI->getOperand()->getType().isAddress(), "Operand of destroy_addr must be address"); require(F.isTypeABIAccessible(DI->getOperand()->getType()), "cannot directly destroy type with inaccessible ABI"); } void checkBindMemoryInst(BindMemoryInst *BI) { require(BI->getBoundType(), "BI must have a bound type"); require(BI->getBase()->getType().is(), "bind_memory base be a RawPointer"); requireSameType(BI->getIndex()->getType(), SILType::getBuiltinWordType(F.getASTContext()), "bind_memory index must be a Word"); } void checkIndexAddrInst(IndexAddrInst *IAI) { require(IAI->getType().isAddress(), "index_addr must produce an address"); requireSameType( IAI->getType(), IAI->getBase()->getType(), "index_addr must produce an address of the same type as its base"); require(IAI->getIndex()->getType().is(), "index_addr index must be of a builtin integer type"); } void checkTailAddrInst(TailAddrInst *IAI) { require(IAI->getType().isAddress(), "tail_addr must produce an address"); require(IAI->getIndex()->getType().is(), "tail_addr index must be of a builtin integer type"); } void checkIndexRawPointerInst(IndexRawPointerInst *IAI) { require(IAI->getType().is(), "index_raw_pointer must produce a RawPointer"); require(IAI->getBase()->getType().is(), "index_raw_pointer base must be a RawPointer"); require(IAI->getIndex()->getType().is(), "index_raw_pointer index must be of a builtin integer type"); } void checkTupleExtractInst(TupleExtractInst *EI) { CanTupleType operandTy = requireObjectType(TupleType, EI->getOperand(), "Operand of tuple_extract"); require(EI->getType().isObject(), "result of tuple_extract must be object"); require(EI->getFieldNo() < operandTy->getNumElements(), "invalid field index for tuple_extract instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { requireSameType(EI->getType().getASTType(), operandTy.getElementType(EI->getFieldNo()), "type of tuple_extract does not match type of element"); } } void checkStructExtractInst(StructExtractInst *EI) { SILType operandTy = EI->getOperand()->getType(); require(operandTy.isObject(), "cannot struct_extract from address"); require(EI->getType().isObject(), "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(), "cannot load computed property with struct_extract"); require(EI->getField()->getDeclContext() == sd, "struct_extract field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { SILType loweredFieldTy = operandTy.getFieldType( EI->getField(), F.getModule(), F.getTypeExpansionContext()); requireSameType(loweredFieldTy, EI->getType(), "result of struct_extract does not match type of field"); } } void checkTupleElementAddrInst(TupleElementAddrInst *EI) { SILType operandTy = EI->getOperand()->getType(); require(operandTy.isAddress(), "must derive element_addr from address"); require(EI->getType().isAddress(), "result of tuple_element_addr must be address"); require(operandTy.is(), "must derive tuple_element_addr from tuple"); ArrayRef fields = operandTy.castTo()->getElements(); require(EI->getFieldNo() < fields.size(), "invalid field index for element_addr instruction"); if (EI->getModule().getStage() != SILStage::Lowered) { requireSameType( EI->getType().getASTType(), CanType(fields[EI->getFieldNo()].getType()), "type of tuple_element_addr does not match type of element"); } } void checkStructElementAddrInst(StructElementAddrInst *EI) { SILType operandTy = EI->getOperand()->getType(); require(operandTy.isAddress(), "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(), "cannot get address of static property with struct_element_addr"); require(EI->getField()->hasStorage(), "cannot get address of computed property with struct_element_addr"); require(EI->getField()->getDeclContext() == sd, "struct_element_addr field is not a member of the struct"); if (EI->getModule().getStage() != SILStage::Lowered) { SILType loweredFieldTy = operandTy.getFieldType( EI->getField(), F.getModule(), F.getTypeExpansionContext()); requireSameType( loweredFieldTy, EI->getType(), "result of struct_element_addr does not match type of field"); } } void checkRefElementAddrInst(RefElementAddrInst *EI) { requireReferenceValue(EI->getOperand(), "Operand of ref_element_addr"); require(EI->getType().isAddress(), "result of ref_element_addr must be lvalue"); require(!EI->getField()->isStatic(), "cannot get address of static property with struct_element_addr"); require(EI->getField()->hasStorage(), "cannot get address of computed property with ref_element_addr"); 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"); if (EI->getModule().getStage() != SILStage::Lowered) { SILType loweredFieldTy = operandTy.getFieldType( EI->getField(), F.getModule(), F.getTypeExpansionContext()); requireSameType( loweredFieldTy, EI->getType(), "result of ref_element_addr does not match type of field"); } EI->getFieldNo(); // Make sure we can access the field without crashing. } void checkRefTailAddrInst(RefTailAddrInst *RTAI) { requireReferenceValue(RTAI->getOperand(), "Operand of ref_tail_addr"); require(RTAI->getType().isAddress(), "result of ref_tail_addr must be lvalue"); 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()); } void checkWitnessMethodInst(WitnessMethodInst *AMI) { auto methodType = requireObjectType(SILFunctionType, AMI, "result of witness_method"); auto *protocol = dyn_cast(AMI->getMember().getDecl()->getDeclContext()); require(protocol, "witness_method method must be a protocol method"); require(methodType->getRepresentation() == F.getModule().Types.getProtocolWitnessRepresentation(protocol), "result of witness_method must have correct representation for protocol"); require(methodType->isPolymorphic(), "result of witness_method must be polymorphic"); auto genericSig = methodType->getInvocationGenericSignature(); auto selfGenericParam = genericSig->getGenericParams()[0]; require(selfGenericParam->getDepth() == 0 && selfGenericParam->getIndex() == 0, "method should be polymorphic on Self parameter at depth 0 index 0"); Optional selfRequirement; for (auto req : genericSig->getRequirements()) { if (req.getKind() != RequirementKind::SameType) { selfRequirement = req; break; } } require(selfRequirement && selfRequirement->getKind() == RequirementKind::Conformance, "first non-same-typerequirement should be conformance requirement"); auto conformsTo = genericSig->getConformsTo(selfGenericParam); require(std::find(conformsTo.begin(), conformsTo.end(), protocol) != conformsTo.end(), "requirement Self parameter must conform to called protocol"); auto lookupType = AMI->getLookupType(); if (getOpenedArchetypeOf(lookupType)) { require(AMI->getTypeDependentOperands().size() == 1, "Must have a type dependent operand for the opened archetype"); verifyOpenedArchetype(AMI, lookupType); } else { require(AMI->getTypeDependentOperands().empty(), "Should not have an operand for the opened existential"); } if (!isa(lookupType)) { require(AMI->getConformance().isConcrete(), "concrete type lookup requires concrete conformance"); auto conformance = AMI->getConformance().getConcrete(); requireSameType( conformance->getType(), AMI->getLookupType(), "concrete type lookup requires conformance that matches type"); } require(AMI->getMember().requiresNewWitnessTableEntry(), "method does not have a witness table entry"); } // Get the expected type of a dynamic method reference. SILType getDynamicMethodType(SILType selfType, SILDeclRef method) { auto &C = F.getASTContext(); // The type of the dynamic method must match the usual type of the method, // but with the more opaque Self type. auto constantInfo = F.getModule().Types.getConstantInfo(F.getTypeExpansionContext(), method); auto methodTy = constantInfo.SILFnType; assert(!methodTy->isCoroutine()); // Map interface types to archetypes. if (auto *env = F.getModule().Types.getConstantGenericEnvironment(method)) { auto subs = env->getForwardingSubstitutionMap(); methodTy = methodTy->substGenericArgs(F.getModule(), subs, F.getTypeExpansionContext()); } assert(!methodTy->isPolymorphic()); // Replace Self parameter with type of 'self' at the call site. auto params = methodTy->getParameters(); SmallVector dynParams(params.begin(), params.end() - 1); dynParams.push_back(SILParameterInfo(selfType.getASTType(), params.back().getConvention())); auto results = methodTy->getResults(); SmallVector dynResults(results.begin(), results.end()); // If the method returns Self, substitute AnyObject for the result type. if (auto fnDecl = dyn_cast(method.getDecl())) { if (fnDecl->hasDynamicSelfResult()) { auto anyObjectTy = C.getAnyObjectType(); for (auto &dynResult : dynResults) { auto newResultTy = dynResult.getReturnValueType(F.getModule(), methodTy) ->replaceCovariantResultType(anyObjectTy, 0); dynResult = SILResultInfo(newResultTy->getCanonicalType(), dynResult.getConvention()); } } } auto fnTy = SILFunctionType::get(nullptr, methodTy->getExtInfo(), methodTy->getCoroutineKind(), methodTy->getCalleeConvention(), dynParams, methodTy->getYields(), dynResults, methodTy->getOptionalErrorResult(), SubstitutionMap(), SubstitutionMap(), F.getASTContext()); return SILType::getPrimitiveObjectType(fnTy); } /// Visitor class that checks whether a given decl ref has an entry in the /// class's vtable. class VerifyClassMethodVisitor : public SILVTableVisitor { public: SILDeclRef Method; bool Seen = false; VerifyClassMethodVisitor(SILDeclRef method) : Method(method.getOverriddenVTableEntry()) { auto *theClass = cast(Method.getDecl()->getDeclContext()); addVTableEntries(theClass); } void addMethod(SILDeclRef method) { if (Seen) return; if (method == Method) Seen = true; } void addMethodOverride(SILDeclRef base, SILDeclRef derived) {} void addPlaceholder(MissingMemberDecl *) {} }; void checkClassMethodInst(ClassMethodInst *CMI) { auto member = CMI->getMember(); auto overrideTy = TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (CMI->getModule().getStage() != SILStage::Lowered) { requireSameType( CMI->getType(), SILType::getPrimitiveObjectType(overrideTy), "result type of class_method must match abstracted type of method"); } auto methodType = requireObjectType(SILFunctionType, CMI, "result of class_method"); require(!methodType->getExtInfo().hasContext(), "result method must be of a context-free function type"); SILType operandType = CMI->getOperand()->getType(); require(operandType.isClassOrClassMetatype(), "operand must be of a class type"); require(getMethodSelfType(methodType).isClassOrClassMetatype(), "result must be a method of a class"); require(!member.isForeign, "foreign method cannot be dispatched natively"); require(!isa(member.getDecl()->getDeclContext()), "extension method cannot be dispatched natively"); // The method ought to appear in the class vtable. require(VerifyClassMethodVisitor(member).Seen, "method does not appear in the class's vtable"); } void checkSuperMethodInst(SuperMethodInst *CMI) { auto member = CMI->getMember(); auto overrideTy = TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (CMI->getModule().getStage() != SILStage::Lowered) { requireSameType( CMI->getType(), SILType::getPrimitiveObjectType(overrideTy), "result type of super_method must match abstracted type of method"); } auto methodType = requireObjectType(SILFunctionType, CMI, "result of super_method"); require(!methodType->getExtInfo().hasContext(), "result method must be of a context-free function type"); SILType operandType = CMI->getOperand()->getType(); require(operandType.isClassOrClassMetatype(), "operand must be of a class type"); require(getMethodSelfType(methodType).isClassOrClassMetatype(), "result must be a method of a class"); require(!member.isForeign, "foreign method cannot be dispatched natively"); require(!isa(member.getDecl()->getDeclContext()), "extension method cannot be dispatched natively"); auto decl = CMI->getMember().getDecl(); auto methodClass = decl->getDeclContext()->getDeclaredInterfaceType(); require(methodClass->getClassOrBoundGenericClass(), "super_method must look up a class method"); // The method ought to appear in the class vtable. require(VerifyClassMethodVisitor(member).Seen, "method does not appear in the class's vtable"); } void checkObjCMethodInst(ObjCMethodInst *OMI) { auto member = OMI->getMember(); require(member.isForeign, "native method cannot be dispatched via objc"); auto methodType = requireObjectType(SILFunctionType, OMI, "result of objc_method"); require(!methodType->getExtInfo().hasContext(), "result method must be of a context-free function type"); require(methodType->getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod, "wrong function type representation"); auto operandType = OMI->getOperand()->getType(); auto operandInstanceType = operandType.getASTType(); if (auto metatypeType = dyn_cast(operandInstanceType)) operandInstanceType = metatypeType.getInstanceType(); if (operandInstanceType.getClassOrBoundGenericClass()) { auto overrideTy = TC.getConstantOverrideType(F.getTypeExpansionContext(), member); requireSameType( OMI->getType(), SILType::getPrimitiveObjectType(overrideTy), "result type of objc_method must match abstracted type of method"); } else { require(isa(operandInstanceType) || operandInstanceType->isObjCExistentialType(), "operand type must be an archetype or self-conforming existential"); verifyOpenedArchetype(OMI, OMI->getType().getASTType()); } // TODO: We should enforce that ObjC methods are dispatched on ObjC // metatypes, but IRGen appears not to care right now. #if 0 if (auto metaTy = operandType.getAs()) { bool objcMetatype = metaTy->getRepresentation() == MetatypeRepresentation::ObjC; require(objcMetatype, "objc class methods must be invoked on objc metatypes"); } #endif } void checkObjCSuperMethodInst(ObjCSuperMethodInst *OMI) { auto member = OMI->getMember(); auto overrideTy = TC.getConstantOverrideType(F.getTypeExpansionContext(), member); if (OMI->getModule().getStage() != SILStage::Lowered) { requireSameType( OMI->getType(), SILType::getPrimitiveObjectType(overrideTy), "result type of super_method must match abstracted type of method"); } auto methodType = requireObjectType(SILFunctionType, OMI, "result of objc_super_method"); require(!methodType->getExtInfo().hasContext(), "result method must be of a context-free function type"); SILType operandType = OMI->getOperand()->getType(); require(operandType.isClassOrClassMetatype(), "operand must be of a class type"); require(getMethodSelfType(methodType).isClassOrClassMetatype(), "result must be a method of a class"); require(member.isForeign, "native method cannot be dispatched via objc"); auto decl = member.getDecl(); auto methodClass = decl->getDeclContext()->getDeclaredInterfaceType(); require(methodClass->getClassOrBoundGenericClass(), "objc_super_method must look up a class method"); } void checkOpenExistentialAddrInst(OpenExistentialAddrInst *OEI) { SILType operandType = OEI->getOperand()->getType(); require(operandType.isAddress(), "open_existential_addr must be applied to address"); require(operandType.canUseExistentialRepresentation( ExistentialRepresentation::Opaque), "open_existential_addr must be applied to opaque existential"); require(OEI->getType().isAddress(), "open_existential_addr result must be an address"); auto archetype = getOpenedArchetypeOf(OEI->getType().getASTType()); require(archetype, "open_existential_addr result must be an opened existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, "Archetype opened by open_existential_addr should be registered in " "SILFunction"); // Check all the uses. Consuming or mutating uses must have mutable access // to the opened value. auto allowedAccessKind = OEI->getAccessKind(); if (allowedAccessKind == OpenedExistentialAccess::Mutable) return; require(allowedAccessKind == OpenedExistentialAccess::Mutable || !ImmutableAddressUseVerifier().isMutatingOrConsuming(OEI), "open_existential_addr uses that consumes or mutates but is not " "opened for mutation"); } void checkOpenExistentialRefInst(OpenExistentialRefInst *OEI) { SILType operandType = OEI->getOperand()->getType(); require(operandType.isObject(), "open_existential_ref operand must not be address"); require(operandType.canUseExistentialRepresentation( ExistentialRepresentation::Class), "open_existential_ref operand must be class existential"); CanType resultInstanceTy = OEI->getType().getASTType(); require(OEI->getType().isObject(), "open_existential_ref result must be an address"); auto archetype = getOpenedArchetypeOf(resultInstanceTy); require(archetype, "open_existential_ref result must be an opened existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, "Archetype opened by open_existential_ref should be registered in " "SILFunction"); } void checkOpenExistentialBoxInst(OpenExistentialBoxInst *OEI) { SILType operandType = OEI->getOperand()->getType(); require(operandType.isObject(), "open_existential_box operand must not be address"); require(operandType.canUseExistentialRepresentation( ExistentialRepresentation::Boxed), "open_existential_box operand must be boxed existential"); CanType resultInstanceTy = OEI->getType().getASTType(); require(OEI->getType().isAddress(), "open_existential_box result must be an address"); auto archetype = getOpenedArchetypeOf(resultInstanceTy); require(archetype, "open_existential_box result must be an opened existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, "Archetype opened by open_existential_box should be registered in " "SILFunction"); } void checkOpenExistentialBoxValueInst(OpenExistentialBoxValueInst *OEI) { SILType operandType = OEI->getOperand()->getType(); require(operandType.isObject(), "open_existential_box operand must not be address"); require(operandType.canUseExistentialRepresentation( ExistentialRepresentation::Boxed), "open_existential_box operand must be boxed existential"); CanType resultInstanceTy = OEI->getType().getASTType(); require(!OEI->getType().isAddress(), "open_existential_box_value result must not be an address"); auto archetype = getOpenedArchetypeOf(resultInstanceTy); require(archetype, "open_existential_box_value result not an opened existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, "Archetype opened by open_existential_box_value should be " "registered in SILFunction"); } void checkOpenExistentialMetatypeInst(OpenExistentialMetatypeInst *I) { SILType operandType = I->getOperand()->getType(); require(operandType.isObject(), "open_existential_metatype operand must not be address"); require(operandType.is(), "open_existential_metatype operand must be existential metatype"); require(operandType.castTo()->hasRepresentation(), "open_existential_metatype operand must have a representation"); SILType resultType = I->getType(); require(resultType.isObject(), "open_existential_metatype result must not be address"); require(resultType.is(), "open_existential_metatype result must be metatype"); require(resultType.castTo()->hasRepresentation(), "open_existential_metatype result must have a representation"); require(operandType.castTo()->getRepresentation() == resultType.castTo()->getRepresentation(), "open_existential_metatype result must match representation of " "operand"); CanType operandInstTy = operandType.castTo().getInstanceType(); CanType resultInstTy = resultType.castTo().getInstanceType(); while (auto operandMetatype = dyn_cast(operandInstTy)) { require(isa(resultInstTy), "metatype depth mismatch in open_existential_metatype result"); operandInstTy = operandMetatype.getInstanceType(); resultInstTy = cast(resultInstTy).getInstanceType(); } require(operandInstTy.isExistentialType(), "ill-formed existential metatype in open_existential_metatype " "operand"); auto archetype = getOpenedArchetypeOf(resultInstTy); require(archetype, "open_existential_metatype result must be an opened " "existential metatype"); require( OpenedArchetypes.getOpenedArchetypeDef(archetype) == I, "Archetype opened by open_existential_metatype should be registered in " "SILFunction"); } void checkOpenExistentialValueInst(OpenExistentialValueInst *OEI) { SILType operandType = OEI->getOperand()->getType(); require(!operandType.isAddress(), "open_existential_value must not be applied to address"); require(operandType.canUseExistentialRepresentation( ExistentialRepresentation::Opaque), "open_existential_value must be applied to opaque existential"); require(!OEI->getType().isAddress(), "open_existential_value result must not be an address"); auto archetype = getOpenedArchetypeOf(OEI->getType().getASTType()); require(archetype, "open_existential_value result must be an opened " "existential archetype"); require(OpenedArchetypes.getOpenedArchetypeDef(archetype) == OEI, "Archetype opened by open_existential should be registered in " "SILFunction"); } void checkAllocExistentialBoxInst(AllocExistentialBoxInst *AEBI) { SILType exType = AEBI->getExistentialType(); require(exType.isObject(), "alloc_existential_box #0 result should be a value"); require(exType.canUseExistentialRepresentation( ExistentialRepresentation::Boxed, AEBI->getFormalConcreteType()), "alloc_existential_box must be used with a boxed existential " "type"); checkExistentialProtocolConformances(exType.getASTType(), AEBI->getFormalConcreteType(), AEBI->getConformances()); verifyOpenedArchetype(AEBI, AEBI->getFormalConcreteType()); } void checkInitExistentialAddrInst(InitExistentialAddrInst *AEI) { SILType exType = AEI->getOperand()->getType(); require(exType.isAddress(), "init_existential_addr must be applied to an address"); require(exType.canUseExistentialRepresentation( ExistentialRepresentation::Opaque, AEI->getFormalConcreteType()), "init_existential_addr must be used with an opaque " "existential type"); // The lowered type must be the properly-abstracted form of the AST type. auto archetype = OpenedArchetypeType::get(exType.getASTType()); auto loweredTy = F.getLoweredType(Lowering::AbstractionPattern(archetype), AEI->getFormalConcreteType()) .getAddressType(); requireSameType(loweredTy, AEI->getLoweredConcreteType(), "init_existential_addr result type must be the lowered " "concrete type at the right abstraction level"); require(isLoweringOf(AEI->getLoweredConcreteType(), AEI->getFormalConcreteType()), "init_existential_addr payload must be a lowering of the formal " "concrete type"); checkExistentialProtocolConformances(exType.getASTType(), AEI->getFormalConcreteType(), AEI->getConformances()); verifyOpenedArchetype(AEI, AEI->getFormalConcreteType()); } void checkInitExistentialValueInst(InitExistentialValueInst *IEI) { SILType concreteType = IEI->getOperand()->getType(); require(!concreteType.isAddress(), "init_existential_value must not be used on addresses"); require(!IEI->getType().isAddress(), "init_existential_value result must not be an address"); // The operand must be at the right abstraction level for the existential. SILType exType = IEI->getType(); auto archetype = OpenedArchetypeType::get(exType.getASTType()); auto loweredTy = F.getLoweredType(Lowering::AbstractionPattern(archetype), IEI->getFormalConcreteType()); requireSameType( concreteType, loweredTy, "init_existential_value operand must be lowered to the right " "abstraction level for the existential"); require(isLoweringOf(IEI->getOperand()->getType(), IEI->getFormalConcreteType()), "init_existential_value operand must be a lowering of the formal " "concrete type"); checkExistentialProtocolConformances(exType.getASTType(), IEI->getFormalConcreteType(), IEI->getConformances()); verifyOpenedArchetype(IEI, IEI->getFormalConcreteType()); } void checkInitExistentialRefInst(InitExistentialRefInst *IEI) { SILType concreteType = IEI->getOperand()->getType(); require(concreteType.getASTType()->isBridgeableObjectType(), "init_existential_ref operand must be a class instance"); require(IEI->getType().canUseExistentialRepresentation( ExistentialRepresentation::Class, IEI->getFormalConcreteType()), "init_existential_ref must be used with a class existential type"); require(IEI->getType().isObject(), "init_existential_ref result must not be an address"); // The operand must be at the right abstraction level for the existential. SILType exType = IEI->getType(); auto archetype = OpenedArchetypeType::get(exType.getASTType()); auto loweredTy = F.getLoweredType(Lowering::AbstractionPattern(archetype), IEI->getFormalConcreteType()); requireSameType(concreteType, loweredTy, "init_existential_ref operand must be lowered to the right " "abstraction level for the existential"); require(isLoweringOf(IEI->getOperand()->getType(), IEI->getFormalConcreteType()), "init_existential_ref operand must be a lowering of the formal " "concrete type"); checkExistentialProtocolConformances(exType.getASTType(), IEI->getFormalConcreteType(), IEI->getConformances()); verifyOpenedArchetype(IEI, IEI->getFormalConcreteType()); } void checkDeinitExistentialAddrInst(DeinitExistentialAddrInst *DEI) { SILType exType = DEI->getOperand()->getType(); require(exType.isAddress(), "deinit_existential_addr must be applied to an address"); require(exType.canUseExistentialRepresentation( ExistentialRepresentation::Opaque), "deinit_existential_addr must be applied to an opaque existential"); } void checkDeinitExistentialValueInst(DeinitExistentialValueInst *DEI) { SILType exType = DEI->getOperand()->getType(); require(!exType.isAddress(), "deinit_existential_value must not be applied to an address"); require( exType.canUseExistentialRepresentation( ExistentialRepresentation::Opaque), "deinit_existential_value must be applied to an opaque existential"); } void checkDeallocExistentialBoxInst(DeallocExistentialBoxInst *DEBI) { SILType exType = DEBI->getOperand()->getType(); require(exType.isObject(), "dealloc_existential_box must be applied to a value"); require(exType.canUseExistentialRepresentation( ExistentialRepresentation::Boxed), "dealloc_existential_box must be applied to a boxed " "existential"); } void checkInitExistentialMetatypeInst(InitExistentialMetatypeInst *I) { SILType operandType = I->getOperand()->getType(); require(operandType.isObject(), "init_existential_metatype operand must not be an address"); require(operandType.is(), "init_existential_metatype operand must be a metatype"); require(operandType.castTo()->hasRepresentation(), "init_existential_metatype operand must have a representation"); SILType resultType = I->getType(); require(resultType.is(), "init_existential_metatype result must be an existential metatype"); auto MetaTy = resultType.castTo(); require(resultType.isObject(), "init_existential_metatype result must not be an address"); require(MetaTy->hasRepresentation(), "init_existential_metatype result must have a representation"); require(MetaTy->getRepresentation() == operandType.castTo()->getRepresentation(), "init_existential_metatype result must match representation of " "operand"); auto resultInstanceType = resultType.getASTType(); auto operandInstanceType = operandType.getASTType(); while (isa(resultInstanceType)) { resultInstanceType = cast(resultInstanceType).getInstanceType(); operandInstanceType = cast(operandInstanceType).getInstanceType(); } checkExistentialProtocolConformances(resultInstanceType, operandInstanceType, I->getConformances()); verifyOpenedArchetype(I, MetaTy.getInstanceType()); } void checkExistentialProtocolConformances(CanType resultType, CanType concreteType, ArrayRef conformances) { auto layout = resultType.getExistentialLayout(); auto protocols = layout.getProtocols(); require(conformances.size() == protocols.size(), "init_existential instruction must have the " "right number of conformances"); if (layout.requiresClass()) { require(concreteType->mayHaveSuperclass() || (concreteType.isExistentialType() && concreteType.getExistentialLayout().requiresClass()), "init_existential of class existential with non-class type"); } if (auto superclass = layout.getSuperclass()) { require(superclass->isExactSuperclassOf(concreteType), "init_existential of subclass existential with wrong type"); } for (auto i : indices(conformances)) { require(conformances[i].getRequirement() == protocols[i]->getDecl(), "init_existential instruction must have conformances in " "proper order"); } } void verifyCheckedCast(bool isExact, SILType fromTy, SILType toTy, bool isOpaque = false) { // Verify common invariants. require(fromTy.isObject() && toTy.isObject(), "value checked cast src and dest must be objects"); auto fromCanTy = fromTy.getASTType(); auto toCanTy = toTy.getASTType(); require(isOpaque || canUseScalarCheckedCastInstructions(F.getModule(), fromCanTy, toCanTy), "invalid value checked cast src or dest types"); // Peel off metatypes. If two types are checked-cast-able, so are their // metatypes. unsigned MetatyLevel = 0; while (isa(fromCanTy) && isa(toCanTy)) { auto fromMetaty = cast(fromCanTy); auto toMetaty = cast(toCanTy); // Check representations only for the top-level metatypes as only // those are SIL-lowered. if (!MetatyLevel) { // The representations must match. require(fromMetaty->getRepresentation() == toMetaty->getRepresentation(), "metatype checked cast cannot change metatype representation"); // We can't handle the 'thin' case yet, but it shouldn't really even be // interesting. require(fromMetaty->getRepresentation() != MetatypeRepresentation::Thin, "metatype checked cast cannot check thin metatypes"); } fromCanTy = fromMetaty.getInstanceType(); toCanTy = toMetaty.getInstanceType(); MetatyLevel++; } if (isExact) { require(fromCanTy.getClassOrBoundGenericClass(), "downcast operand must be a class type"); require(toCanTy.getClassOrBoundGenericClass(), "downcast must convert to a class type"); require(fromCanTy->isBindableToSuperclassOf(toCanTy), "downcast must convert to a subclass"); } } void checkUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *CI) { verifyCheckedCast(/*exact*/ false, CI->getOperand()->getType(), CI->getType()); verifyOpenedArchetype(CI, CI->getType().getASTType()); } void checkUnconditionalCheckedCastValueInst( UnconditionalCheckedCastValueInst *CI) { verifyCheckedCast(/*exact*/ false, CI->getOperand()->getType(), CI->getType(), true); verifyOpenedArchetype(CI, CI->getType().getASTType()); } /// Verify if a given type is or contains an opened archetype or dynamic self. /// If this is the case, verify that the provided instruction has a type /// dependent operand for it. void verifyOpenedArchetype(SILInstruction *I, CanType Ty) { if (!Ty) return; // Check the type and all of its contained types. Ty.visit([&](CanType t) { SILValue Def; if (t->isOpenedExistential()) { auto archetypeTy = cast(t); Def = OpenedArchetypes.getOpenedArchetypeDef(archetypeTy); require(Def, "Opened archetype should be registered in SILFunction"); } else if (t->hasDynamicSelfType()) { require(I->getFunction()->hasSelfParam() || I->getFunction()->hasSelfMetadataParam(), "Function containing dynamic self type must have self parameter"); if (I->getFunction()->hasSelfMetadataParam()) Def = I->getFunction()->getArguments().back(); else Def = I->getFunction()->getSelfArgument(); } else { return; } for (auto &TypeDefOp : I->getTypeDependentOperands()) { if (TypeDefOp.get() == Def) return; } require(false, "Instruction should contain a type dependent operand for " "every used open archetype or dynamic self"); }); } void checkCheckedCastBranchInst(CheckedCastBranchInst *CBI) { verifyCheckedCast(CBI->isExact(), CBI->getSourceLoweredType(), CBI->getTargetLoweredType()); verifyOpenedArchetype(CBI, CBI->getTargetFormalType()); require(CBI->getSuccessBB()->args_size() == 1, "success dest of checked_cast_br must take one argument"); requireSameType( CBI->getSuccessBB()->args_begin()[0]->getType(), CBI->getTargetLoweredType(), "success dest block argument of checked_cast_br must match type of " "cast"); if (F.hasOwnership()) { require(CBI->getFailureBB()->args_size() == 1, "failure dest of checked_cast_br must take one argument in " "ownership qualified sil"); requireSameType( CBI->getFailureBB()->args_begin()[0]->getType(), CBI->getOperand()->getType(), "failure dest block argument must match type of original type in " "ownership qualified sil"); } else { require(CBI->getFailureBB()->args_empty(), "Failure dest of checked_cast_br must not take any argument in " "non-ownership qualified sil"); } } void checkCheckedCastValueBranchInst(CheckedCastValueBranchInst *CBI) { verifyCheckedCast(false, CBI->getSourceLoweredType(), CBI->getTargetLoweredType(), true); verifyOpenedArchetype(CBI, CBI->getTargetFormalType()); require(CBI->getSuccessBB()->args_size() == 1, "success dest of checked_cast_value_br must take one argument"); requireSameType( CBI->getSuccessBB()->args_begin()[0]->getType(), CBI->getTargetLoweredType(), "success dest block argument of checked_cast_value_br must match " "type of cast"); require(F.hasOwnership() || CBI->getFailureBB()->args_empty(), "failure dest of checked_cast_value_br in unqualified ownership " "sil must take no arguments"); } void checkCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI) { require(CCABI->getSrc()->getType().isAddress(), "checked_cast_addr_br src must be an address"); require(CCABI->getDest()->getType().isAddress(), "checked_cast_addr_br dest must be an address"); require( CCABI->getSuccessBB()->args_size() == 0, "success dest block of checked_cast_addr_br must not take an argument"); require( CCABI->getFailureBB()->args_size() == 0, "failure dest block of checked_cast_addr_br must not take an argument"); } void checkThinToThickFunctionInst(ThinToThickFunctionInst *TTFI) { auto opFTy = requireObjectType(SILFunctionType, TTFI->getOperand(), "thin_to_thick_function operand"); auto resFTy = requireObjectType(SILFunctionType, TTFI, "thin_to_thick_function result"); require(opFTy->isPolymorphic() == resFTy->isPolymorphic(), "thin_to_thick_function operand and result type must differ only " " in thinness"); requireSameFunctionComponents(opFTy, resFTy, "thin_to_thick_function operand and result"); require(opFTy->getRepresentation() == SILFunctionType::Representation::Thin, "operand of thin_to_thick_function must be thin"); require(resFTy->getRepresentation() == SILFunctionType::Representation::Thick, "result of thin_to_thick_function must be thick"); auto adjustedOperandExtInfo = opFTy->getExtInfo() .withRepresentation(SILFunctionType::Representation::Thick) .withNoEscape(resFTy->isNoEscape()); require(adjustedOperandExtInfo == resFTy->getExtInfo(), "operand and result of thin_to_think_function must agree in particulars"); } void checkThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCI) { auto opTy = requireObjectType(AnyMetatypeType, TTOCI->getOperand(), "thick_to_objc_metatype operand"); auto resTy = requireObjectType(AnyMetatypeType, TTOCI, "thick_to_objc_metatype result"); require(TTOCI->getOperand()->getType().is() == TTOCI->getType().is(), "thick_to_objc_metatype cannot change metatype kinds"); require(opTy->getRepresentation() == MetatypeRepresentation::Thick, "operand of thick_to_objc_metatype must be thick"); require(resTy->getRepresentation() == MetatypeRepresentation::ObjC, "operand of thick_to_objc_metatype must be ObjC"); requireSameType(opTy->getInstanceType(), resTy->getInstanceType(), "thick_to_objc_metatype instance types do not match"); } void checkObjCToThickMetatypeInst(ObjCToThickMetatypeInst *OCTTI) { auto opTy = requireObjectType(AnyMetatypeType, OCTTI->getOperand(), "objc_to_thick_metatype operand"); auto resTy = requireObjectType(AnyMetatypeType, OCTTI, "objc_to_thick_metatype result"); require(OCTTI->getOperand()->getType().is() == OCTTI->getType().is(), "objc_to_thick_metatype cannot change metatype kinds"); require(opTy->getRepresentation() == MetatypeRepresentation::ObjC, "operand of objc_to_thick_metatype must be ObjC"); require(resTy->getRepresentation() == MetatypeRepresentation::Thick, "operand of objc_to_thick_metatype must be thick"); requireSameType(opTy->getInstanceType(), resTy->getInstanceType(), "objc_to_thick_metatype instance types do not match"); } void checkUpcastInst(UpcastInst *UI) { require(UI->getType() != UI->getOperand()->getType(), "can't upcast to same type"); if (UI->getType().is()) { CanType instTy(UI->getType().castTo()->getInstanceType()); require(UI->getOperand()->getType().is(), "upcast operand must be a class or class metatype instance"); CanType opInstTy(UI->getOperand()->getType().castTo() ->getInstanceType()); auto instClass = instTy->getClassOrBoundGenericClass(); require(instClass, "upcast must convert a class metatype to a class metatype"); if (instClass->usesObjCGenericsModel()) { require(instClass->getDeclaredTypeInContext() ->isBindableToSuperclassOf(opInstTy), "upcast must cast to a superclass or an existential metatype"); } else { require(instTy->isExactSuperclassOf(opInstTy), "upcast must cast to a superclass or an existential metatype"); } return; } require(UI->getType().getCategory() == UI->getOperand()->getType().getCategory(), "Upcast can only upcast in between types of the same " "SILValueCategory. This prevents address types from being cast to " "object types or vis-a-versa"); auto ToTy = UI->getType(); auto FromTy = UI->getOperand()->getType(); // Upcast from Optional to Optional is legal as long as B is a // subclass of A. if (ToTy.getASTType().getOptionalObjectType() && FromTy.getASTType().getOptionalObjectType()) { ToTy = SILType::getPrimitiveObjectType( ToTy.getASTType().getOptionalObjectType()); FromTy = SILType::getPrimitiveObjectType( FromTy.getASTType().getOptionalObjectType()); } auto ToClass = ToTy.getClassOrBoundGenericClass(); require(ToClass, "upcast must convert a class instance to a class type"); if (ToClass->usesObjCGenericsModel()) { require(ToClass->getDeclaredTypeInContext() ->isBindableToSuperclassOf(FromTy.getASTType()), "upcast must cast to a superclass or an existential metatype"); } else { require(ToTy.isExactSuperclassOf(FromTy), "upcast must cast to a superclass or an existential metatype"); } } void checkAddressToPointerInst(AddressToPointerInst *AI) { require(AI->getOperand()->getType().isAddress(), "address-to-pointer operand must be an address"); require(AI->getType().getASTType()->isEqual( AI->getType().getASTContext().TheRawPointerType), "address-to-pointer result type must be RawPointer"); } void checkUncheckedRefCastInst(UncheckedRefCastInst *AI) { verifyOpenedArchetype(AI, AI->getType().getASTType()); require(AI->getOperand()->getType().isObject(), "unchecked_ref_cast operand must be a value"); require(AI->getType().isObject(), "unchecked_ref_cast result must be an object"); require(SILType::canRefCast(AI->getOperand()->getType(), AI->getType(), AI->getModule()), "unchecked_ref_cast requires a heap object reference type"); } void checkUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *AI) { auto srcTy = AI->getSrc()->getType(); auto destTy = AI->getDest()->getType(); require(srcTy.isAddress(), "unchecked_ref_cast_addr operand must be an address"); require(destTy.isAddress(), "unchecked_ref_cast_addr result must be an address"); // The static src/dest types cannot be checked here even if they are // loadable. unchecked_ref_cast_addr may accept nonreference static types // (as a result of specialization). These cases will never be promoted to // value bitcast, thus will cause the subsequent runtime cast to fail. } void checkUncheckedAddrCastInst(UncheckedAddrCastInst *AI) { verifyOpenedArchetype(AI, AI->getType().getASTType()); require(AI->getOperand()->getType().isAddress(), "unchecked_addr_cast operand must be an address"); require(AI->getType().isAddress(), "unchecked_addr_cast result must be an address"); } void checkUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *BI) { verifyOpenedArchetype(BI, BI->getType().getASTType()); require(BI->getOperand()->getType().isObject(), "unchecked_trivial_bit_cast must operate on a value"); require(BI->getType().isObject(), "unchecked_trivial_bit_cast must produce a value"); require(BI->getType().isTrivial(F), "unchecked_trivial_bit_cast must produce a value of trivial type"); } void checkUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *BI) { verifyOpenedArchetype(BI, BI->getType().getASTType()); require(BI->getOperand()->getType().isObject(), "unchecked_bitwise_cast must operate on a value"); require(BI->getType().isObject(), "unchecked_bitwise_cast must produce a value"); } void checkRefToRawPointerInst(RefToRawPointerInst *AI) { require(AI->getOperand()->getType().isAnyClassReferenceType(), "ref-to-raw-pointer operand must be a class reference or" " NativeObject"); requireSameType(AI->getType().getASTType(), AI->getType().getASTContext().TheRawPointerType, "ref-to-raw-pointer result must be RawPointer"); } void checkRawPointerToRefInst(RawPointerToRefInst *AI) { verifyOpenedArchetype(AI, AI->getType().getASTType()); require(AI->getType() .getASTType()->isBridgeableObjectType() || AI->getType().getASTType()->isEqual( AI->getType().getASTContext().TheNativeObjectType), "raw-pointer-to-ref result must be a class reference or NativeObject"); requireSameType(AI->getOperand()->getType().getASTType(), AI->getType().getASTContext().TheRawPointerType, "raw-pointer-to-ref operand must be NativeObject"); } void checkRefToBridgeObjectInst(RefToBridgeObjectInst *RI) { require(RI->getConverted()->getType().isObject(), "ref_to_bridge_object must convert from a value"); require(RI->getConverted()->getType().getASTType() ->isBridgeableObjectType(), "ref_to_bridge_object must convert from a heap object ref"); requireSameType( RI->getBitsOperand()->getType(), SILType::getBuiltinWordType(F.getASTContext()), "ref_to_bridge_object must take a Builtin.Word bits operand"); requireSameType(RI->getType(), SILType::getBridgeObjectType(F.getASTContext()), "ref_to_bridge_object must produce a BridgeObject"); } void checkBridgeObjectToRefInst(BridgeObjectToRefInst *RI) { verifyOpenedArchetype(RI, RI->getType().getASTType()); requireSameType(RI->getConverted()->getType(), SILType::getBridgeObjectType(F.getASTContext()), "bridge_object_to_ref must take a BridgeObject"); require(RI->getType().isObject(), "bridge_object_to_ref must produce a value"); require(RI->getType().getASTType()->isBridgeableObjectType(), "bridge_object_to_ref must produce a heap object reference"); } void checkBridgeObjectToWordInst(BridgeObjectToWordInst *RI) { requireSameType(RI->getConverted()->getType(), SILType::getBridgeObjectType(F.getASTContext()), "bridge_object_to_word must take a BridgeObject"); require(RI->getType().isObject(), "bridge_object_to_word must produce a value"); requireSameType(RI->getType(), SILType::getBuiltinWordType(F.getASTContext()), "bridge_object_to_word must produce a Word"); } void checkConvertFunctionInst(ConvertFunctionInst *ICI) { auto opTI = requireObjectType(SILFunctionType, ICI->getOperand(), "convert_function operand"); auto resTI = requireObjectType(SILFunctionType, ICI, "convert_function result"); // convert_function is required to be an ABI-compatible conversion. requireABICompatibleFunctionTypes( opTI, resTI, "convert_function cannot change function ABI", *ICI->getFunction()); } void checkConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *ICI) { auto opTI = requireObjectType(SILFunctionType, ICI->getOperand(), "convert_escape_to_noescape operand"); auto resTI = ICI->getType().castTo(); // FIXME: Not yet, to be enabled when this is true. // require(resTI->isTrivial(F.getModule()), // "convert_escape_to_noescape should produce a trivial result type"); // convert_escape_to_noescape is required to be an ABI-compatible // conversion once escapability is the same on both sides. requireABICompatibleFunctionTypes( opTI, resTI->getWithExtInfo(resTI->getExtInfo().withNoEscape(false)), "convert_escape_to_noescape cannot change function ABI", *ICI->getFunction()); // After mandatory passes convert_escape_to_noescape should not have the // '[not_guaranteed]' or '[escaped]' attributes. if (!SkipConvertEscapeToNoescapeAttributes && F.getModule().getStage() != SILStage::Raw) { require(ICI->isLifetimeGuaranteed(), "convert_escape_to_noescape [not_guaranteed] not " "allowed after mandatory passes"); } } void checkThinFunctionToPointerInst(ThinFunctionToPointerInst *CI) { auto opTI = requireObjectType(SILFunctionType, CI->getOperand(), "thin_function_to_pointer operand"); requireObjectType(BuiltinRawPointerType, CI, "thin_function_to_pointer result"); auto rep = opTI->getRepresentation(); require(rep == SILFunctionTypeRepresentation::Thin || rep == SILFunctionTypeRepresentation::Method || rep == SILFunctionTypeRepresentation::WitnessMethod, "thin_function_to_pointer only works on thin, method or " "witness_method functions"); } void checkPointerToThinFunctionInst(PointerToThinFunctionInst *CI) { auto resultTI = requireObjectType(SILFunctionType, CI, "pointer_to_thin_function result"); requireObjectType(BuiltinRawPointerType, CI->getOperand(), "pointer_to_thin_function operand"); auto rep = resultTI->getRepresentation(); require(rep == SILFunctionTypeRepresentation::Thin || rep == SILFunctionTypeRepresentation::Method || rep == SILFunctionTypeRepresentation::WitnessMethod, "pointer_to_thin_function only works on thin, method or " "witness_method functions"); } void checkCondFailInst(CondFailInst *CFI) { requireSameType(CFI->getOperand()->getType(), SILType::getBuiltinIntegerType(1, F.getASTContext()), "cond_fail operand must be a Builtin.Int1"); } void checkReturnInst(ReturnInst *RI) { LLVM_DEBUG(RI->print(llvm::dbgs())); SILType functionResultType = F.getLoweredType( F.mapTypeIntoContext(fnConv.getSILResultType()).getASTType()) .getCategoryType(fnConv.getSILResultType().getCategory()); SILType instResultType = RI->getOperand()->getType(); LLVM_DEBUG(llvm::dbgs() << "function return type: "; functionResultType.dump(); llvm::dbgs() << "return inst type: "; instResultType.dump();); requireSameType(functionResultType, instResultType, "return value type does not match return type of function"); } void checkThrowInst(ThrowInst *TI) { LLVM_DEBUG(TI->print(llvm::dbgs())); require(fnConv.funcTy->hasErrorResult(), "throw in function that doesn't have an error result"); SILType functionResultType = F.getLoweredType( F.mapTypeIntoContext(fnConv.getSILErrorType()).getASTType()) .getCategoryType(fnConv.getSILErrorType().getCategory()); SILType instResultType = TI->getOperand()->getType(); LLVM_DEBUG(llvm::dbgs() << "function error result type: "; functionResultType.dump(); llvm::dbgs() << "throw operand type: "; instResultType.dump();); requireSameType( functionResultType, instResultType, "throw operand type does not match error result type of function"); } void checkUnwindInst(UnwindInst *UI) { require(F.getLoweredFunctionType()->isCoroutine(), "unwind in non-coroutine function"); } void checkYieldInst(YieldInst *YI) { require(fnConv.funcTy->isCoroutine(), "yield in non-coroutine function"); auto yieldedValues = YI->getYieldedValues(); auto yieldInfos = fnConv.funcTy->getYields(); require(yieldedValues.size() == yieldInfos.size(), "wrong number of yielded values for function"); for (auto i : indices(yieldedValues)) { SILType yieldType = F.mapTypeIntoContext(fnConv.getSILType(yieldInfos[i])); requireSameType(yieldedValues[i]->getType(), yieldType, "yielded value does not match yield type of coroutine"); } // We require the resume and unwind destinations to be unique in order // to prevent either edge from becoming critical. require(YI->getResumeBB()->getSinglePredecessorBlock(), "resume dest of 'yield' must be uniquely used"); require(YI->getUnwindBB()->getSinglePredecessorBlock(), "unwind dest of 'yield' must be uniquely used"); } void checkSelectEnumCases(SelectEnumInstBase *I) { EnumDecl *eDecl = I->getEnumOperand()->getType().getEnumOrBoundGenericEnum(); require(eDecl, "select_enum operand must be an enum"); // Find the set of enum elements for the type so we can verify // exhaustiveness. llvm::DenseSet unswitchedElts; eDecl->getAllElements(unswitchedElts); // Verify the set of enum cases we dispatch on. for (unsigned i = 0, e = I->getNumCases(); i < e; ++i) { EnumElementDecl *elt; SILValue result; std::tie(elt, result) = I->getCase(i); require(elt->getDeclContext() == eDecl, "select_enum dispatches on enum element that is not part of " "its type"); require(unswitchedElts.count(elt), "select_enum dispatches on same enum element more than once"); unswitchedElts.erase(elt); // The result value must match the type of the instruction. requireSameType(result->getType(), I->getType(), "select_enum case operand must match type of instruction"); } // If the select is non-exhaustive, we require a default. bool isExhaustive = eDecl->isEffectivelyExhaustive(F.getModule().getSwiftModule(), F.getResilienceExpansion()); require((isExhaustive && unswitchedElts.empty()) || I->hasDefault(), "nonexhaustive select_enum must have a default destination"); if (I->hasDefault()) { requireSameType(I->getDefaultResult()->getType(), I->getType(), "select_enum default operand must match type of instruction"); } } void checkSelectEnumInst(SelectEnumInst *SEI) { require(SEI->getEnumOperand()->getType().isObject(), "select_enum operand must be an object"); checkSelectEnumCases(SEI); } void checkSelectEnumAddrInst(SelectEnumAddrInst *SEI) { require(SEI->getEnumOperand()->getType().isAddress(), "select_enum_addr operand must be an address"); checkSelectEnumCases(SEI); } void checkSwitchValueInst(SwitchValueInst *SVI) { // TODO: Type should be either integer or function auto Ty = SVI->getOperand()->getType(); require(Ty.is() || Ty.is(), "switch_value operand should be either of an integer " "or function type"); auto ult = [](const SILValue &a, const SILValue &b) { return a == b || a < b; }; std::set cases(ult); for (unsigned i = 0, e = SVI->getNumCases(); i < e; ++i) { SILValue value; SILBasicBlock *dest; std::tie(value, dest) = SVI->getCase(i); requireSameType( value->getType(), Ty, "switch_value case value should have the same type as its operand"); require(!cases.count(value), "multiple switch_value cases for same value"); cases.insert(value); require(dest->args_empty(), "switch_value case destination cannot take arguments"); } if (SVI->hasDefault()) require(SVI->getDefaultBB()->args_empty(), "switch_value default destination cannot take arguments"); } void checkSelectValueCases(SelectValueInst *I) { struct APIntCmp { bool operator()(const APInt &a, const APInt &b) const { return a.ult(b); }; }; llvm::SmallSet seenCaseValues; // Verify the set of cases we dispatch on. for (unsigned i = 0, e = I->getNumCases(); i < e; ++i) { SILValue casevalue; SILValue result; std::tie(casevalue, result) = I->getCase(i); if (!isa(casevalue)) { auto *il = dyn_cast(casevalue); require(il, "select_value case operands should refer to integer literals"); APInt elt = il->getValue(); require(!seenCaseValues.count(elt), "select_value dispatches on same case value more than once"); seenCaseValues.insert(elt); } requireSameType(I->getOperand()->getType(), casevalue->getType(), "select_value case value must match type of operand"); // The result value must match the type of the instruction. requireSameType(result->getType(), I->getType(), "select_value case result must match type of instruction"); } require(I->hasDefault(), "select_value should always have a default"); requireSameType(I->getDefaultResult()->getType(), I->getType(), "select_value default operand must match type of instruction"); } void checkSelectValueInst(SelectValueInst *SVI) { require(SVI->getOperand()->getType().isObject(), "select_value operand must be an object"); checkSelectValueCases(SVI); } void checkSwitchEnumInst(SwitchEnumInst *SOI) { require(SOI->getOperand()->getType().isObject(), "switch_enum operand must be an object"); SILType uTy = SOI->getOperand()->getType(); EnumDecl *uDecl = uTy.getEnumOrBoundGenericEnum(); require(uDecl, "switch_enum operand is not an enum"); // Find the set of enum elements for the type so we can verify // exhaustiveness. llvm::DenseSet unswitchedElts; uDecl->getAllElements(unswitchedElts); // Verify the set of enum cases we dispatch on. for (unsigned i = 0, e = SOI->getNumCases(); i < e; ++i) { EnumElementDecl *elt; SILBasicBlock *dest; std::tie(elt, dest) = SOI->getCase(i); require(elt->getDeclContext() == uDecl, "switch_enum dispatches on enum element that is not part of " "its type"); require(unswitchedElts.count(elt), "switch_enum dispatches on same enum element more than once"); unswitchedElts.erase(elt); // The destination BB can take the argument payload, if any, as a BB // arguments, or it can ignore it and take no arguments. if (elt->hasAssociatedValues()) { if (isSILOwnershipEnabled() && F.hasOwnership()) { require(dest->getArguments().size() == 1, "switch_enum destination for case w/ args must take 1 " "argument"); } else { require(dest->getArguments().empty() || dest->getArguments().size() == 1, "switch_enum destination for case w/ args must take 0 or 1 " "arguments"); } if (dest->getArguments().size() == 1) { SILType eltArgTy = uTy.getEnumElementType(elt, F.getModule(), F.getTypeExpansionContext()); SILType bbArgTy = dest->getArguments()[0]->getType(); if (F.getModule().getStage() != SILStage::Lowered) { // During the lowered stage, a function type might have different // signature require(eltArgTy == bbArgTy, "switch_enum destination bbarg must match case arg type"); } require(!dest->getArguments()[0]->getType().isAddress(), "switch_enum destination bbarg type must not be an address"); } } else { require(dest->getArguments().empty(), "switch_enum destination for no-argument case must take no " "arguments"); } } // If the switch is non-exhaustive, we require a default. bool isExhaustive = uDecl->isEffectivelyExhaustive(F.getModule().getSwiftModule(), F.getResilienceExpansion()); require((isExhaustive && unswitchedElts.empty()) || SOI->hasDefault(), "nonexhaustive switch_enum must have a default destination"); if (SOI->hasDefault()) { // When SIL ownership is enabled, we require all default branches to take // an @owned original version of the enum. // // When SIL ownership is disabled, we no longer support this. if (isSILOwnershipEnabled() && F.hasOwnership()) { require(SOI->getDefaultBB()->getNumArguments() == 1, "Switch enum default block should have one argument"); requireSameType( SOI->getDefaultBB()->getArgument(0)->getType(), SOI->getOperand()->getType(), "Switch enum default block should have one argument that is " "the same as the input type"); } else if (!F.hasOwnership()) { require(SOI->getDefaultBB()->args_empty(), "switch_enum default destination must take no arguments"); } } } void checkSwitchEnumAddrInst(SwitchEnumAddrInst *SOI) { require(SOI->getOperand()->getType().isAddress(), "switch_enum_addr operand must be an address"); SILType uTy = SOI->getOperand()->getType(); EnumDecl *uDecl = uTy.getEnumOrBoundGenericEnum(); require(uDecl, "switch_enum_addr operand must be an enum"); // Find the set of enum elements for the type so we can verify // exhaustiveness. llvm::DenseSet unswitchedElts; uDecl->getAllElements(unswitchedElts); // Verify the set of enum cases we dispatch on. for (unsigned i = 0, e = SOI->getNumCases(); i < e; ++i) { EnumElementDecl *elt; SILBasicBlock *dest; std::tie(elt, dest) = SOI->getCase(i); require(elt->getDeclContext() == uDecl, "switch_enum_addr dispatches on enum element that " "is not part of its type"); require(unswitchedElts.count(elt), "switch_enum_addr dispatches on same enum element " "more than once"); unswitchedElts.erase(elt); // The destination BB must not have BB arguments. require(dest->getArguments().empty(), "switch_enum_addr destination must take no BB args"); } // If the switch is non-exhaustive, we require a default. 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 " "no arguments"); } bool verifyBranchArgs(SILValue branchArg, SILArgument *bbArg) { // NOTE: IRGen currently does not support the following method_inst // variants as branch arguments. // Once this is supported, the check can be removed. require(!(isa(branchArg) && cast(branchArg)->getMember().isForeign), "branch argument cannot be a witness_method or an objc method_inst"); require(!(branchArg->getType().is() && branchArg->getType() .castTo() ->getExtInfo() .getRepresentation() == SILFunctionTypeRepresentation::ObjCMethod), "branch argument cannot be a objective-c method"); return branchArg->getType() == bbArg->getType(); } void checkBranchInst(BranchInst *BI) { require(BI->getArgs().size() == BI->getDestBB()->args_size(), "branch has wrong number of arguments for dest bb"); require(std::equal(BI->getArgs().begin(), BI->getArgs().end(), BI->getDestBB()->args_begin(), [&](SILValue branchArg, SILArgument *bbArg) { return verifyBranchArgs(branchArg, bbArg); }), "branch argument types do not match arguments for dest bb"); } void checkCondBranchInst(CondBranchInst *cbi) { // It is important that cond_br keeps an i1 type. ARC Sequence Opts assumes // that cond_br does not use reference counted values or decrement reference // counted values under the assumption that the instruction that computes // the i1 is the use/decrement that ARC cares about and that after that // instruction is evaluated, the scalar i1 has a different identity and the // object can be deallocated. requireSameType(cbi->getCondition()->getType(), SILType::getBuiltinIntegerType( 1, cbi->getCondition()->getType().getASTContext()), "condition of conditional branch must have Int1 type"); require(cbi->getTrueArgs().size() == cbi->getTrueBB()->args_size(), "true branch has wrong number of arguments for dest bb"); require(cbi->getTrueBB() != cbi->getFalseBB(), "identical destinations"); require(std::equal(cbi->getTrueArgs().begin(), cbi->getTrueArgs().end(), cbi->getTrueBB()->args_begin(), [&](SILValue branchArg, SILArgument *bbArg) { return verifyBranchArgs(branchArg, bbArg); }), "true branch argument types do not match arguments for dest bb"); require(cbi->getFalseArgs().size() == cbi->getFalseBB()->args_size(), "false branch has wrong number of arguments for dest bb"); require(std::equal(cbi->getFalseArgs().begin(), cbi->getFalseArgs().end(), cbi->getFalseBB()->args_begin(), [&](SILValue branchArg, SILArgument *bbArg) { return verifyBranchArgs(branchArg, bbArg); }), "false branch argument types do not match arguments for dest bb"); // When we are in ossa, cond_br can not have any arguments that are // non-trivial. if (!F.hasOwnership()) return; require(llvm::all_of(cbi->getOperandValues(), [&](SILValue v) -> bool { return v->getType().isTrivial(*cbi->getFunction()); }), "cond_br must not have a non-trivial value in ossa."); } void checkDynamicMethodBranchInst(DynamicMethodBranchInst *DMBI) { SILType operandType = DMBI->getOperand()->getType(); require(DMBI->getMember().getDecl()->isObjC(), "method must be @objc"); if (!DMBI->getMember().getDecl()->isInstanceMember()) { require(operandType.is(), "operand must have metatype type"); require(operandType.castTo() ->getInstanceType()->mayHaveSuperclass(), "operand must have metatype of class or class-bound type"); } // Check that the branch argument is of the expected dynamic method type. require(DMBI->getHasMethodBB()->args_size() == 1, "true bb for dynamic_method_br must take an argument"); auto bbArgTy = DMBI->getHasMethodBB()->args_begin()[0]->getType(); require(getDynamicMethodType(operandType, DMBI->getMember()) .getASTType() ->isBindableTo(bbArgTy.getASTType()), "bb argument for dynamic_method_br must be of the method's type"); } void checkProjectBlockStorageInst(ProjectBlockStorageInst *PBSI) { require(PBSI->getOperand()->getType().isAddress(), "operand must be an address"); auto storageTy = PBSI->getOperand()->getType().getAs(); require(storageTy, "operand must be a @block_storage type"); require(PBSI->getType().isAddress(), "result must be an address"); auto captureTy = PBSI->getType().getASTType(); requireSameType( storageTy->getCaptureType(), captureTy, "result must be the capture type of the @block_storage type"); } void checkInitBlockStorageHeaderInst(InitBlockStorageHeaderInst *IBSHI) { auto storage = IBSHI->getBlockStorage(); require(storage->getType().isAddress(), "block storage operand must be an address"); auto storageTy = storage->getType().getAs(); require(storageTy, "block storage operand must be a @block_storage type"); auto captureTy = storageTy->getCaptureType(); if (auto capturedFnTy = captureTy->getAs()) { if (capturedFnTy->isNoEscape()) { // If the capture is a noescape function then it must be possible to // locally determine the value stored to initialize the storage for the // capture. This is required to diagnose static exclusivity violations // when a noescape closure is converted to a noescape block that // is then passed to a function. auto *storageProjection = storage->getSingleUserOfType(); require(storageProjection, "block storage operand with noescape capture must have " "projection from block"); auto *storeInst = storageProjection->getSingleUserOfType(); require(storeInst, "block storage operand with noescape capture must have " "store to projection"); } } require(IBSHI->getInvokeFunction()->getType().isObject(), "invoke function operand must be a value"); auto invokeTy = IBSHI->getInvokeFunction()->getType().getAs(); require(invokeTy, "invoke function operand must be a function"); require(invokeTy->getRepresentation() == SILFunctionType::Representation::CFunctionPointer, "invoke function operand must be a c function"); require(invokeTy->getParameters().size() >= 1, "invoke function must take at least one parameter"); require(!invokeTy->getInvocationGenericSignature() || invokeTy->getExtInfo().isPseudogeneric(), "invoke function must not take reified generic parameters"); invokeTy = checkApplySubstitutions(IBSHI->getSubstitutions(), SILType::getPrimitiveObjectType(invokeTy)); auto storageParam = invokeTy->getParameters()[0]; require(storageParam.getConvention() == ParameterConvention::Indirect_InoutAliasable, "invoke function must take block storage as @inout_aliasable " "parameter"); requireSameType( storageParam.getArgumentType(F.getModule(), invokeTy), storageTy, "invoke function must take block storage type as first parameter"); require(IBSHI->getType().isObject(), "result must be a value"); auto blockTy = IBSHI->getType().getAs(); require(blockTy, "result must be a function"); require(blockTy->getRepresentation() == SILFunctionType::Representation::Block, "result must be a cdecl block function"); require(blockTy->getResults() == invokeTy->getResults(), "result must have same results as invoke function"); require(blockTy->getParameters().size() + 1 == invokeTy->getParameters().size(), "result must match all parameters of invoke function but the first"); auto blockParams = blockTy->getParameters(); auto invokeBlockParams = invokeTy->getParameters().slice(1); for (unsigned i : indices(blockParams)) { require(blockParams[i] == invokeBlockParams[i], "result must match all parameters of invoke function but the first"); } } void checkObjCProtocolInst(ObjCProtocolInst *OPI) { require(OPI->getProtocol()->isObjC(), "objc_protocol must be applied to an @objc protocol"); auto classTy = OPI->getType(); require(classTy.isObject(), "objc_protocol must produce a value"); auto classDecl = classTy.getClassOrBoundGenericClass(); require(classDecl, "objc_protocol must produce a class instance"); require(classDecl->getName() == F.getASTContext().Id_Protocol, "objc_protocol must produce an instance of ObjectiveC.Protocol class"); require(classDecl->getModuleContext()->getName() == F.getASTContext().Id_ObjectiveC, "objc_protocol must produce an instance of ObjectiveC.Protocol class"); } void checkObjCMetatypeToObjectInst(ObjCMetatypeToObjectInst *OMOI) { require(OMOI->getOperand()->getType().isObject(), "objc_metatype_to_object must take a value"); auto fromMetaTy = OMOI->getOperand()->getType().getAs(); require(fromMetaTy, "objc_metatype_to_object must take an @objc metatype value"); require(fromMetaTy->getRepresentation() == MetatypeRepresentation::ObjC, "objc_metatype_to_object must take an @objc metatype value"); require(OMOI->getType().isObject(), "objc_metatype_to_object must produce a value"); require(OMOI->getType().getASTType()->isAnyObject(), "objc_metatype_to_object must produce an AnyObject value"); } void checkObjCExistentialMetatypeToObjectInst( ObjCExistentialMetatypeToObjectInst *OMOI) { require(OMOI->getOperand()->getType().isObject(), "objc_metatype_to_object must take a value"); auto fromMetaTy = OMOI->getOperand()->getType() .getAs(); require(fromMetaTy, "objc_metatype_to_object must take an @objc existential metatype value"); require(fromMetaTy->getRepresentation() == MetatypeRepresentation::ObjC, "objc_metatype_to_object must take an @objc existential metatype value"); require(OMOI->getType().isObject(), "objc_metatype_to_object must produce a value"); require(OMOI->getType().getASTType()->isAnyObject(), "objc_metatype_to_object must produce an AnyObject value"); } void checkKeyPathInst(KeyPathInst *KPI) { auto kpTy = KPI->getType(); require(kpTy.isObject(), "keypath result must be an object type"); auto kpBGT = kpTy.getAs(); require(kpBGT, "keypath result must be a generic type"); auto &C = F.getASTContext(); require(kpBGT->getDecl() == C.getKeyPathDecl() || kpBGT->getDecl() == C.getWritableKeyPathDecl() || kpBGT->getDecl() == C.getReferenceWritableKeyPathDecl(), "keypath result must be a key path type"); auto baseTy = CanType(kpBGT->getGenericArgs()[0]); auto pattern = KPI->getPattern(); SubstitutionMap patternSubs = KPI->getSubstitutions(); requireSameType( baseTy, pattern->getRootType().subst(patternSubs)->getCanonicalType(), "keypath root type should match root type of keypath pattern"); auto leafTy = CanType(kpBGT->getGenericArgs()[1]); requireSameType( leafTy, pattern->getValueType().subst(patternSubs)->getCanonicalType(), "keypath value type should match value type of keypath pattern"); { for (auto &component : pattern->getComponents()) { bool hasIndices; switch (component.getKind()) { case KeyPathPatternComponent::Kind::GettableProperty: case KeyPathPatternComponent::Kind::SettableProperty: hasIndices = !component.getSubscriptIndices().empty(); break; case KeyPathPatternComponent::Kind::StoredProperty: case KeyPathPatternComponent::Kind::OptionalChain: case KeyPathPatternComponent::Kind::OptionalWrap: case KeyPathPatternComponent::Kind::OptionalForce: case KeyPathPatternComponent::Kind::TupleElement: hasIndices = false; break; } verifyKeyPathComponent(F.getModule(), F.getResilienceExpansion(), [&](bool reqt, StringRef message) { _require(reqt, message); }, baseTy, leafTy, component, KPI->getAllOperands(), KPI->getPattern()->getGenericSignature(), KPI->getSubstitutions(), /*property descriptor*/false, hasIndices); } } requireSameType( CanType(baseTy), CanType(leafTy), "final component should match leaf value type of key path type"); } void checkIsEscapingClosureInst(IsEscapingClosureInst *IEC) { // The closure operand is allowed to be an optional closure. auto operandType = IEC->getOperand()->getType(); if (operandType.getOptionalObjectType()) operandType = operandType.getOptionalObjectType(); auto fnType = operandType.getAs(); require(fnType && fnType->getExtInfo().hasContext() && !fnType->isNoEscape() && fnType->getExtInfo().getRepresentation() == SILFunctionTypeRepresentation::Thick, "is_escaping_closure must have a thick " "function operand"); require(IEC->getVerificationType() == IsEscapingClosureInst::ObjCEscaping || IEC->getVerificationType() == IsEscapingClosureInst::WithoutActuallyEscaping, "unknown verfication type"); } void checkDifferentiableFunctionInst(DifferentiableFunctionInst *dfi) { // FIXME(TF-1197): Re-enable verification after substituted SIL function // types. return; #if 0 auto origTy = dfi->getOriginalFunction()->getType().getAs(); require(origTy, "The original function must have a function type"); require(!origTy->isDifferentiable(), "The original function must not be @differentiable"); // Skip verification in lowered SIL: LoadableByAddress changes // parameter/result conventions. // TODO: Check that derivative function types match excluding // parameter/result conventions in lowered SIL. if (F.getModule().getStage() == SILStage::Lowered) return; if (dfi->hasDerivativeFunctions()) { auto jvp = dfi->getJVPFunction(); auto jvpType = jvp->getType().getAs(); require(jvpType, "The JVP function must have a function type"); require(!jvpType->isDifferentiable(), "The JVP function must not be @differentiable"); auto expectedJVPType = origTy->getAutoDiffDerivativeFunctionType( dfi->getParameterIndices(), /*resultIndex*/ 0, AutoDiffDerivativeFunctionKind::JVP, TC, LookUpConformanceInModule(M)); requireSameType(SILType::getPrimitiveObjectType(jvpType), SILType::getPrimitiveObjectType(expectedJVPType), "JVP type does not match expected JVP type"); auto vjp = dfi->getVJPFunction(); auto vjpType = vjp->getType().getAs(); require(vjpType, "The VJP function must have a function type"); require(!vjpType->isDifferentiable(), "The VJP function must not be @differentiable"); auto expectedVJPType = origTy->getAutoDiffDerivativeFunctionType( dfi->getParameterIndices(), /*resultIndex*/ 0, AutoDiffDerivativeFunctionKind::VJP, TC, LookUpConformanceInModule(M)); requireSameType(SILType::getPrimitiveObjectType(vjpType), SILType::getPrimitiveObjectType(expectedVJPType), "VJP type does not match expected VJP type"); } #endif } void checkLinearFunctionInst(LinearFunctionInst *lfi) { auto origTy = lfi->getOriginalFunction()->getType().getAs(); require(origTy, "The original function must have a function type"); require(!origTy->isDifferentiable(), "The original function must not be differentiable"); // Skip lowered SIL: LoadableByAddress changes parameter/result conventions. // TODO: Check that transpose function type matches excluding // parameter/result conventions in lowered SIL. if (F.getModule().getStage() == SILStage::Lowered) return; if (lfi->hasTransposeFunction()) { auto transpose = lfi->getTransposeFunction(); auto transposeType = transpose->getType().getAs(); require(transposeType, "The transpose function must have a function type"); require(!transposeType->isDifferentiable(), "The transpose function must not be differentiable"); auto expectedTransposeType = origTy->getAutoDiffTransposeFunctionType( lfi->getParameterIndices(), TC, LookUpConformanceInModule(M)); // TODO: Consider tightening verification. This requires changes to // `SILFunctionType::getAutoDiffTransposeFunctionType`. requireSameType( SILType::getPrimitiveObjectType( transposeType->getUnsubstitutedType(F.getModule())), SILType::getPrimitiveObjectType( expectedTransposeType->getUnsubstitutedType(F.getModule())), "Transpose type does not match expected transpose type"); } } void checkDifferentiableFunctionExtractInst( DifferentiableFunctionExtractInst *dfei) { auto fnTy = dfei->getOperand()->getType().getAs(); require(fnTy, "The function operand must have a function type"); require(fnTy->getDifferentiabilityKind() == DifferentiabilityKind::Normal, "The function operand must be a '@differentiable' function"); } void checkLinearFunctionExtractInst(LinearFunctionExtractInst *lfei) { auto fnTy = lfei->getFunctionOperand()->getType().getAs(); require(fnTy, "The function operand must have a function type"); require(fnTy->getDifferentiabilityKind() == DifferentiabilityKind::Linear, "The function operand must be a '@differentiable(linear)' " "function"); } void checkDifferentiabilityWitnessFunctionInst( DifferentiabilityWitnessFunctionInst *dwfi) { auto witnessFnTy = dwfi->getType().castTo(); auto *witness = dwfi->getWitness(); // `DifferentiabilityWitnessFunctionInst` constructor asserts that // `witness` is non-null. auto witnessKind = dwfi->getWitnessKind(); // Return if not witnessing a derivative function. auto derivKind = witnessKind.getAsDerivativeFunctionKind(); if (!derivKind) return; // Return if witness does not define the referenced derivative. auto *derivativeFn = witness->getDerivative(*derivKind); if (!derivativeFn) return; auto derivativeFnTy = derivativeFn->getLoweredFunctionType(); requireSameType(SILType::getPrimitiveObjectType(witnessFnTy), SILType::getPrimitiveObjectType(derivativeFnTy), "Type of witness instruction does not match actual type of " "witnessed function"); } // This verifies that the entry block of a SIL function doesn't have // any predecessors and also verifies the entry point arguments. void verifyEntryBlock(SILBasicBlock *entry) { require(entry->pred_empty(), "entry block cannot have predecessors"); LLVM_DEBUG(llvm::dbgs() << "Argument types for entry point BB:\n"; for (auto *arg : make_range(entry->args_begin(), entry->args_end())) arg->getType() .dump(); llvm::dbgs() << "Input types for SIL function type "; F.getLoweredFunctionType()->print(llvm::dbgs()); llvm::dbgs() << ":\n"; for (auto paramTy : fnConv.getParameterSILTypes()) { paramTy.dump(); }); require(entry->args_size() == (fnConv.getNumIndirectSILResults() + fnConv.getNumParameters()), "entry point has wrong number of arguments"); bool matched = true; auto argI = entry->args_begin(); auto check = [&](const char *what, SILType ty) { auto mappedTy = F.mapTypeIntoContext(ty); SILArgument *bbarg = *argI; ++argI; if (bbarg->getType() != mappedTy && bbarg->getType() != F.getLoweredType(mappedTy.getASTType()) .getCategoryType(mappedTy.getCategory())) { llvm::errs() << what << " type mismatch!\n"; llvm::errs() << " argument: "; bbarg->dump(); llvm::errs() << " expected: "; mappedTy.dump(); matched = false; } // If we do not have qualified ownership, do not check ownership. if (!F.hasOwnership()) { return; } auto ownershipkind = ValueOwnershipKind( F, mappedTy, fnConv.getSILArgumentConvention(bbarg->getIndex())); if (bbarg->getOwnershipKind() != ownershipkind) { llvm::errs() << what << " ownership kind mismatch!\n"; llvm::errs() << " argument: " << bbarg->getOwnershipKind() << '\n'; llvm::errs() << " expected: " << ownershipkind << '\n'; matched = false; } }; for (auto result : fnConv.getIndirectSILResults()) { assert(fnConv.isSILIndirect(result)); check("indirect result", fnConv.getSILType(result)); } for (auto param : F.getLoweredFunctionType()->getParameters()) { check("parameter", fnConv.getSILType(param)); } require(matched, "entry point argument types do not match function type"); // TBAA requirement for all address arguments. require(std::equal(entry->args_begin() + fnConv.getNumIndirectSILResults(), entry->args_end(), fnConv.funcTy->getParameters().begin(), [&](SILArgument *bbarg, SILParameterInfo paramInfo) { if (!bbarg->getType().isAddress()) return true; switch (paramInfo.getConvention()) { default: return false; case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: case ParameterConvention::Indirect_In_Guaranteed: return true; } }), "entry point address argument must have an indirect calling " "convention"); } void verifyEpilogBlocks(SILFunction *F) { bool FoundReturnBlock = false; bool FoundThrowBlock = false; bool FoundUnwindBlock = false; for (auto &BB : *F) { if (isa(BB.getTerminator())) { require(!FoundReturnBlock, "more than one return block in function"); FoundReturnBlock = true; } else if (isa(BB.getTerminator())) { require(!FoundThrowBlock, "more than one throw block in function"); FoundThrowBlock = true; } else if (isa(BB.getTerminator())) { require(!FoundUnwindBlock, "more than one unwind block in function"); FoundUnwindBlock = true; } else { assert(!BB.getTerminator()->isFunctionExiting()); } } } bool isUnreachableAlongAllPathsStartingAt( SILBasicBlock *StartBlock, SmallPtrSetImpl &Visited) { if (isa(StartBlock->getTerminator())) return true; else if (isa(StartBlock->getTerminator())) return false; else if (isa(StartBlock->getTerminator())) return false; // Recursively check all successors. for (auto *SuccBB : StartBlock->getSuccessorBlocks()) if (!Visited.insert(SuccBB).second) if (!isUnreachableAlongAllPathsStartingAt(SuccBB, Visited)) return false; return true; } void verifySILFunctionType(CanSILFunctionType FTy) { // Make sure that if FTy's calling convention implies that it must have a // self parameter. require(!FTy->hasSelfParam() || !FTy->getParameters().empty(), "Functions with a calling convention with self parameter must " "have at least one argument for self."); } struct VerifyFlowSensitiveRulesDetails { enum CFGState { /// No special rules are in play. Normal, /// We've followed the resume edge of a yield in a yield_once coroutine. YieldOnceResume, /// We've followed the unwind edge of a yield. YieldUnwind }; struct BBState { std::vector Stack; /// Contents: BeginAccessInst*, BeginApplyInst*. std::set ActiveOps; CFGState CFG = Normal; }; }; /// Verify the various control-flow-sensitive rules of SIL: /// /// - stack allocations and deallocations must obey a stack discipline /// - accesses must be uniquely ended /// - flow-sensitive states must be equivalent on all paths into a block void verifyFlowSensitiveRules(SILFunction *F) { // Do a traversal of the basic blocks. // Note that we intentionally don't verify these properties in blocks // that can't be reached from the entry block. llvm::DenseMap visitedBBs; SmallVector Worklist; visitedBBs.try_emplace(&*F->begin()); Worklist.push_back(&*F->begin()); while (!Worklist.empty()) { SILBasicBlock *BB = Worklist.pop_back_val(); VerifyFlowSensitiveRulesDetails::BBState state = visitedBBs[BB]; for (SILInstruction &i : *BB) { CurInstruction = &i; if (i.isAllocatingStack()) { state.Stack.push_back(cast(&i)); } else if (i.isDeallocatingStack()) { SILValue op = i.getOperand(0); require(!state.Stack.empty(), "stack dealloc with empty stack"); require(op == state.Stack.back(), "stack dealloc does not match most recent stack alloc"); state.Stack.pop_back(); } else if (isa(i) || isa(i)) { bool notAlreadyPresent = state.ActiveOps.insert(&i).second; require(notAlreadyPresent, "operation was not ended before re-beginning it"); } else if (isa(i) || isa(i) || isa(i)) { if (auto beginOp = i.getOperand(0)->getDefiningInstruction()) { bool present = state.ActiveOps.erase(beginOp); require(present, "operation has already been ended"); } } else if (auto term = dyn_cast(&i)) { if (term->isFunctionExiting()) { require(state.Stack.empty(), "return with stack allocs that haven't been deallocated"); require(state.ActiveOps.empty(), "return with operations still active"); if (isa(term)) { require(state.CFG == VerifyFlowSensitiveRulesDetails::YieldUnwind, "encountered 'unwind' when not on unwind path"); } else { require(state.CFG != VerifyFlowSensitiveRulesDetails::YieldUnwind, "encountered 'return' or 'throw' when on unwind path"); if (isa(term) && F->getLoweredFunctionType()->getCoroutineKind() == SILCoroutineKind::YieldOnce && F->getModule().getStage() != SILStage::Raw) { require(state.CFG == VerifyFlowSensitiveRulesDetails::YieldOnceResume, "encountered 'return' before yielding a value in " "yield_once coroutine"); } } } if (isa(term)) { require(state.CFG != VerifyFlowSensitiveRulesDetails::YieldOnceResume, "encountered multiple 'yield's along single path"); require(state.CFG == VerifyFlowSensitiveRulesDetails::Normal, "encountered 'yield' on abnormal CFG path"); } auto successors = term->getSuccessors(); for (auto i : indices(successors)) { SILBasicBlock *succBB = successors[i].getBB(); // Optimistically try to set our current state as the state // of the successor. We can use a move on the final successor; // note that if the insertion fails, the move won't actually // happen, which is important because we'll still need it // to compare against the already-recorded state for the block. auto insertResult = i + 1 == successors.size() ? visitedBBs.try_emplace(succBB, std::move(state)) : visitedBBs.try_emplace(succBB, state); // If the insertion was successful, add the successor to the // worklist and continue. if (insertResult.second) { Worklist.push_back(succBB); // If we're following a 'yield', update the CFG state: if (isa(term)) { // Enforce that the unwind logic is segregated in all stages. if (i == 1) { insertResult.first->second.CFG = VerifyFlowSensitiveRulesDetails::YieldUnwind; // We check the yield_once rule in the mandatory analyses, // so we can't assert it yet in the raw stage. } else if (F->getLoweredFunctionType()->getCoroutineKind() == SILCoroutineKind::YieldOnce && F->getModule().getStage() != SILStage::Raw) { insertResult.first->second.CFG = VerifyFlowSensitiveRulesDetails::YieldOnceResume; } } continue; } // This rule is checked elsewhere, but we'd want to assert it // here anyway. require(!isa(term), "successor of 'yield' should not be encountered twice"); // Check that the stack height is consistent coming from all entry // points into this BB. We only care about consistency if there is // a possible return from this function along the path starting at // this successor bb. (FIXME: Why? Infinite loops should still // preserve consistency...) auto isUnreachable = [&] { SmallPtrSet visited; return isUnreachableAlongAllPathsStartingAt(succBB, visited); }; const auto &foundState = insertResult.first->second; require(state.Stack == foundState.Stack || isUnreachable(), "inconsistent stack heights entering basic block"); require(state.ActiveOps == foundState.ActiveOps || isUnreachable(), "inconsistent active-operations sets entering basic block"); require(state.CFG == foundState.CFG, "inconsistent coroutine states entering basic block"); } } } } } void verifyBranches(const SILFunction *F) { // Verify that there is no non_condbr critical edge. auto isCriticalEdgePred = [](const TermInst *T, unsigned EdgeIdx) { assert(T->getSuccessors().size() > EdgeIdx && "Not enough successors"); // A critical edge has more than one outgoing edges from the source // block. auto SrcSuccs = T->getSuccessors(); if (SrcSuccs.size() <= 1) return false; // And its destination block has more than one predecessor. SILBasicBlock *DestBB = SrcSuccs[EdgeIdx]; assert(!DestBB->pred_empty() && "There should be a predecessor"); if (DestBB->getSinglePredecessorBlock()) return false; return true; }; for (auto &BB : *F) { const TermInst *TI = BB.getTerminator(); CurInstruction = TI; // Check for non-cond_br critical edges. if (isa(TI)) { continue; } for (unsigned Idx = 0, e = BB.getSuccessors().size(); Idx != e; ++Idx) { require(!isCriticalEdgePred(TI, Idx), "non cond_br critical edges not allowed"); } } } void verifyOpenedArchetypes(SILFunction *F) { require(OpenedArchetypes.getFunction() == F, "Wrong SILFunction provided to verifyOpenedArchetypes"); // Check that definitions of all opened archetypes from // OpenedArchetypesDefs are existing instructions // belonging to the function F. for (auto KV: OpenedArchetypes.getOpenedArchetypeDefs()) { require(getOpenedArchetypeOf(CanType(KV.first)), "Only opened archetypes should be registered in SILFunction"); auto Def = cast(KV.second); require(Def->getFunction() == F, "Definition of every registered opened archetype should be an" " existing instruction in a current SILFunction"); } } /// This pass verifies that there are no hole in debug scopes at -Onone. void verifyDebugScopeHoles(SILBasicBlock *BB) { if (!VerifyDIHoles) return; // This check only makes sense at -Onone. Optimizations, // e.g. inlining, can move scopes around. llvm::DenseSet AlreadySeenScopes; if (BB->getParent()->getEffectiveOptimizationMode() != OptimizationMode::NoOptimization) return; // Exit early if this BB is empty. if (BB->empty()) return; const SILDebugScope *LastSeenScope = nullptr; for (SILInstruction &SI : *BB) { if (SI.isMetaInstruction()) continue; LastSeenScope = SI.getDebugScope(); AlreadySeenScopes.insert(LastSeenScope); break; } for (SILInstruction &SI : *BB) { if (SI.isMetaInstruction()) continue; // If we haven't seen this debug scope yet, update the // map and go on. auto *DS = SI.getDebugScope(); assert(DS && "Each instruction should have a debug scope"); if (!AlreadySeenScopes.count(DS)) { AlreadySeenScopes.insert(DS); LastSeenScope = DS; continue; } // Otherwise, we're allowed to re-enter a scope only if // the scope is an ancestor of the scope we're currently leaving. auto isAncestorScope = [](const SILDebugScope *Cur, const SILDebugScope *Previous) { const SILDebugScope *Tmp = Previous; assert(Tmp && "scope can't be null"); while (Tmp) { PointerUnion Parent = Tmp->Parent; auto *ParentScope = Parent.dyn_cast(); if (!ParentScope) break; if (ParentScope == Cur) return true; Tmp = ParentScope; } return false; }; if (isAncestorScope(DS, LastSeenScope)) { LastSeenScope = DS; continue; } if (DS != LastSeenScope) { LLVM_DEBUG(llvm::dbgs() << "Broken instruction!\n"; SI.dump()); LLVM_DEBUG(llvm::dbgs() << "Please report a bug on bugs.swift.org\n"); LLVM_DEBUG(llvm::dbgs() << "Pass -Xllvm -verify-di-holes=false to disable the verification\n"); require( DS == LastSeenScope, "Basic block contains a non-contiguous lexical scope at -Onone"); } } } void visitSILBasicBlock(SILBasicBlock *BB) { // Make sure that each of the successors/predecessors of this basic block // have this basic block in its predecessor/successor list. for (const auto *SuccBB : BB->getSuccessorBlocks()) { require(SuccBB->isPredecessorBlock(BB), "Must be a predecessor of each successor."); } for (const SILBasicBlock *PredBB : BB->getPredecessorBlocks()) { require(PredBB->isSuccessorBlock(BB), "Must be a successor of each predecessor."); } SILInstructionVisitor::visitSILBasicBlock(BB); verifyDebugScopeHoles(BB); } void visitBasicBlockArguments(SILBasicBlock *BB) { CurInstruction = nullptr; for (auto argI = BB->args_begin(), argEnd = BB->args_end(); argI != argEnd; ++argI) visitSILArgument(*argI); } void visitSILBasicBlocks(SILFunction *F) { // Visit all basic blocks in the RPOT order. // This ensures that any open_existential instructions, which // open archetypes, are seen before the uses of these archetypes. llvm::ReversePostOrderTraversal RPOT(F); llvm::DenseSet VisitedBBs; for (auto Iter = RPOT.begin(), E = RPOT.end(); Iter != E; ++Iter) { auto *BB = *Iter; VisitedBBs.insert(BB); visitSILBasicBlock(BB); } // Visit all basic blocks that were not visited during the RPOT traversal, // e.g. unreachable basic blocks. for (auto &BB : *F) { if (VisitedBBs.count(&BB)) continue; visitSILBasicBlock(&BB); } } void visitSILFunction(SILFunction *F) { PrettyStackTraceSILFunction stackTrace("verifying", F); CanSILFunctionType FTy = F->getLoweredFunctionType(); verifySILFunctionType(FTy); if (F->isExternalDeclaration()) { if (F->hasForeignBody()) return; require(F->isAvailableExternally(), "external declaration of internal SILFunction not allowed"); // If F is an external declaration, there is nothing further to do, // return. return; } assert(!F->hasForeignBody()); // Make sure that our SILFunction only has context generic params if our // SILFunctionType is non-polymorphic. if (F->getGenericEnvironment()) { require(FTy->isPolymorphic(), "non-generic function definitions cannot have a " "generic environment"); } else { require(!FTy->isPolymorphic(), "generic function definition must have a generic environment"); } // Otherwise, verify the body of the function. verifyEntryBlock(&*F->getBlocks().begin()); verifyEpilogBlocks(F); verifyFlowSensitiveRules(F); verifyBranches(F); visitSILBasicBlocks(F); // Verify archetypes after all basic blocks are visited, // because we build the map of archetypes as we visit the // instructions. verifyOpenedArchetypes(F); if (F->hasOwnership() && F->shouldVerifyOwnership() && !F->getModule().getASTContext().hadError()) { verifyMemoryLifetime(F); } } void verify() { visitSILFunction(const_cast(&F)); } }; } // end anonymous namespace #undef require #undef requireObjectType //===----------------------------------------------------------------------===// // Out of Line Verifier Run Functions //===----------------------------------------------------------------------===// static bool verificationEnabled(const SILModule &M) { #ifdef NDEBUG if (!M.getOptions().VerifyAll) return false; #endif return !M.getOptions().VerifyNone; } /// verify - Run the SIL verifier to make sure that the SILFunction follows /// invariants. void SILFunction::verify(bool SingleFunction) const { if (!verificationEnabled(getModule())) return; // Please put all checks in visitSILFunction in SILVerifier, not here. This // ensures that the pretty stack trace in the verifier is included with the // back trace when the verifier crashes. SILVerifier(*this, SingleFunction).verify(); } void SILFunction::verifyCriticalEdges() const { if (!verificationEnabled(getModule())) return; SILVerifier(*this, /*SingleFunction=*/true).verifyBranches(this); } /// Verify that a property descriptor follows invariants. void SILProperty::verify(const SILModule &M) const { if (!verificationEnabled(M)) return; auto *decl = getDecl(); auto *dc = decl->getInnermostDeclContext(); // TODO: base type for global/static descriptors auto sig = dc->getGenericSignatureOfContext(); auto baseTy = dc->getInnermostTypeContext()->getSelfInterfaceType() ->getCanonicalType(sig); auto leafTy = decl->getValueInterfaceType()->getCanonicalType(sig); SubstitutionMap subs; if (sig) { auto env = dc->getGenericEnvironmentOfContext(); subs = env->getForwardingSubstitutionMap(); baseTy = env->mapTypeIntoContext(baseTy)->getCanonicalType(); leafTy = env->mapTypeIntoContext(leafTy)->getCanonicalType(); } bool hasIndices = false; if (auto subscript = dyn_cast(decl)) { hasIndices = subscript->getIndices()->size() != 0; } auto canSig = sig.getCanonicalSignature(); auto require = [&](bool reqt, StringRef message) { if (!reqt) { llvm::errs() << message << "\n"; assert(false && "invoking standard assertion failure"); } }; if (auto &component = getComponent()) { verifyKeyPathComponent(const_cast(M), 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, "component type of property descriptor should match type of storage"); } } /// Verify that a vtable follows invariants. void SILVTable::verify(const SILModule &M) const { if (!verificationEnabled(M)) return; for (auto &entry : getEntries()) { // All vtable entries must be decls in a class context. assert(entry.Method.hasDecl() && "vtable entry is not a decl"); auto baseInfo = M.Types.getConstantInfo(TypeExpansionContext::minimal(), entry.Method); ValueDecl *decl = entry.Method.getDecl(); assert((!isa(decl) || !cast(decl)->isObservingAccessor()) && "observing accessors shouldn't have vtable entries"); // For ivar destroyers, the decl is the class itself. ClassDecl *theClass; if (entry.Method.kind == SILDeclRef::Kind::IVarDestroyer) theClass = dyn_cast(decl); else theClass = dyn_cast(decl->getDeclContext()); assert(theClass && "vtable entry must refer to a class member"); // The class context must be the vtable's class, or a superclass thereof. assert(theClass->isSuperclassOf(getClass()) && "vtable entry must refer to a member of the vtable's class"); // Foreign entry points shouldn't appear in vtables. assert(!entry.Method.isForeign && "vtable entry must not be foreign"); // The vtable entry must be ABI-compatible with the overridden vtable slot. SmallString<32> baseName; { llvm::raw_svector_ostream os(baseName); entry.Method.print(os); } if (M.getStage() != SILStage::Lowered) { SILVerifier(*entry.Implementation) .requireABICompatibleFunctionTypes( baseInfo.getSILType().castTo(), entry.Implementation->getLoweredFunctionType(), "vtable entry for " + baseName + " must be ABI-compatible", *entry.Implementation); } } } /// Verify that a witness table follows invariants. void SILWitnessTable::verify(const SILModule &M) const { if (!verificationEnabled(M)) return; if (isDeclaration()) assert(getEntries().empty() && "A witness table declaration should not have any entries."); for (const Entry &E : getEntries()) if (E.getKind() == SILWitnessTable::WitnessKind::Method) { SILFunction *F = E.getMethodWitness().Witness; if (F) { // If a SILWitnessTable is going to be serialized, it must only // reference public or serializable functions. if (isSerialized()) { assert(F->hasValidLinkageForFragileRef() && "Fragile witness tables should not reference " "less visible functions."); } assert(F->getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod && "Witnesses must have witness_method representation."); } } } /// Verify that a default witness table follows invariants. void SILDefaultWitnessTable::verify(const SILModule &M) const { #ifndef NDEBUG for (const Entry &E : getEntries()) { // FIXME: associated type witnesses. if (!E.isValid() || E.getKind() != SILWitnessTable::Method) continue; SILFunction *F = E.getMethodWitness().Witness; #if 0 // FIXME: For now, all default witnesses are private. assert(F->hasValidLinkageForFragileRef() && "Default witness tables should not reference " "less visible functions."); #endif assert(F->getLoweredFunctionType()->getRepresentation() == SILFunctionTypeRepresentation::WitnessMethod && "Default witnesses must have witness_method representation."); } #endif } /// Verify that a global variable follows invariants. void SILGlobalVariable::verify() const { if (!verificationEnabled(getModule())) return; assert(getLoweredType().isObject() && "global variable cannot have address type"); // Verify the static initializer. for (const SILInstruction &I : StaticInitializerBlock) { assert(isValidStaticInitializerInst(&I, getModule()) && "illegal static initializer"); auto init = cast(&I); if (init == &StaticInitializerBlock.back()) { assert(init->use_empty() && "Init value must not have another use"); } else { assert(!init->use_empty() && "dead instruction in static initializer"); assert(!isa(init) && "object instruction is only allowed for final initial value"); } assert(I.getParent() == &StaticInitializerBlock); } } /// Verify the module. void SILModule::verify() const { if (!verificationEnabled(*this)) return; // Uniquing set to catch symbol name collisions. llvm::DenseSet symbolNames; // When merging partial modules, we only link functions from the current // module, without enabling "LinkAll" mode or running the SILLinker pass; // in this case, we need to relax some of the checks. bool SingleFunction = false; if (getOptions().MergePartialModules) SingleFunction = true; // Check all functions. for (const SILFunction &f : *this) { if (!symbolNames.insert(f.getName()).second) { llvm::errs() << "Symbol redefined: " << f.getName() << "!\n"; assert(false && "triggering standard assertion failure routine"); } f.verify(SingleFunction); } // Check all globals. for (const SILGlobalVariable &g : getSILGlobals()) { if (!symbolNames.insert(g.getName()).second) { llvm::errs() << "Symbol redefined: " << g.getName() << "!\n"; assert(false && "triggering standard assertion failure routine"); } g.verify(); } // Check all vtables and the vtable cache. llvm::DenseSet vtableClasses; unsigned EntriesSZ = 0; for (const SILVTable &vt : getVTables()) { if (!vtableClasses.insert(vt.getClass()).second) { llvm::errs() << "Vtable redefined: " << vt.getClass()->getName() << "!\n"; assert(false && "triggering standard assertion failure routine"); } vt.verify(*this); // Check if there is a cache entry for each vtable entry for (auto entry : vt.getEntries()) { if (VTableEntryCache.find({&vt, entry.Method}) == VTableEntryCache.end()) { llvm::errs() << "Vtable entry for function: " << entry.Implementation->getName() << "not in cache!\n"; assert(false && "triggering standard assertion failure routine"); } EntriesSZ++; } } assert(EntriesSZ == VTableEntryCache.size() && "Cache size is not equal to true number of VTable entries"); // Check all witness tables. LLVM_DEBUG(llvm::dbgs() <<"*** Checking witness tables for duplicates ***\n"); llvm::DenseSet wtableConformances; for (const SILWitnessTable &wt : getWitnessTables()) { LLVM_DEBUG(llvm::dbgs() << "Witness Table:\n"; wt.dump()); auto conformance = wt.getConformance(); if (!wtableConformances.insert(conformance).second) { llvm::errs() << "Witness table redefined: "; conformance->printName(llvm::errs()); assert(false && "triggering standard assertion failure routine"); } wt.verify(*this); } // Check all default witness tables. LLVM_DEBUG(llvm::dbgs() << "*** Checking default witness tables for " "duplicates ***\n"); llvm::DenseSet defaultWitnessTables; for (const SILDefaultWitnessTable &wt : getDefaultWitnessTables()) { LLVM_DEBUG(llvm::dbgs() << "Default Witness Table:\n"; wt.dump()); if (!defaultWitnessTables.insert(wt.getProtocol()).second) { llvm::errs() << "Default witness table redefined: "; wt.dump(); assert(false && "triggering standard assertion failure routine"); } wt.verify(*this); } // Check property descriptors. LLVM_DEBUG(llvm::dbgs() << "*** Checking property descriptors ***\n"); for (auto &prop : getPropertyList()) { prop.verify(*this); } }