| //===--- 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/ProtocolConformance.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/Range.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| #include "swift/SIL/Dominance.h" |
| #include "swift/SIL/DynamicCasts.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/SILVisitor.h" |
| #include "swift/SIL/BasicBlockUtils.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<bool> SkipUnreachableMustBeLastErrors( |
| "verify-skip-unreachable-must-be-last", |
| 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) { |
| if (!A->getOpenedExistentialType().isNull()) |
| return true; |
| |
| // Find the primary archetype. |
| A = A->getPrimary(); |
| |
| // Ok, we have a primary archetype, make sure it is in the nested generic |
| // environment of our caller. |
| if (auto *genericEnv = F->getGenericEnvironment()) |
| if (genericEnv->containsPrimaryArchetype(A)) |
| return true; |
| |
| return false; |
| } |
| |
| namespace { |
| |
| /// Metaprogramming-friendly base class. |
| template <class Impl> |
| class SILVerifierBase : public SILVisitor<Impl> { |
| public: |
| // visitCLASS calls visitPARENT and checkCLASS. |
| // checkCLASS does nothing by default. |
| #define VALUE(CLASS, PARENT) \ |
| void visit##CLASS(CLASS *I) { \ |
| static_cast<Impl*>(this)->visit##PARENT(I); \ |
| static_cast<Impl*>(this)->check##CLASS(I); \ |
| } \ |
| void check##CLASS(CLASS *I) {} |
| #include "swift/SIL/SILNodes.def" |
| |
| void visitValueBase(ValueBase *V) { |
| static_cast<Impl*>(this)->checkValueBase(V); |
| } |
| void checkValueBase(ValueBase *V) {} |
| }; |
| } // end anonymous namespace |
| |
| namespace { |
| |
| /// The SIL verifier walks over a SIL function / basic block / instruction, |
| /// checking and enforcing its invariants. |
| class SILVerifier : public SILVerifierBase<SILVerifier> { |
| ModuleDecl *M; |
| const SILFunction &F; |
| SILFunctionConventions fnConv; |
| Lowering::TypeConverter &TC; |
| SILOpenedArchetypesTracker OpenedArchetypes; |
| SmallVector<StringRef, 16> DebugVars; |
| const SILInstruction *CurInstruction = nullptr; |
| DominanceInfo *Dominance = nullptr; |
| DeadEndBlocks DEBlocks; |
| bool SingleFunction = true; |
| |
| SILVerifier(const SILVerifier&) = delete; |
| void operator=(const SILVerifier&) = delete; |
| public: |
| bool isSILOwnershipEnabled() const { |
| return F.getModule().getOptions().EnableSILOwnership; |
| } |
| |
| void _require(bool condition, const Twine &complaint, |
| const std::function<void()> &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()); |
| llvm::dbgs() << "In function:\n"; |
| F.print(llvm::dbgs()); |
| } else { |
| llvm::dbgs() << "In function:\n"; |
| F.print(llvm::dbgs()); |
| } |
| |
| abort(); |
| } |
| #define require(condition, complaint) \ |
| _require(bool(condition), complaint ": " #condition) |
| |
| template <class T> typename CanTypeWrapperTraits<T>::type |
| _requireObjectType(SILType type, const Twine &valueDescription, |
| const char *typeName) { |
| _require(type.isObject(), valueDescription + " must be an object"); |
| auto result = type.getAs<T>(); |
| _require(bool(result), valueDescription + " must have type " + typeName); |
| return result; |
| } |
| template <class T> typename CanTypeWrapperTraits<T>::type |
| _requireObjectType(SILValue value, const Twine &valueDescription, |
| const char *typeName) { |
| return _requireObjectType<T>(value->getType(), valueDescription, typeName); |
| } |
| #define requireObjectType(type, value, valueDescription) \ |
| _requireObjectType<type>(value, valueDescription, #type) |
| |
| template <class T> typename CanTypeWrapperTraits<T>::type |
| _requireAddressType(SILType type, const Twine &valueDescription, |
| const char *typeName) { |
| _require(type.isAddress(), valueDescription + " must be an address"); |
| auto result = type.getAs<T>(); |
| _require(bool(result), valueDescription + " must have type " + typeName); |
| return result; |
| } |
| template <class T> typename CanTypeWrapperTraits<T>::type |
| _requireAddressType(SILValue value, const Twine &valueDescription, |
| const char *typeName) { |
| return _requireAddressType<T>(value->getType(), valueDescription, typeName); |
| } |
| #define requireAddressType(type, value, valueDescription) \ |
| _requireAddressType<type>(value, valueDescription, #type) |
| |
| template <class T> |
| typename CanTypeWrapperTraits<T>::type |
| _forbidObjectType(SILType type, const Twine &valueDescription, |
| const char *typeName) { |
| _require(type.isObject(), valueDescription + " must be an object"); |
| auto result = type.getAs<T>(); |
| _require(!bool(result), |
| valueDescription + " must not have type " + typeName); |
| return result; |
| } |
| template <class T> |
| typename CanTypeWrapperTraits<T>::type |
| _forbidObjectType(SILValue value, const Twine &valueDescription, |
| const char *typeName) { |
| return _forbidObjectType<T>(value->getType(), valueDescription, typeName); |
| } |
| #define forbidObjectType(type, value, valueDescription) \ |
| _forbidObjectType<type>(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().unwrapAnyOptionalType(); |
| |
| 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<SILFunctionType>(), |
| valueDescription + " cannot apply to a function type"); |
| } |
| |
| /// 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) { |
| |
| auto complain = [=](const char *msg) -> std::function<void()> { |
| return [=]{ |
| llvm::dbgs() << " " << msg << '\n' |
| << " " << type1 << "\n " << type2 << '\n'; |
| }; |
| }; |
| auto complainBy = [=](std::function<void()> msg) -> std::function<void()> { |
| return [=]{ |
| msg(); |
| llvm::dbgs() << '\n'; |
| llvm::dbgs() << " " << type1 << "\n " << type2 << '\n'; |
| }; |
| }; |
| |
| // The calling convention and function representation can't be changed. |
| _require(type1->getRepresentation() == type2->getRepresentation(), what, |
| complain("Different function representations")); |
| |
| // TODO: We should compare generic signatures. Class and witness methods |
| // allow variance in "self"-fulfilled parameters; other functions must |
| // match exactly. |
| |
| // TODO: More sophisticated param and return ABI compatibility rules could |
| // diverge. |
| std::function<bool (SILType, SILType)> |
| areABICompatibleParamsOrReturns = [&](SILType a, SILType b) -> bool { |
| // Address parameters are all ABI-compatible, though the referenced |
| // values may not be. Assume whoever's doing this knows what they're |
| // doing. |
| if (a.isAddress() && b.isAddress()) |
| return true; |
| // Addresses aren't compatible with values. |
| // TODO: An exception for pointerish types? |
| else if (a.isAddress() || b.isAddress()) |
| return false; |
| |
| // Tuples are ABI compatible if their elements are. |
| // TODO: Should destructure recursively. |
| SmallVector<CanType, 1> aElements, bElements; |
| if (auto tup = a.getAs<TupleType>()) { |
| auto types = tup.getElementTypes(); |
| aElements.append(types.begin(), types.end()); |
| } else { |
| aElements.push_back(a.getSwiftRValueType()); |
| } |
| if (auto tup = b.getAs<TupleType>()) { |
| auto types = tup.getElementTypes(); |
| bElements.append(types.begin(), types.end()); |
| } else { |
| bElements.push_back(b.getSwiftRValueType()); |
| } |
| |
| if (aElements.size() != bElements.size()) |
| return false; |
| |
| for (unsigned i : indices(aElements)) { |
| auto aa = SILType::getPrimitiveObjectType(aElements[i]), |
| bb = SILType::getPrimitiveObjectType(bElements[i]); |
| // Equivalent types are always ABI-compatible. |
| if (aa == bb) |
| continue; |
| |
| // FIXME: If one or both types are dependent, we can't accurately assess |
| // whether they're ABI-compatible without a generic context. We can |
| // do a better job here when dependent types are related to their |
| // generic signatures. |
| if (aa.hasTypeParameter() || bb.hasTypeParameter()) |
| continue; |
| |
| // Bridgeable object types are interchangeable. |
| if (aa.isBridgeableObjectType() && bb.isBridgeableObjectType()) |
| continue; |
| |
| // Optional and IUO are interchangeable if their elements are. |
| auto aObject = aa.getAnyOptionalObjectType(); |
| auto bObject = bb.getAnyOptionalObjectType(); |
| if (aObject && bObject |
| && areABICompatibleParamsOrReturns(aObject, bObject)) |
| continue; |
| // Optional objects are ABI-interchangeable with non-optionals; |
| // None is represented by a null pointer. |
| if (aObject && aObject.isBridgeableObjectType() |
| && bb.isBridgeableObjectType()) |
| continue; |
| if (bObject && bObject.isBridgeableObjectType() |
| && aa.isBridgeableObjectType()) |
| continue; |
| |
| // Optional thick metatypes are ABI-interchangeable with non-optionals |
| // too. |
| if (aObject) |
| if (auto aObjMeta = aObject.getAs<MetatypeType>()) |
| if (auto bMeta = bb.getAs<MetatypeType>()) |
| if (aObjMeta->getRepresentation() == bMeta->getRepresentation() |
| && bMeta->getRepresentation() != MetatypeRepresentation::Thin) |
| continue; |
| if (bObject) |
| if (auto aMeta = aa.getAs<MetatypeType>()) |
| if (auto bObjMeta = bObject.getAs<MetatypeType>()) |
| if (aMeta->getRepresentation() == bObjMeta->getRepresentation() |
| && aMeta->getRepresentation() != MetatypeRepresentation::Thin) |
| continue; |
| |
| // Function types are interchangeable if they're also ABI-compatible. |
| if (auto aFunc = aa.getAs<SILFunctionType>()) |
| if (auto bFunc = bb.getAs<SILFunctionType>()) { |
| // FIXME |
| requireABICompatibleFunctionTypes(aFunc, bFunc, what); |
| return true; |
| } |
| |
| // Metatypes are interchangeable with metatypes with the same |
| // representation. |
| if (auto aMeta = aa.getAs<MetatypeType>()) |
| if (auto bMeta = bb.getAs<MetatypeType>()) |
| if (aMeta->getRepresentation() == bMeta->getRepresentation()) |
| continue; |
| |
| // Other types must match exactly. |
| return false; |
| } |
| return true; |
| }; |
| |
| // Check the results. |
| _require(type1->getNumResults() == type2->getNumResults(), what, |
| complain("different number of results")); |
| for (unsigned i : indices(type1->getResults())) { |
| auto result1 = type1->getResults()[i]; |
| auto result2 = type2->getResults()[i]; |
| |
| _require(result1.getConvention() == result2.getConvention(), what, |
| complain("Different return value conventions")); |
| _require(areABICompatibleParamsOrReturns(result1.getSILStorageType(), |
| result2.getSILStorageType()), |
| what, complain("ABI-incompatible return values")); |
| } |
| |
| // Our error result conventions are designed to be ABI compatible |
| // with functions lacking error results. Just make sure that the |
| // actual conventions match up. |
| if (type1->hasErrorResult() && type2->hasErrorResult()) { |
| auto error1 = type1->getErrorResult(); |
| auto error2 = type2->getErrorResult(); |
| _require(error1.getConvention() == error2.getConvention(), what, |
| complain("Different error result conventions")); |
| _require(areABICompatibleParamsOrReturns(error1.getSILStorageType(), |
| error2.getSILStorageType()), |
| what, complain("ABI-incompatible error results")); |
| } |
| |
| // Check the parameters. |
| // TODO: Could allow known-empty types to be inserted or removed, but SIL |
| // doesn't know what empty types are yet. |
| |
| _require(type1->getParameters().size() == type2->getParameters().size(), |
| what, complain("different number of parameters")); |
| for (unsigned i : indices(type1->getParameters())) { |
| auto param1 = type1->getParameters()[i]; |
| auto param2 = type2->getParameters()[i]; |
| |
| _require(param1.getConvention() == param2.getConvention(), what, |
| complainBy([=] { |
| llvm::dbgs() << "Different conventions for parameter " << i; |
| })); |
| _require(areABICompatibleParamsOrReturns(param1.getSILStorageType(), |
| param2.getSILStorageType()), |
| what, complainBy([=] { |
| llvm::dbgs() << "ABI-incompatible types for parameter " << i; |
| })); |
| } |
| } |
| |
| 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"); |
| } |
| } |
| |
| SILVerifier(const SILFunction &F, bool SingleFunction = true) |
| : M(F.getModule().getSwiftModule()), F(F), |
| fnConv(F.getLoweredFunctionType(), F.getModule()), |
| TC(F.getModule().Types), OpenedArchetypes(&F), Dominance(nullptr), |
| 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. |
| for (auto &BB : F) { |
| require(!BB.empty(), "Basic blocks cannot be empty"); |
| require(isa<TermInst>(BB.back()), |
| "Basic blocks must end with a terminator instruction"); |
| } |
| |
| Dominance = new DominanceInfo(const_cast<SILFunction *>(&F)); |
| |
| auto *DebugScope = F.getDebugScope(); |
| require(DebugScope, "All SIL functions must have a debug scope"); |
| require(DebugScope->Parent.get<SILFunction *>() == &F, |
| "Scope of SIL function points to different function"); |
| } |
| |
| ~SILVerifier() { |
| if (Dominance) |
| delete Dominance; |
| } |
| |
| void visitSILArgument(SILArgument *arg) { |
| checkLegalType(arg->getFunction(), arg, nullptr); |
| checkValueBaseOwnership(arg); |
| } |
| |
| void visitSILInstruction(SILInstruction *I) { |
| CurInstruction = I; |
| OpenedArchetypes.registerOpenedArchetypes(I); |
| checkSILInstruction(I); |
| |
| // Check the SILLLocation attached to the instruction. |
| checkInstructionsSILLocation(I); |
| |
| checkLegalType(I->getFunction(), I, I); |
| |
| // Check ownership. |
| SILFunction *F = I->getFunction(); |
| assert(F && "Expected value base with parent function"); |
| |
| // Check ownership. |
| checkValueBaseOwnership(I); |
| } |
| |
| void checkValueBaseOwnership(ValueBase *V) { |
| // If ownership is not enabled, bail. |
| if (!isSILOwnershipEnabled()) |
| return; |
| |
| // If V does not have a value, bail. |
| if (!V->hasValue()) |
| 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->hasQualifiedOwnership()) |
| return; |
| SILValue(V).verifyOwnership(F->getModule(), &DEBlocks); |
| } |
| |
| void checkSILInstruction(SILInstruction *I) { |
| const SILBasicBlock *BB = I->getParent(); |
| require(BB, "Instruction with null parent"); |
| |
| // Check that non-terminators look ok. |
| if (!isa<TermInst>(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<UnreachableInst>(I) || !SkipUnreachableMustBeLastErrors) |
| require(&*BB->rbegin() == I, |
| "Terminator must be the last in block"); |
| } |
| |
| // Verify that all of our uses are in this function. |
| for (Operand *use : I->getUses()) { |
| auto user = use->getUser(); |
| require(user, "instruction user is null?"); |
| require(isa<SILInstruction>(user), |
| "instruction used by non-instruction"); |
| auto userI = cast<SILInstruction>(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 = dyn_cast<SILInstruction>(operand.get())) { |
| 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(Dominance->properlyDominates(valueI, I), |
| "instruction isn't dominated by its operand"); |
| } |
| } |
| |
| if (auto *valueBBA = dyn_cast<SILArgument>(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<SILInstruction>(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(); |
| ValueKind 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) { |
| SILDebugVariable VarInfo; |
| if (auto *DI = dyn_cast<AllocStackInst>(I)) { |
| VarInfo = DI->getVarInfo(); |
| } else if (auto *DI = dyn_cast<AllocBoxInst>(I)) { |
| VarInfo = DI->getVarInfo(); |
| } else if (auto *DI = dyn_cast<DebugValueInst>(I)) { |
| VarInfo = DI->getVarInfo(); |
| } else if (auto *DI = dyn_cast<DebugValueAddrInst>(I)) { |
| VarInfo = DI->getVarInfo(); |
| } |
| |
| if (unsigned ArgNo = VarInfo.ArgNo) { |
| // It is a function argument. |
| if (ArgNo < DebugVars.size() && !DebugVars[ArgNo].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 0 |
| // FIXME: This check was tautological before the removal of |
| // AutoreleaseReturnInst, and it turns out that we're violating it. |
| // Fix incoming. |
| if (LocKind == SILLocation::CleanupKind || |
| LocKind == SILLocation::InlinedKind) |
| require(InstKind != ValueKind::ReturnInst || |
| InstKind != ValueKind::AutoreleaseReturnInst, |
| "cleanup and inlined locations are not allowed on return instructions"); |
| #endif |
| |
| if (LocKind == SILLocation::ReturnKind || |
| LocKind == SILLocation::ImplicitReturnKind) |
| require(InstKind == ValueKind::BranchInst || |
| InstKind == ValueKind::ReturnInst || |
| InstKind == ValueKind::UnreachableInst, |
| "return locations are only allowed on branch and return instructions"); |
| |
| if (LocKind == SILLocation::ArtificialUnreachableKind) |
| require(InstKind == ValueKind::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) { |
| if (SILType type = value->getType()) { |
| checkLegalType(F, type, I); |
| } |
| } |
| |
| /// Check that the given type is a legal SIL value type. |
| void checkLegalType(SILFunction *F, SILType type, SILInstruction *I) { |
| checkLegalSILType(F, type.getSwiftRValueType(), 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<LValueType>(rvalueType), |
| "l-value types are not legal in SIL"); |
| require(!isa<AnyFunctionType>(rvalueType), |
| "AST function types are not legal in SIL"); |
| |
| // Tuples should have had their element lowered. |
| if (auto tuple = dyn_cast<TupleType>(rvalueType)) { |
| for (auto eltTy : tuple.getElementTypes()) { |
| checkLegalSILType(F, eltTy, I); |
| } |
| return; |
| } |
| |
| // Optionals should have had their objects lowered. |
| OptionalTypeKind optKind; |
| if (auto objectType = rvalueType.getAnyOptionalObjectType(optKind)) { |
| require(optKind == OTK_Optional, |
| "ImplicitlyUnwrappedOptional is not legal in SIL values"); |
| return checkLegalSILType(F, objectType, I); |
| } |
| |
| // Metatypes should have explicit representations. |
| if (auto metatype = dyn_cast<AnyMetatypeType>(rvalueType)) { |
| require(metatype->hasRepresentation(), |
| "metatypes in SIL must have a representation");; |
| // fallthrough for archetype check |
| } |
| |
| rvalueType.visit([&](CanType t) { |
| auto A = dyn_cast<ArchetypeType>(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 || |
| Dominance->properlyDominates(cast<SILInstruction>(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().getSwiftRValueType()); |
| |
| // 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().getSwiftRValueType()); |
| 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].getSwiftRValueType()); |
| require(Counts[Idx].get()->getType().is<BuiltinIntegerType>(), |
| "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<AnyMetatypeType>(), |
| "operand of alloc_ref_dynamic must be of metatype type"); |
| auto metaTy = Metadata->getType().castTo<AnyMetatypeType>(); |
| 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(SubstitutionList subs, |
| SILType calleeTy) { |
| auto fnTy = requireObjectType(SILFunctionType, calleeTy, "callee operand"); |
| |
| // If there are substitutions, verify them and apply them to the callee. |
| if (subs.empty()) { |
| 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 sub : subs) { |
| sub.getReplacement()->getCanonicalType().visit([&](CanType t) { |
| auto A = dyn_cast<ArchetypeType>(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."); |
| }); |
| } |
| |
| // Apply the substitutions. |
| return fnTy->substGenericArgs(F.getModule(), subs); |
| } |
| |
| /// 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<ArchetypeType *> FoundOpenedArchetypes; |
| unsigned hasDynamicSelf = 0; |
| |
| // Function to collect opened archetypes in FoundOpenedArchetypes and set |
| // hasDynamicSelf. |
| auto HandleType = [&](CanType Ty) { |
| if (Ty->isOpenedExistential()) { |
| auto A = cast<ArchetypeType>(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 || |
| Dominance->properlyDominates(cast<SILInstruction>(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 &Sub : AS.getSubstitutions()) { |
| Sub.getReplacement()->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<SILArgument>(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<SILInstruction>(V), |
| "opened archetype operand should refer to a SIL instruction"); |
| auto Archetype = getOpenedArchetypeOf(cast<SILInstruction>(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.getSubstitutions(), |
| 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.getNumCallArguments() == substConv.getNumSILArguments(), |
| "apply doesn't have right number of arguments for function"); |
| for (size_t i = 0, size = site.getNumCallArguments(); 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()); |
| require(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"); |
| } |
| |
| // 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<UnreachableInst>(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()); |
| |
| 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 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<SILArgument>(BI->getArguments()[3]), |
| "alignment argument of memory intrinsics must be an integer " |
| "literal"); |
| require(!isa<SILArgument>(BI->getArguments()[4]), |
| "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<SILArgument>(BI->getArguments()[0]), |
| "size argument of memory use markers must be an integer literal"); |
| break; |
| case llvm::Intrinsic::invariant_end: |
| require(!isa<SILArgument>(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->getSubstitutions(), |
| 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) { |
| require(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.getType(), |
| 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.getType(), |
| 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<SILFunctionType>() |
| ->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<SILBoxType>()) |
| return true; |
| if (t.getSwiftRValueType() == 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); |
| } |
| |
| void checkFunctionRefInst(FunctionRefInst *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->getReferencedFunction(); |
| |
| // A direct reference to a shared_external declaration is an error; we |
| // should have deserialized a body. |
| 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 checkAllocGlobalInst(AllocGlobalInst *AGI) { |
| if (F.isSerialized()) { |
| SILGlobalVariable *RefG = AGI->getReferencedGlobal(); |
| 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(); |
| require(GAI->getType().getObjectType() == RefG->getLoweredType(), |
| "global_addr/value must be the type of the variable it references"); |
| 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<BuiltinIntegerType>(), |
| "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->getModule()), |
| "Load must have a loadable type"); |
| require(LI->getOperand()->getType().isAddress(), |
| "Load operand must be an address"); |
| require(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.hasUnqualifiedOwnership(), |
| "Load with unqualified ownership in a qualified function"); |
| break; |
| case LoadOwnershipQualifier::Copy: |
| case LoadOwnershipQualifier::Take: |
| require(F.hasQualifiedOwnership(), |
| "Load with qualified ownership in an unqualified function"); |
| // TODO: Could probably make this a bit stricter. |
| require(!LI->getType().isTrivial(LI->getModule()), |
| "load [copy] or load [take] can only be applied to non-trivial " |
| "types"); |
| break; |
| case LoadOwnershipQualifier::Trivial: |
| require(F.hasQualifiedOwnership(), |
| "Load with qualified ownership in an unqualified function"); |
| require(LI->getType().isTrivial(LI->getModule()), |
| "A load with trivial ownership must load a trivial type"); |
| break; |
| } |
| } |
| |
| void checkLoadBorrowInst(LoadBorrowInst *LBI) { |
| require( |
| F.hasQualifiedOwnership(), |
| "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->getModule()), |
| "Load must have a loadable type"); |
| require(LBI->getOperand()->getType().isAddress(), |
| "Load operand must be an address"); |
| require(LBI->getOperand()->getType().getObjectType() == LBI->getType(), |
| "Load operand type and result type mismatch"); |
| } |
| |
| void checkEndBorrowInst(EndBorrowInst *EBI) { |
| require( |
| F.hasQualifiedOwnership(), |
| "Inst with qualified ownership in a function that is not qualified"); |
| // We allow for end_borrow to express relationships in between addresses and |
| // values, but we require that the types are the same ignoring value |
| // category. |
| require(EBI->getBorrowedValue()->getType().getObjectType() == |
| EBI->getOriginalValue()->getType().getObjectType(), |
| "end_borrow can only relate the same types ignoring value " |
| "category"); |
| } |
| |
| void checkBeginAccessInst(BeginAccessInst *BAI) { |
| auto op = BAI->getOperand(); |
| requireSameType(BAI->getType(), op->getType(), |
| "result must be same type as operand"); |
| require(BAI->getType().isAddress(), |
| "begin_access operand must have address type"); |
| |
| if (BAI->getModule().getStage() != SILStage::Raw) { |
| require(BAI->getEnforcement() != SILAccessEnforcement::Unknown, |
| "access must have known enforcement outside raw stage"); |
| } |
| |
| 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; |
| } |
| } |
| |
| void checkEndAccessInst(EndAccessInst *EAI) { |
| auto BAI = dyn_cast<BeginAccessInst>(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 *I) { |
| require(I->getEnforcement() != SILAccessEnforcement::Unknown, |
| "unpaired access can never use unknown enforcement"); |
| require(I->getSource()->getType().isAddress(), |
| "address operand must have address type"); |
| requireAddressType(BuiltinUnsafeValueBufferType, I->getBuffer(), |
| "scratch buffer operand"); |
| } |
| |
| void checkEndUnpairedAccessInst(EndUnpairedAccessInst *I) { |
| require(I->getEnforcement() != SILAccessEnforcement::Unknown, |
| "unpaired access can never use unknown enforcement"); |
| requireAddressType(BuiltinUnsafeValueBufferType, I->getBuffer(), |
| "scratch buffer operand"); |
| } |
| |
| void checkStoreInst(StoreInst *SI) { |
| require(SI->getSrc()->getType().isObject(), |
| "Can't store from an address source"); |
| require(!fnConv.useLoweredAddresses() |
| || SI->getSrc()->getType().isLoadable(SI->getModule()), |
| "Can't store a non loadable type"); |
| require(SI->getDest()->getType().isAddress(), |
| "Must store to an address dest"); |
| require(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.hasUnqualifiedOwnership(), |
| "Qualified store in function with unqualified ownership?!"); |
| break; |
| case StoreOwnershipQualifier::Init: |
| case StoreOwnershipQualifier::Assign: |
| require( |
| F.hasQualifiedOwnership(), |
| "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->getModule()), |
| "store [init] or store [assign] can only be applied to " |
| "non-trivial types"); |
| break; |
| case StoreOwnershipQualifier::Trivial: { |
| require( |
| F.hasQualifiedOwnership(), |
| "Inst with qualified ownership in a function that is not qualified"); |
| SILValue Src = SI->getSrc(); |
| require(Src->getType().isTrivial(SI->getModule()) || |
| Src.getOwnershipKind() == ValueOwnershipKind::Trivial, |
| "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"); |
| require(Dest->getType().getObjectType() == Src->getType(), |
| "Store operand type and dest type mismatch"); |
| } |
| |
| void checkLoadUnownedInst(LoadUnownedInst *LUI) { |
| require(LUI->getType().isObject(), "Result of load must be an object"); |
| auto PointerType = LUI->getOperand()->getType(); |
| auto PointerRVType = PointerType.getSwiftRValueType(); |
| require(PointerType.isAddress() && |
| PointerRVType->is<UnownedStorageType>(), |
| "load_unowned operand must be an unowned address"); |
| require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == |
| LUI->getType().getSwiftRValueType(), |
| "Load operand type and result type mismatch"); |
| } |
| |
| void checkStoreUnownedInst(StoreUnownedInst *SUI) { |
| require(SUI->getSrc()->getType().isObject(), |
| "Can't store from an address source"); |
| auto PointerType = SUI->getDest()->getType(); |
| auto PointerRVType = PointerType.getSwiftRValueType(); |
| require(PointerType.isAddress() && |
| PointerRVType->is<UnownedStorageType>(), |
| "store_unowned address operand must be an unowned address"); |
| require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == |
| SUI->getSrc()->getType().getSwiftRValueType(), |
| "Store operand type and dest type mismatch"); |
| } |
| |
| void checkLoadWeakInst(LoadWeakInst *LWI) { |
| require(LWI->getType().isObject(), "Result of load must be an object"); |
| require(LWI->getType().getAnyOptionalObjectType(), |
| "Result of weak load must be an optional"); |
| auto PointerType = LWI->getOperand()->getType(); |
| auto PointerRVType = PointerType.getSwiftRValueType(); |
| require(PointerType.isAddress() && |
| PointerRVType->is<WeakStorageType>(), |
| "load_weak operand must be a weak address"); |
| require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == |
| LWI->getType().getSwiftRValueType(), |
| "Load operand type and result type mismatch"); |
| } |
| |
| void checkStoreWeakInst(StoreWeakInst *SWI) { |
| require(SWI->getSrc()->getType().isObject(), |
| "Can't store from an address source"); |
| require(SWI->getSrc()->getType().getAnyOptionalObjectType(), |
| "store_weak must be of an optional value"); |
| auto PointerType = SWI->getDest()->getType(); |
| auto PointerRVType = PointerType.getSwiftRValueType(); |
| require(PointerType.isAddress() && |
| PointerRVType->is<WeakStorageType>(), |
| "store_weak address operand must be a weak address"); |
| require(PointerRVType->getReferenceStorageReferent()->getCanonicalType() == |
| SWI->getSrc()->getType().getSwiftRValueType(), |
| "Store operand type and dest type mismatch"); |
| } |
| |
| 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() |
| .getSwiftRValueType() |
| ->getClassOrBoundGenericClass() || |
| Src->getType().getAs<SILBoxType>(), |
| "mark_uninitialized must be an address, class, or box type"); |
| require(Src->getType() == MU->getType(),"operand and result type mismatch"); |
| #if 0 |
| // This will be turned back on in a couple of commits. |
| require(isa<AllocationInst>(Src) || isa<SILArgument>(Src), |
| "Mark Uninitialized should always be on the storage location"); |
| #endif |
| } |
| |
| void checkMarkUninitializedBehaviorInst(MarkUninitializedBehaviorInst *MU) { |
| require(MU->getModule().getStage() == SILStage::Raw, |
| "mark_uninitialized instruction can only exist in raw SIL"); |
| auto InitStorage = MU->getInitStorageFunc(); |
| auto InitStorageTy = InitStorage->getType().getAs<SILFunctionType>(); |
| require(InitStorageTy, |
| "mark_uninitialized initializer must be a function"); |
| auto SubstInitStorageTy = InitStorageTy->substGenericArgs(F.getModule(), |
| MU->getInitStorageSubstitutions()); |
| // FIXME: Destructured value or results? |
| require(SubstInitStorageTy->getResults().size() == 1, |
| "mark_uninitialized initializer must have one result"); |
| auto StorageTy = SILType::getPrimitiveAddressType( |
| SubstInitStorageTy->getSingleResult().getType()); |
| requireSameType(StorageTy, MU->getStorage()->getType(), |
| "storage must be address of initializer's result type"); |
| |
| auto Setter = MU->getSetterFunc(); |
| auto SetterTy = Setter->getType().getAs<SILFunctionType>(); |
| require(SetterTy, |
| "mark_uninitialized setter must be a function"); |
| auto SubstSetterTy = SetterTy->substGenericArgs(F.getModule(), |
| MU->getSetterSubstitutions()); |
| require(SubstSetterTy->getParameters().size() == 2, |
| "mark_uninitialized setter must have a value and self param"); |
| requireSameType(fnConv.getSILType(SubstSetterTy->getSelfParameter()), |
| MU->getSelf()->getType(), |
| "self type must match setter's self parameter type"); |
| |
| auto ValueTy = SubstInitStorageTy->getParameters()[0].getType(); |
| requireSameType(SILType::getPrimitiveAddressType(ValueTy), |
| SILType::getPrimitiveAddressType( |
| SubstSetterTy->getParameters()[0].getType()), |
| "value parameter type must match between initializer " |
| "and setter"); |
| |
| auto ValueAddrTy = SILType::getPrimitiveAddressType(ValueTy); |
| requireSameType(ValueAddrTy, MU->getType(), |
| "result of mark_uninitialized_behavior should be address " |
| "of value parameter to setter and initializer"); |
| } |
| |
| 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"); |
| require(SI->getDest()->getType() == SI->getSrc()->getType(), |
| "Store operand type and dest type mismatch"); |
| } |
| |
| void checkRetainValueInst(RetainValueInst *I) { |
| require(I->getOperand()->getType().isObject(), |
| "Source value should be an object value"); |
| require(F.hasUnqualifiedOwnership(), |
| "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.hasUnqualifiedOwnership(), |
| "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(!fnConv.useLoweredAddresses() || F.hasQualifiedOwnership(), |
| "copy_value is only valid in functions with qualified " |
| "ownership"); |
| } |
| |
| void checkCopyUnownedValueInst(CopyUnownedValueInst *I) { |
| auto unownedType = requireObjectType(UnownedStorageType, I->getOperand(), |
| "Operand of unowned_retain"); |
| require(unownedType->isLoadable(ResilienceExpansion::Maximal), |
| "unowned_retain requires unowned type to be loadable"); |
| require(F.hasQualifiedOwnership(), |
| "copy_unowned_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(!fnConv.useLoweredAddresses() || F.hasQualifiedOwnership(), |
| "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.hasUnqualifiedOwnership(), |
| "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.hasUnqualifiedOwnership(), |
| "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"); |
| require(I->getOperand()->getType() == I->getType(), |
| "result of copy_block should be same type as operand"); |
| } |
| |
| void checkAllocValueBufferInst(AllocValueBufferInst *I) { |
| require(I->getOperand()->getType().isAddress(), |
| "Operand value should be an address"); |
| require(I->getOperand()->getType().is<BuiltinUnsafeValueBufferType>(), |
| "Operand value should be a Builtin.UnsafeValueBuffer"); |
| verifyOpenedArchetype(I, I->getValueType().getSwiftRValueType()); |
| } |
| |
| void checkProjectValueBufferInst(ProjectValueBufferInst *I) { |
| require(I->getOperand()->getType().isAddress(), |
| "Operand value should be an address"); |
| require(I->getOperand()->getType().is<BuiltinUnsafeValueBufferType>(), |
| "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<SILBoxType>(); |
| require(boxTy, "project_box operand should be a @box type"); |
| require(I->getType() == boxTy->getFieldType(F.getModule(), |
| 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<MarkUninitializedInst>(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(F.getModule(), |
| 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<AllocExistentialBoxInst>(PEBI->getOperand())) { |
| // The lowered type must be the properly-abstracted form of the AST type. |
| SILType exType = AEBI->getExistentialType(); |
| auto archetype = ArchetypeType::getOpened(exType.getSwiftRValueType()); |
| |
| auto loweredTy = F.getModule().Types.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<BuiltinUnsafeValueBufferType>(), |
| "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(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()); |
| if (SI->getModule().getStage() != SILStage::Lowered) { |
| require((*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()); |
| require(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()); |
| 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()); |
| require(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()); |
| require(caseTy == UI->getType(), |
| "UncheckedTakeEnumDataAddrInst result does not match type of enum case"); |
| } |
| |
| void checkInjectEnumAddrInst(InjectEnumAddrInst *IUAI) { |
| require(IUAI->getOperand()->getType().is<EnumType>() |
| || IUAI->getOperand()->getType().is<BoundGenericEnumType>(), |
| "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) { |
| require(TI->getElement(i)->getType().getSwiftRValueType() == |
| 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.getModule(), formalType); |
| } |
| |
| void checkMetatypeInst(MetatypeInst *MI) { |
| require(MI->getType().is<MetatypeType>(), |
| "metatype instruction must be of metatype type"); |
| auto MetaTy = MI->getType().castTo<MetatypeType>(); |
| require(MetaTy->hasRepresentation(), |
| "metatype instruction must have a metatype representation"); |
| verifyOpenedArchetype(MI, MetaTy.getInstanceType()); |
| } |
| void checkValueMetatypeInst(ValueMetatypeInst *MI) { |
| require(MI->getType().is<MetatypeType>(), |
| "value_metatype instruction must be of metatype type"); |
| require(MI->getType().castTo<MetatypeType>()->hasRepresentation(), |
| "value_metatype instruction must have a metatype representation"); |
| auto formalInstanceTy |
| = MI->getType().castTo<MetatypeType>().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<ExistentialMetatypeType>(), |
| "existential_metatype instruction must be of metatype type"); |
| require(MI->getType().castTo<ExistentialMetatypeType>()->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<ExistentialMetatypeType>().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.hasUnqualifiedOwnership(), |
| "strong_retain is only in functions with unqualified ownership"); |
| } |
| void checkStrongReleaseInst(StrongReleaseInst *RI) { |
| requireReferenceValue(RI->getOperand(), "Operand of release"); |
| require(F.hasUnqualifiedOwnership(), |
| "strong_release is only in functions with unqualified ownership"); |
| } |
| void checkStrongRetainUnownedInst(StrongRetainUnownedInst *RI) { |
| auto unownedType = requireObjectType(UnownedStorageType, RI->getOperand(), |
| "Operand of strong_retain_unowned"); |
| require(unownedType->isLoadable(ResilienceExpansion::Maximal), |
| "strong_retain_unowned requires unowned type to be loadable"); |
| require(F.hasUnqualifiedOwnership(), "strong_retain_unowned is only in " |
| "functions with unqualified " |
| "ownership"); |
| } |
| void checkUnownedRetainInst(UnownedRetainInst *RI) { |
| auto unownedType = requireObjectType(UnownedStorageType, RI->getOperand(), |
| "Operand of unowned_retain"); |
| require(unownedType->isLoadable(ResilienceExpansion::Maximal), |
| "unowned_retain requires unowned type to be loadable"); |
| require(F.hasUnqualifiedOwnership(), |
| "unowned_retain is only in functions with unqualified ownership"); |
| } |
| void checkUnownedReleaseInst(UnownedReleaseInst *RI) { |
| auto unownedType = requireObjectType(UnownedStorageType, RI->getOperand(), |
| "Operand of unowned_release"); |
| require(unownedType->isLoadable(ResilienceExpansion::Maximal), |
| "unowned_release requires unowned type to be loadable"); |
| require(F.hasUnqualifiedOwnership(), |
| "unowned_release is only in functions with unqualified ownership"); |
| } |
| void checkDeallocStackInst(DeallocStackInst *DI) { |
| require(isa<SILUndef>(DI->getOperand()) || |
| isa<AllocStackInst>(DI->getOperand()), |
| "Operand of dealloc_stack must be an alloc_stack"); |
| } |
| void checkDeallocRefInst(DeallocRefInst *DI) { |
| require(DI->getOperand()->getType().isObject(), |
| "Operand of dealloc_ref must be object"); |
| require(DI->getOperand()->getType().getClassOrBoundGenericClass(), |
| "Operand of dealloc_ref must be of class type"); |
| } |
| 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<MetatypeType>(), |
| "Second operand of dealloc_partial_ref must be a metatype"); |
| auto class2 = DPRI->getMetatype()->getType().castTo<MetatypeType>() |
| ->getInstanceType()->getClassOrBoundGenericClass(); |
| require(class2, |
| "Second operand of dealloc_partial_ref must be a class metatype"); |
| while (class1 != class2) { |
| class1 = class1->getSuperclass()->getClassOrBoundGenericClass(); |
| require(class1, "First operand not superclass of second instance type"); |
| } |
| } |
| |
| void checkAllocBoxInst(AllocBoxInst *AI) { |
| auto boxTy = AI->getType().getAs<SILBoxType>(); |
| 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, |
| AI->getBoxType()->getFieldLoweredType(F.getModule(), 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<MarkUninitializedInst>(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<SILBoxType>(); |
| 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"); |
| } |
| |
| void checkBindMemoryInst(BindMemoryInst *BI) { |
| require(!BI->getType(), "BI must not produce a type"); |
| require(BI->getBoundType(), "BI must have a bound type"); |
| require(BI->getBase()->getType().is<BuiltinRawPointerType>(), |
| "bind_memory base be a RawPointer"); |
| require(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"); |
| require(IAI->getType() == IAI->getBase()->getType(), |
| "index_addr must produce an address of the same type as its base"); |
| require(IAI->getIndex()->getType().is<BuiltinIntegerType>(), |
| "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<BuiltinIntegerType>(), |
| "tail_addr index must be of a builtin integer type"); |
| } |
| |
| void checkIndexRawPointerInst(IndexRawPointerInst *IAI) { |
| require(IAI->getType().is<BuiltinRawPointerType>(), |
| "index_raw_pointer must produce a RawPointer"); |
| require(IAI->getBase()->getType().is<BuiltinRawPointerType>(), |
| "index_raw_pointer base must be a RawPointer"); |
| require(IAI->getIndex()->getType().is<BuiltinIntegerType>(), |
| "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) { |
| require(EI->getType().getSwiftRValueType() == |
| 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(!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()); |
| require(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<TupleType>(), |
| "must derive tuple_element_addr from tuple"); |
| |
| ArrayRef<TupleTypeElt> fields = operandTy.castTo<TupleType>()->getElements(); |
| require(EI->getFieldNo() < fields.size(), |
| "invalid field index for element_addr instruction"); |
| if (EI->getModule().getStage() != SILStage::Lowered) { |
| require(EI->getType().getSwiftRValueType() == |
| 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(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()); |
| require(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(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()); |
| require(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"); |
| } |
| |
| 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<ProtocolDecl>(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->getGenericSignature(); |
| |
| 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<Requirement> 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, |
| *F.getModule().getSwiftModule()); |
| require(conformsTo.size() == 1, |
| "requirement Self parameter must conform to exactly one protocol"); |
| require(conformsTo[0] == protocol, |
| "requirement Self parameter should be constrained by 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<ArchetypeType>(lookupType) || lookupType->isAnyExistentialType()) { |
| require(AMI->getConformance().isAbstract(), |
| "archetype or existential lookup should have abstract conformance"); |
| } else { |
| require(AMI->getConformance().isConcrete(), |
| "concrete type lookup requires concrete conformance"); |
| auto conformance = AMI->getConformance().getConcrete(); |
| require(conformance->getType()->isEqual(AMI->getLookupType()), |
| "concrete type lookup requires conformance that matches type"); |
| require(AMI->getModule().lookUpWitnessTable(conformance, false), |
| "Could not find witness table for conformance"); |
| } |
| } |
| |
| // 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(method); |
| auto methodTy = constantInfo.SILFnType; |
| |
| // Map interface types to archetypes. |
| if (auto *env = constantInfo.GenericEnv) { |
| auto subs = env->getForwardingSubstitutions(); |
| methodTy = methodTy->substGenericArgs(F.getModule(), subs); |
| } |
| assert(!methodTy->isPolymorphic()); |
| |
| // Replace Self parameter with type of 'self' at the call site. |
| auto params = methodTy->getParameters(); |
| SmallVector<SILParameterInfo, 4> |
| dynParams(params.begin(), params.end() - 1); |
| dynParams.push_back(SILParameterInfo(selfType.getSwiftRValueType(), |
| params.back().getConvention())); |
| |
| auto results = methodTy->getResults(); |
| SmallVector<SILResultInfo, 4> dynResults(results.begin(), results.end()); |
| |
| // If the method returns Self, substitute AnyObject for the result type. |
| if (auto fnDecl = dyn_cast<FuncDecl>(method.getDecl())) { |
| if (fnDecl->hasDynamicSelf()) { |
| auto anyObjectTy = C.getAnyObjectType(); |
| for (auto &dynResult : dynResults) { |
| auto newResultTy |
| = dynResult.getType()->replaceCovariantResultType(anyObjectTy, 0); |
| dynResult = SILResultInfo(newResultTy->getCanonicalType(), |
| dynResult.getConvention()); |
| } |
| } |
| } |
| |
| auto fnTy = SILFunctionType::get(nullptr, |
| methodTy->getExtInfo(), |
| methodTy->getCalleeConvention(), |
| dynParams, |
| dynResults, |
| methodTy->getOptionalErrorResult(), |
| F.getASTContext()); |
| return SILType::getPrimitiveObjectType(fnTy); |
| } |
| |
| void checkDynamicMethodInst(DynamicMethodInst *EMI) { |
| requireObjectType(SILFunctionType, EMI, "result of dynamic_method"); |
| SILType operandType = EMI->getOperand()->getType(); |
| |
| require(EMI->getMember().getDecl()->isObjC(), "method must be @objc"); |
| if (!EMI->getMember().getDecl()->isInstanceMember()) { |
| require(operandType.is<MetatypeType>(), |
| "operand must have metatype type"); |
| require(operandType.castTo<MetatypeType>() |
| ->getInstanceType()->mayHaveSuperclass(), |
| "operand must have metatype of class or class-bounded type"); |
| } |
| |
| require(getDynamicMethodType(operandType, EMI->getMember()) |
| .getSwiftRValueType() |
| ->isBindableTo(EMI->getType().getSwiftRValueType()), |
| "result must be of the method's type"); |
| verifyOpenedArchetype(EMI, EMI->getType().getSwiftRValueType()); |
| } |
| |
| void checkClassMethodInst(ClassMethodInst *CMI) { |
| auto overrideTy = TC.getConstantOverrideType(CMI->getMember()); |
| 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(CMI->getMember().isForeign |
| || !CMI->getMember().getDecl()->hasClangNode(), |
| "foreign method cannot be dispatched natively"); |
| |
| require(CMI->getMember().isForeign |
| || !isa<ExtensionDecl>(CMI->getMember().getDecl()->getDeclContext()), |
| "extension method cannot be dispatched natively"); |
| |
| // 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<AnyMetatypeType>()) { |
| bool objcMetatype |
| = metaTy->getRepresentation() == MetatypeRepresentation::ObjC; |
| bool objcMethod = CMI->getMember().isForeign; |
| require(objcMetatype == objcMethod, |
| "objc class methods must be invoked on objc metatypes"); |
| } |
| #endif |
| } |
| |
| void checkSuperMethodInst(SuperMethodInst *CMI) { |
| auto overrideTy = TC.getConstantOverrideType(CMI->getMember()); |
| 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"); |
| |
| Type methodClass; |
| auto decl = CMI->getMember().getDecl(); |
| if (auto classDecl = dyn_cast<ClassDecl>(decl)) |
| methodClass = classDecl->getDeclaredTypeInContext(); |
| else |
| methodClass = decl->getDeclContext()->getDeclaredTypeInContext(); |
| |
| require(methodClass->getClassOrBoundGenericClass(), |
| "super_method must look up a class method"); |
| require(!methodClass->isEqual(operandType.getSwiftRValueType()), |
| "super_method operand should be a subtype of the " |
| "lookup class type"); |
| } |
| |
| void checkOpenExistentialAddrInst(OpenExistentialAddrInst *OEI) { |
| SILType operandType = OEI->getOperand()->getType(); |
| require(operandType.isAddress(), |
| "open_existential_addr must be applied to address"); |
| require(operandType.canUseExistentialRepresentation(F.getModule(), |
| 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().getSwiftRValueType()); |
| 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; |
| |
| auto isConsumingOrMutatingApplyUse = [](Operand *use) -> bool { |
| ApplySite apply(use->getUser()); |
| assert(apply && "Not an apply instruction kind"); |
| auto conv = apply.getArgumentConvention(use->getOperandNumber() - 1); |
| switch (conv) { |
| case SILArgumentConvention::Indirect_In_Guaranteed: |
| return false; |
| |
| case SILArgumentConvention::Indirect_Out: |
| case SILArgumentConvention::Indirect_In: |
| case SILArgumentConvention::Indirect_In_Constant: |
| case SILArgumentConvention::Indirect_Inout: |
| case SILArgumentConvention::Indirect_InoutAliasable: |
| 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?!"); |
| }; |
| |
| // A "copy_addr %src [take] to *" is consuming on "%src". |
| // A "copy_addr * to * %dst" is mutating on "%dst". |
| auto isConsumingOrMutatingCopyAddrUse = [](Operand *use) -> bool { |
| auto *copyAddr = cast<CopyAddrInst>(use->getUser()); |
| if (copyAddr->getDest() == use->get()) |
| return true; |
| if (copyAddr->getSrc() == use->get() && copyAddr->isTakeOfSrc() == IsTake) |
| return true; |
| return false; |
| }; |
| |
| auto isMutatingOrConsuming = [=](OpenExistentialAddrInst *OEI) -> bool { |
| for (auto *use : OEI->getUses()) { |
| auto *inst = use->getUser(); |
| if (inst->isTypeDependentOperand(*use)) |
| continue; |
| switch (inst->getKind()) { |
| case ValueKind::ApplyInst: |
| case ValueKind::TryApplyInst: |
| case ValueKind::PartialApplyInst: |
| if (isConsumingOrMutatingApplyUse(use)) |
| return true; |
| else |
| break; |
| case ValueKind::CopyAddrInst: |
| if (isConsumingOrMutatingCopyAddrUse(use)) |
| return true; |
| else |
| break; |
| case ValueKind::DestroyAddrInst: |
| return true; |
| case ValueKind::UncheckedAddrCastInst: |
| // Escaping use lets be conservative here. |
| return true; |
| case ValueKind::CheckedCastAddrBranchInst: |
| if (cast<CheckedCastAddrBranchInst>(inst)->getConsumptionKind() != |
| CastConsumptionKind::CopyOnSuccess) |
| return true; |
| break; |
| case ValueKind::LoadInst: |
| // A 'non-taking' value load is harmless. |
| return cast<LoadInst>(inst)->getOwnershipQualifier() == |
| LoadOwnershipQualifier::Take; |
| break; |
| case ValueKind::DebugValueAddrInst: |
| // Harmless use. |
| break; |
| default: |
| llvm_unreachable("Unhandled unexpected instruction"); |
| break; |
| } |
| } |
| return false; |
| }; |
| require(allowedAccessKind == OpenedExistentialAccess::Mutable |
| || !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(F.getModule(), |
| ExistentialRepresentation::Class), |
| "open_existential_ref operand must be class existential"); |
| |
| CanType resultInstanceTy = OEI->getType().getSwiftRValueType(); |
| |
| 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(F.getModule(), |
| ExistentialRepresentation::Boxed), |
| "open_existential_box operand must be boxed existential"); |
| |
| CanType resultInstanceTy = OEI->getType().getSwiftRValueType(); |
| |
| 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(F.getModule(), |
| ExistentialRepresentation::Boxed), |
| "open_existential_box operand must be boxed existential"); |
| |
| CanType resultInstanceTy = OEI->getType().getSwiftRValueType(); |
| |
| 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<ExistentialMetatypeType>(), |
| "open_existential_metatype operand must be existential metatype"); |
| require(operandType.castTo<ExistentialMetatypeType>()->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<MetatypeType>(), |
| "open_existential_metatype result must be metatype"); |
| require(resultType.castTo<MetatypeType>()->hasRepresentation(), |
| "open_existential_metatype result must have a representation"); |
| require(operandType.castTo<ExistentialMetatypeType>()->getRepresentation() |
| == resultType.castTo<MetatypeType>()->getRepresentation(), |
| "open_existential_metatype result must match representation of " |
| "operand"); |
| |
| CanType operandInstTy = |
| operandType.castTo<ExistentialMetatypeType>().getInstanceType(); |
| CanType resultInstTy = |
| resultType.castTo<MetatypeType>().getInstanceType(); |
| |
| while (auto operandMetatype = |
| dyn_cast<ExistentialMetatypeType>(operandInstTy)) { |
| require(isa<MetatypeType>(resultInstTy), |
| "metatype depth mismatch in open_existential_metatype result"); |
| operandInstTy = operandMetatype.getInstanceType(); |
| resultInstTy = cast<MetatypeType>(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( |
| F.getModule(), 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().getSwiftRValueType()); |
| 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(F.getModule(), |
| ExistentialRepresentation::Boxed, |
| AEBI->getFormalConcreteType()), |
| "alloc_existential_box must be used with a boxed existential " |
| "type"); |
| |
| checkExistentialProtocolConformances(exType, |
| 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(F.getModule(), |
| 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 = ArchetypeType::getOpened(exType.getSwiftRValueType()); |
| |
| auto loweredTy = F.getModule().Types.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, |
| 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 = ArchetypeType::getOpened(exType.getSwiftRValueType()); |
| auto loweredTy = F.getModule().Types.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, |
| IEI->getFormalConcreteType(), |
| IEI->getConformances()); |
| verifyOpenedArchetype(IEI, IEI->getFormalConcreteType()); |
| } |
| |
| void checkInitExistentialRefInst(InitExistentialRefInst *IEI) { |
| SILType concreteType = IEI->getOperand()->getType(); |
| require(concreteType.getSwiftRValueType()->isBridgeableObjectType(), |
| "init_existential_ref operand must be a class instance"); |
| require(IEI->getType().canUseExistentialRepresentation(F.getModule(), |
| 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 = ArchetypeType::getOpened(exType.getSwiftRValueType()); |
| auto loweredTy = F.getModule().Types.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, |
| 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( |
| F.getModule(), 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( |
| F.getModule(), 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(F.getModule(), |
| 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<MetatypeType>(), |
| "init_existential_metatype operand must be a metatype"); |
| require(operandType.castTo<MetatypeType>()->hasRepresentation(), |
| "init_existential_metatype operand must have a representation"); |
| |
| SILType resultType = I->getType(); |
| require(resultType.is<ExistentialMetatypeType>(), |
| "init_existential_metatype result must be an existential metatype"); |
| auto MetaTy = resultType.castTo<ExistentialMetatypeType>(); |
| 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<MetatypeType>()->getRepresentation(), |
| "init_existential_metatype result must match representation of " |
| "operand"); |
| |
| while (resultType.is<ExistentialMetatypeType>()) { |
| resultType = resultType.getMetatypeInstanceType(F.getModule()); |
| operandType = operandType.getMetatypeInstanceType(F.getModule()); |
| } |
| |
| checkExistentialProtocolConformances(resultType, |
| operandType.getSwiftRValueType(), |
| I->getConformances()); |
| verifyOpenedArchetype(I, MetaTy.getInstanceType()); |
| } |
| |
| void checkExistentialProtocolConformances(SILType resultType, |
| CanType concreteType, |
| ArrayRef<ProtocolConformanceRef> conformances) { |
| auto layout = resultType.getSwiftRValueType().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 (layout.superclass) { |
| require(layout.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"); |
| |
| if (conformances[i].isConcrete()) { |
| auto conformance = conformances[i].getConcrete(); |
| require(F.getModule().lookUpWitnessTable(conformance, false), |
| "Could not find witness table for conformance."); |
| |
| } |
| } |
| } |
| |
| 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.getSwiftRValueType(); |
| auto toCanTy = toTy.getSwiftRValueType(); |
| |
| 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<AnyMetatypeType>(fromCanTy) && isa<AnyMetatypeType>(toCanTy)) { |
| auto fromMetaty = cast<AnyMetatypeType>(fromCanTy); |
| auto toMetaty = cast<AnyMetatypeType>(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(SILType::getPrimitiveObjectType(fromCanTy). |
| isBindableToSuperclassOf(SILType::getPrimitiveObjectType(toCanTy)), |
| "downcast must convert to a subclass"); |
| } |
| } |
| |
| void checkUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *CI) { |
| verifyCheckedCast(/*exact*/ false, |
| CI->getOperand()->getType(), |
| CI->getType()); |
| verifyOpenedArchetype(CI, CI->getType().getSwiftRValueType()); |
| } |
| |
| void checkUnconditionalCheckedCastValueInst( |
| UnconditionalCheckedCastValueInst *CI) { |
| verifyCheckedCast(/*exact*/ false, CI->getOperand()->getType(), |
| CI->getType(), true); |
| verifyOpenedArchetype(CI, CI->getType().getSwiftRValueType()); |
| } |
| |
| /// 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<ArchetypeType>(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->getOperand()->getType(), |
| CBI->getCastType()); |
| verifyOpenedArchetype(CBI, CBI->getCastType().getSwiftRValueType()); |
| |
| require(CBI->getSuccessBB()->args_size() == 1, |
| "success dest of checked_cast_br must take one argument"); |
| require(CBI->getSuccessBB()->args_begin()[0]->getType() == |
| CBI->getCastType(), |
| "success dest block argument of checked_cast_br must match type of " |
| "cast"); |
| require(F.hasQualifiedOwnership() || CBI->getFailureBB()->args_empty(), |
| "failure dest of checked_cast_br in unqualified ownership sil must " |
| "take no arguments"); |
| #if 0 |
| require(F.hasUnqualifiedOwnership() || |
| CBI->getFailureBB()->args_size() == 1, |
| "failure dest of checked_cast_br must take one argument in " |
| "ownership qualified sil"); |
| require(F.hasUnqualifiedOwnership() || |
| CBI->getFailureBB()->args_begin()[0]->getType() == |
| CBI->getOperand()->getType(), |
| "failure dest block argument must match type of original type in " |
| "ownership qualified sil"); |
| #endif |
| } |
| |
| void checkCheckedCastValueBranchInst(CheckedCastValueBranchInst *CBI) { |
| verifyCheckedCast(false, CBI->getOperand()->getType(), CBI->getCastType(), |
| true); |
| verifyOpenedArchetype(CBI, CBI->getCastType().getSwiftRValueType()); |
| |
| require(CBI->getSuccessBB()->args_size() == 1, |
| "success dest of checked_cast_value_br must take one argument"); |
| require(CBI->getSuccessBB()->args_begin()[0]->getType() == |
| CBI->getCastType(), |
| "success dest block argument of checked_cast_value_br must match " |
| "type of cast"); |
| require(F.hasQualifiedOwnership() || 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); |
| 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<MetatypeType>() == |
| TTOCI->getType().is<MetatypeType>(), |
| "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"); |
| |
| require(opTy->getInstanceType()->isEqual(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<MetatypeType>() == |
| OCTTI->getType().is<MetatypeType>(), |
| "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"); |
| |
| require(opTy->getInstanceType()->isEqual(resTy->getInstanceType()), |
| "objc_to_thick_metatype instance types do not match"); |
| } |
| |
| void checkRefToUnownedInst(RefToUnownedInst *I) { |
| requireReferenceStorageCapableValue(I->getOperand(), |
| "Operand of ref_to_unowned"); |
| auto operandType = I->getOperand()->getType().getSwiftRValueType(); |
| auto resultType = requireObjectType(UnownedStorageType, I, |
| "Result of ref_to_unowned"); |
| require(resultType->isLoadable(ResilienceExpansion::Maximal), |
| "ref_to_unowned requires unowned type to be loadable"); |
| require(resultType.getReferentType() == operandType, |
| "Result of ref_to_unowned does not have the " |
| "operand's type as its referent type"); |
| } |
| |
| void checkUnownedToRefInst(UnownedToRefInst *I) { |
| auto operandType = requireObjectType(UnownedStorageType, |
| I->getOperand(), |
| "Operand of unowned_to_ref"); |
| require(operandType->isLoadable(ResilienceExpansion::Maximal), |
| "unowned_to_ref requires unowned type to be loadable"); |
| requireReferenceStorageCapableValue(I, "Result of unowned_to_ref"); |
| auto resultType = I->getType().getSwiftRValueType(); |
| require(operandType.getReferentType() == resultType, |
| "Operand of unowned_to_ref does not have the " |
| "operand's type as its referent type"); |
| } |
| |
| void checkRefToUnmanagedInst(RefToUnmanagedInst *I) { |
| requireReferenceStorageCapableValue(I->getOperand(), |
| "Operand of ref_to_unmanaged"); |
| auto operandType = I->getOperand()->getType().getSwiftRValueType(); |
| auto resultType = requireObjectType(UnmanagedStorageType, I, |
| "Result of ref_to_unmanaged"); |
| require(resultType.getReferentType() == operandType, |
| "Result of ref_to_unmanaged does not have the " |
| "operand's type as its referent type"); |
| } |
| |
| void checkUnmanagedToRefInst(UnmanagedToRefInst *I) { |
| auto operandType = requireObjectType(UnmanagedStorageType, |
| I->getOperand(), |
| "Operand of unmanaged_to_ref"); |
| requireReferenceStorageCapableValue(I, "Result of unmanaged_to_ref"); |
| auto resultType = I->getType().getSwiftRValueType(); |
| require(operandType.getReferentType() == resultType, |
| "Operand of unmanaged_to_ref does not have the " |
| "operand's type as its referent type"); |
| } |
| |
| void checkUpcastInst(UpcastInst *UI) { |
| require(UI->getType() != UI->getOperand()->getType(), |
| "can't upcast to same type"); |
| if (UI->getType().is<MetatypeType>()) { |
| CanType instTy(UI->getType().castTo<MetatypeType>()->getInstanceType()); |
| require(UI->getOperand()->getType().is<MetatypeType>(), |
| "upcast operand must be a class or class metatype instance"); |
| CanType opInstTy(UI->getOperand()->getType().castTo<MetatypeType>() |
| ->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<B> to Optional<A> is legal as long as B is a |
| // subclass of A. |
| if (ToTy.getSwiftRValueType().getAnyOptionalObjectType() && |
| FromTy.getSwiftRValueType().getAnyOptionalObjectType()) { |
| ToTy = SILType::getPrimitiveObjectType( |
| ToTy.getSwiftRValueType().getAnyOptionalObjectType()); |
| FromTy = SILType::getPrimitiveObjectType( |
| FromTy.getSwiftRValueType().getAnyOptionalObjectType()); |
| } |
| |
| auto ToClass = ToTy.getClassOrBoundGenericClass(); |
| require(ToClass, |
| "upcast must convert a class instance to a class type"); |
| if (ToClass->usesObjCGenericsModel()) { |
| require(ToClass->getDeclaredTypeInContext() |
| ->isBindableToSuperclassOf(FromTy.getSwiftRValueType()), |
| "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 checkIsNonnullInst(IsNonnullInst *II) { |
| // The operand must be a function type or a class type. |
| auto OpTy = II->getOperand()->getType().getSwiftRValueType(); |
| require(OpTy->mayHaveSuperclass() || OpTy->is<SILFunctionType>(), |
| "is_nonnull operand must be a class or function type"); |
| } |
| |
| void checkAddressToPointerInst(AddressToPointerInst *AI) { |
| require(AI->getOperand()->getType().isAddress(), |
| "address-to-pointer operand must be an address"); |
| require(AI->getType().getSwiftRValueType()->isEqual( |
| AI->getType().getASTContext().TheRawPointerType), |
| "address-to-pointer result type must be RawPointer"); |
| } |
| |
| void checkUncheckedRefCastInst(UncheckedRefCastInst *AI) { |
| verifyOpenedArchetype(AI, AI->getType().getSwiftRValueType()); |
| 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().getSwiftRValueType()); |
| |
| 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().getSwiftRValueType()); |
| 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.getModule()), |
| "unchecked_trivial_bit_cast must produce a value of trivial type"); |
| } |
| |
| void checkUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *BI) { |
| verifyOpenedArchetype(BI, BI->getType().getSwiftRValueType()); |
| 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().getSwiftRValueType() |
| ->isAnyClassReferenceType(), |
| "ref-to-raw-pointer operand must be a class reference or" |
| " NativeObject"); |
| require(AI->getType().getSwiftRValueType()->isEqual( |
| AI->getType().getASTContext().TheRawPointerType), |
| "ref-to-raw-pointer result must be RawPointer"); |
| } |
| |
| void checkRawPointerToRefInst(RawPointerToRefInst *AI) { |
| verifyOpenedArchetype(AI, AI->getType().getSwiftRValueType()); |
| require(AI->getType() |
| .getSwiftRValueType()->isBridgeableObjectType() |
| || AI->getType().getSwiftRValueType()->isEqual( |
| AI->getType().getASTContext().TheNativeObjectType) |
| || AI->getType().getSwiftRValueType()->isEqual( |
| AI->getType().getASTContext().TheUnknownObjectType), |
| "raw-pointer-to-ref result must be a class reference or NativeObject"); |
| require(AI->getOperand()->getType().getSwiftRValueType()->isEqual( |
| 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().getSwiftRValueType() |
| ->isBridgeableObjectType(), |
| "ref_to_bridge_object must convert from a heap object ref"); |
| require(RI->getBitsOperand()->getType() |
| == SILType::getBuiltinWordType(F.getASTContext()), |
| "ref_to_bridge_object must take a Builtin.Word bits operand"); |
| require(RI->getType() == SILType::getBridgeObjectType(F.getASTContext()), |
| "ref_to_bridge_object must produce a BridgeObject"); |
| } |
| |
| void checkBridgeObjectToRefInst(BridgeObjectToRefInst *RI) { |
| verifyOpenedArchetype(RI, RI->getType().getSwiftRValueType()); |
| require(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().getSwiftRValueType()->isBridgeableObjectType(), |
| "bridge_object_to_ref must produce a heap object reference"); |
| } |
| void checkBridgeObjectToWordInst(BridgeObjectToWordInst *RI) { |
| require(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"); |
| require(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"); |
| } |
| |
| 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) { |
| require(CFI->getOperand()->getType() |
| == SILType::getBuiltinIntegerType(1, F.getASTContext()), |
| "cond_fail operand must be a Builtin.Int1"); |
| } |
| |
| void checkReturnInst(ReturnInst *RI) { |
| DEBUG(RI->print(llvm::dbgs())); |
| |
| SILType functionResultType = |
| F.mapTypeIntoContext(fnConv.getSILResultType()); |
| SILType instResultType = RI->getOperand()->getType(); |
| DEBUG(llvm::dbgs() << "function return type: "; |
| functionResultType.dump(); |
| llvm::dbgs() << "return inst type: "; |
| instResultType.dump();); |
| require(functionResultType == instResultType, |
| "return value type does not match return type of function"); |
| } |
| |
| void checkThrowInst(ThrowInst *TI) { |
| DEBUG(TI->print(llvm::dbgs())); |
| |
| CanSILFunctionType fnType = F.getLoweredFunctionType(); |
| require(fnType->hasErrorResult(), |
| "throw in function that doesn't have an error result"); |
| |
| SILType functionResultType = F.mapTypeIntoContext(fnConv.getSILErrorType()); |
| SILType instResultType = TI->getOperand()->getType(); |
| DEBUG(llvm::dbgs() << "function error result type: "; |
| functionResultType.dump(); |
| llvm::dbgs() << "throw operand type: "; |
| instResultType.dump();); |
| require(functionResultType == instResultType, |
| "throw operand type does not match error result type of function"); |
| } |
| |
| 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. |
| // FIXME: We also need to consider if the enum is resilient, in which case |
| // we're never guaranteed to be exhaustive. |
| llvm::DenseSet<EnumElementDecl*> unswitchedElts; |
| 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. |
| require(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<BuiltinIntegerType>() || Ty.is<SILFunctionType>(), |
| "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<SILValue, decltype(ult)> cases(ult); |
| |
| for (unsigned i = 0, e = SVI->getNumCases(); i < e; ++i) { |
| SILValue value; |
| SILBasicBlock *dest; |
| std::tie(value, dest) = SVI->getCase(i); |
| |
| require(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<APInt, 16, APIntCmp> 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<SILUndef>(casevalue)) { |
| auto *il = dyn_cast<IntegerLiteralInst>(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. |
| // FIXME: We also need to consider if the enum is resilient, in which case |
| // we're never guaranteed to be exhaustive. |
| llvm::DenseSet<EnumElementDecl*> unswitchedElts; |
| uDecl->getAllElements(unswitchedElts); |
| |
| // 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.hasQualifiedOwnership()) { |
| require(dest->getArguments().size() == 1, |
| "switch_enum destination for case w/ args must take 1 " |
| "argument"); |
| } else { |
| require(dest->getArguments().size() == 0 || |
| 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()); |
| SILType bbArgTy = dest->getArguments()[0]->getType(); |
| 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().size() == 0, |
| "switch_enum destination for no-argument case must take no " |
| "arguments"); |
| } |
| } |
| |
| // If the switch is non-exhaustive, we require a default. |
| require(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.hasQualifiedOwnership()) { |
| require(SOI->getDefaultBB()->getNumArguments() == 1, |
| "Switch enum default block should have one argument"); |
| require(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.hasUnqualifiedOwnership()) { |
| 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. |
| // FIXME: We also need to consider if the enum is resilient, in which case |
| // we're never guaranteed to be exhaustive. |
| llvm::DenseSet<EnumElementDecl*> unswitchedElts; |
| uDecl->getAllElements(unswitchedElts); |
| |
| // 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().size() == 0, |
| "switch_enum_addr destination must take no BB args"); |
| } |
| |
| // If the switch is non-exhaustive, we require a default. |
| require(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<MethodInst>(branchArg) && |
| cast<MethodInst>(branchArg)->getMember().isForeign), |
| "branch argument cannot be a witness_method or an objc method_inst"); |
| 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. |
| require(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"); |
| } |
| |
| 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.getSwiftRValueType()->is<MetatypeType>(), |
| "operand must have metatype type"); |
| require(operandType.getSwiftRValueType()->castTo<MetatypeType>() |
| ->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()) |
| .getSwiftRValueType() |
| ->isBindableTo(bbArgTy.getSwiftRValueType()), |
| "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<SILBlockStorageType>(); |
| require(storageTy, "operand must be a @block_storage type"); |
| |
| require(PBSI->getType().isAddress(), |
| "result must be an address"); |
| auto captureTy = PBSI->getType().getSwiftRValueType(); |
| require(storageTy->getCaptureType() == captureTy, |
| "result must be the capture type of the @block_storage type"); |
| } |
| |
| void checkInitBlockStorageHeaderInst(InitBlockStorageHeaderInst *IBSHI) { |
| require(IBSHI->getBlockStorage()->getType().isAddress(), |
| "block storage operand must be an address"); |
| auto storageTy |
| = IBSHI->getBlockStorage()->getType().getAs<SILBlockStorageType>(); |
| require(storageTy, "block storage operand must be a @block_storage type"); |
| |
| require(IBSHI->getInvokeFunction()->getType().isObject(), |
| "invoke function operand must be a value"); |
| auto invokeTy |
| = IBSHI->getInvokeFunction()->getType().getAs<SILFunctionType>(); |
| 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->getGenericSignature() || |
| 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"); |
| require(storageParam.getType() == 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<SILFunctionType>(); |
| 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<MetatypeType>(); |
| 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().getSwiftRValueType()->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<ExistentialMetatypeType>(); |
| 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().getSwiftRValueType()->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<BoundGenericType>(); |
| 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; |
| if (pattern->getGenericSignature()) |
| patternSubs = pattern->getGenericSignature() |
| ->getSubstitutionMap(KPI->getSubstitutions()); |
| require(baseTy == pattern->getRootType().subst(patternSubs)->getCanonicalType(), |
| "keypath root type should match root type of keypath pattern"); |
| |
| auto leafTy = CanType(kpBGT->getGenericArgs()[1]); |
| require(leafTy == pattern->getValueType().subst(patternSubs)->getCanonicalType(), |
| "keypath value type should match value type of keypath pattern"); |
| |
| { |
| Lowering::GenericContextScope scope(F.getModule().Types, |
| pattern->getGenericSignature()); |
| |
| for (auto &component : pattern->getComponents()) { |
| auto loweredBaseTy = |
| F.getModule().Types.getLoweredType(AbstractionPattern::getOpaque(), |
| baseTy); |
| auto componentTy = component.getComponentType().subst(patternSubs) |
| ->getCanonicalType(); |
| auto loweredComponentTy = |
| F.getModule().Types.getLoweredType(AbstractionPattern::getOpaque(), |
| componentTy); |
| |
| switch (auto kind = component.getKind()) { |
| case KeyPathPatternComponent::Kind::StoredProperty: { |
| auto property = component.getStoredPropertyDecl(); |
| require(property->getDeclContext() |
| == baseTy->getAnyNominal(), |
| "property decl should be a member of the component base type"); |
| switch (property->getStorageKind()) { |
| case AbstractStorageDecl::Stored: |
| case AbstractStorageDecl::StoredWithObservers: |
| case AbstractStorageDecl::StoredWithTrivialAccessors: |
| break; |
| case AbstractStorageDecl::Addressed: |
| case AbstractStorageDecl::AddressedWithObservers: |
| case AbstractStorageDecl::AddressedWithTrivialAccessors: |
| case AbstractStorageDecl::Computed: |
| case AbstractStorageDecl::ComputedWithMutableAddress: |
| case AbstractStorageDecl::InheritedWithObservers: |
| require(false, "property must be stored"); |
| } |
| auto propertyTy = loweredBaseTy.getFieldType(property, F.getModule()); |
| 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: { |
| bool hasIndices = !component.getComputedPropertyIndices().empty(); |
| |
| // Getter should be <Sig...> @convention(thin) (@in Base) -> @out Result |
| { |
| auto getter = component.getComputedPropertyGetter(); |
| auto substGetterType = getter->getLoweredFunctionType() |
| ->substGenericArgs(F.getModule(), KPI->getSubstitutions()); |
| 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() == |
| ParameterConvention::Indirect_In, |
| "getter base parameter should be @in"); |
| require(baseParam.getType() == loweredBaseTy.getSwiftRValueType(), |
| "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.getType()->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.getType() == loweredComponentTy.getSwiftRValueType(), |
| "getter result should match the maximal abstraction of the " |
| "formal component type"); |
| } |
| |
| if (kind == KeyPathPatternComponent::Kind::SettableProperty) { |
| // Setter should be |
| // <Sig...> @convention(thin) (@in Result, @in Base) -> () |
| |
| auto setter = component.getComputedPropertySetter(); |
| auto substSetterType = setter->getLoweredFunctionType() |
| ->substGenericArgs(F.getModule(), KPI->getSubstitutions()); |
| |
| 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]; |
| require(newValueParam.getConvention() == |
| ParameterConvention::Indirect_In, |
| "setter value parameter should be @in"); |
| |
| auto baseParam = substSetterType->getParameters()[1]; |
| require(baseParam.getConvention() == |
| ParameterConvention::Indirect_In |
| || baseParam.getConvention() == |
| ParameterConvention::Indirect_Inout, |
| "setter base parameter should be @in or @inout"); |
| |
| if (hasIndices) { |
| auto indicesParam = substSetterType->getParameters()[2]; |
| require(indicesParam.getConvention() |
| == ParameterConvention::Direct_Unowned, |
| "indices pointer should be trivial"); |
| require(indicesParam.getType()->getAnyNominal() |
| == C.getUnsafeRawPointerDecl(), |
| "indices pointer should be an UnsafeRawPointer"); |
| } |
| |
| require(newValueParam.getType() == |
| loweredComponentTy.getSwiftRValueType(), |
| "setter value should match the maximal abstraction of the " |
| "formal component type"); |
| |
| require(substSetterType->getNumResults() == 0, |
| "setter should have no results"); |
| } |
| |
| for (auto &index : component.getComputedPropertyIndices()) { |
| auto opIndex = index.Operand; |
| auto contextType = |
| index.LoweredType.subst(F.getModule(), patternSubs); |
| requireSameType(contextType, |
| KPI->getAllOperands()[opIndex].get()->getType(), |
| "operand must match type required by pattern"); |
| require(isLoweringOf(index.LoweredType, index.FormalType), |
| "pattern index formal type doesn't match lowered type"); |
| } |
| |
| if (!component.getComputedPropertyIndices().empty()) { |
| // Equals should be |
| // <Sig...> @convention(thin) (RawPointer, RawPointer) -> Bool |
| { |
| auto equals = component.getComputedPropertyIndexEquals(); |
| require(equals, "key path pattern with indexes must have equals " |
| "operator"); |
| |
| auto substEqualsType = equals->getLoweredFunctionType() |
| ->substGenericArgs(F.getModule(), KPI->getSubstitutions()); |
| |
| 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.getType()->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].getType()->getAnyNominal() |
| == C.getBoolDecl(), |
| "result should be Bool"); |
| } |
| { |
| // Hash should be |
| // <Sig...> @convention(thin) (RawPointer) -> Int |
| auto hash = component.getComputedPropertyIndexHash(); |
| require(hash, "key path pattern with indexes must have hash " |
| "operator"); |
| |
| auto substHashType = hash->getLoweredFunctionType() |
| ->substGenericArgs(F.getModule(), KPI->getSubstitutions()); |
| |
| 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.getType()->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].getType()->getAnyNominal() |
| == C.getIntDecl(), |
| "result should be Int"); |
| } |
| } else { |
| require(!component.getComputedPropertyIndexEquals() |
| && !component.getComputedPropertyIndexHash(), |
| "component without indexes must not have equals/hash"); |
| } |
| |
| break; |
| } |
| case KeyPathPatternComponent::Kind::OptionalChain: { |
| require(baseTy->getAnyOptionalObjectType()->isEqual(componentTy), |
| "chaining component should unwrap optional"); |
| require(leafTy->getAnyOptionalObjectType(), |
| "key path with chaining component should have optional " |
| "result"); |
| break; |
| } |
| case KeyPathPatternComponent::Kind::OptionalForce: { |
| require(baseTy->getAnyOptionalObjectType()->isEqual(componentTy), |
| "forcing component should unwrap optional"); |
| break; |
| } |
| case KeyPathPatternComponent::Kind::OptionalWrap: { |
| require(componentTy->getAnyOptionalObjectType()->isEqual(baseTy), |
| "wrapping component should wrap optional"); |
| break; |
| } |
| } |
| |
| baseTy = componentTy; |
| } |
| } |
| require(CanType(baseTy) == CanType(leafTy), |
| "final component should match leaf value type of key path type"); |
| } |
| |
| void verifyEntryPointArguments(SILBasicBlock *entry) { |
| 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(); |
| SILModule &M = F.getModule(); |
| |
| auto check = [&](const char *what, SILType ty) { |
| auto mappedTy = F.mapTypeIntoContext(ty); |
| SILArgument *bbarg = *argI; |
| ++argI; |
| auto ownershipkind = ValueOwnershipKind( |
| M, mappedTy, fnConv.getSILArgumentConvention(bbarg->getIndex())); |
| if (bbarg->getType() != mappedTy) { |
| 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.hasQualifiedOwnership()) { |
| return; |
| } |
| |
| 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("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; |
| for (auto &BB : *F) { |
| if (isa<ReturnInst>(BB.getTerminator())) { |
| require(!FoundReturnBlock, |
| "more than one return block in function"); |
| FoundReturnBlock = true; |
| } |
| if (isa<ThrowInst>(BB.getTerminator())) { |
| require(!FoundThrowBlock, |
| "more than one throw block in function"); |
| FoundThrowBlock = true; |
| } |
| } |
| } |
| |
| bool |
| isUnreachableAlongAllPathsStartingAt(SILBasicBlock *StartBlock, |
| SmallPtrSet<SILBasicBlock *, 16> &Visited) { |
| if (isa<UnreachableInst>(StartBlock->getTerminator())) |
| return true; |
| else if (isa<ReturnInst>(StartBlock->getTerminator())) |
| return false; |
| else if (isa<ThrowInst>(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."); |
| } |
| |
| /// 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) { |
| struct BBState { |
| std::vector<SILInstruction*> Stack; |
| std::set<BeginAccessInst*> Accesses; |
| }; |
| |
| // Do a breath-first search through 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<SILBasicBlock*, BBState> visitedBBs; |
| SmallVector<SILBasicBlock*, 16> Worklist; |
| visitedBBs.try_emplace(&*F->begin()); |
| Worklist.push_back(&*F->begin()); |
| while (!Worklist.empty()) { |
| SILBasicBlock *BB = Worklist.pop_back_val(); |
| BBState state = visitedBBs[BB]; |
| for (SILInstruction &i : *BB) { |
| CurInstruction = &i; |
| |
| if (i.isAllocatingStack()) { |
| state.Stack.push_back(&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 (auto access = dyn_cast<BeginAccessInst>(&i)) { |
| bool notAlreadyPresent = state.Accesses.insert(access).second; |
| require(notAlreadyPresent, |
| "access was not ended before re-beginning it"); |
| |
| } else if (auto endAccess = dyn_cast<EndAccessInst>(&i)) { |
| // We don't call getBeginAccess() because this isn't the right |
| // place to assert on malformed SIL. |
| if (auto access = dyn_cast<BeginAccessInst>(endAccess->getOperand())){ |
| bool present = state.Accesses.erase(access); |
| require(present, "access has already been ended"); |
| } |
| |
| } else if (auto term = dyn_cast<TermInst>(&i)) { |
| if (term->isFunctionExiting()) { |
| require(state.Stack.empty(), |
| "return with stack allocs that haven't been deallocated"); |
| require(state.Accesses.empty(), |
| "return with accesses that haven't been ended"); |
| } |
| |
| 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); |
| continue; |
| } |
| |
| // 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<SILBasicBlock *, 16> 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.Accesses == foundState.Accesses || isUnreachable(), |
| "inconsistent access sets entering basic block"); |
| } |
| } |
| } |
| } |
| } |
| |
| void verifyBranches(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; |
| }; |
| |
| SILModule &M = F->getModule(); |
| for (auto &BB : *F) { |
| TermInst *TI = BB.getTerminator(); |
| |
| // Check for non-cond_br critical edges in canonical SIL. |
| if (!isa<CondBranchInst>(TI) && M.getStage() == SILStage::Canonical) { |
| for (unsigned Idx = 0, e = BB.getSuccessors().size(); Idx != e; ++Idx) { |
| require(!isCriticalEdgePred(TI, Idx), |
| "non cond_br critical edges not allowed"); |
| } |
| continue; |
| } |
| |
| // In ownership qualified SIL, ban critical edges from CondBranchInst that |
| // have non-trivial arguments. |
| if (!F->hasQualifiedOwnership()) |
| continue; |
| |
| auto *CBI = dyn_cast<CondBranchInst>(TI); |
| if (!CBI) |
| continue; |
| |
| if (isCriticalEdgePred(CBI, CondBranchInst::TrueIdx)) { |
| require( |
| llvm::all_of(CBI->getTrueArgs(), |
| [](SILValue V) -> bool { |
| return V.getOwnershipKind() == |
| ValueOwnershipKind::Trivial; |
| }), |
| "cond_br with critical edges must not have a non-trivial value"); |
| } |
| if (isCriticalEdgePred(CBI, CondBranchInst::FalseIdx)) { |
| require( |
| llvm::all_of(CBI->getFalseArgs(), |
| [](SILValue V) -> bool { |
| return V.getOwnershipKind() == |
| ValueOwnershipKind::Trivial; |
| }), |
| "cond_br with critical edges must not have a non-trivial value"); |
| } |
| } |
| } |
| |
| 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<SILInstruction>(KV.second); |
| require(Def->getFunction() == F, |
| "Definition of every registered opened archetype should be an" |
| " existing instruction in a current SILFunction"); |
| } |
| } |
| |
| 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()) { |
| bool FoundSelfInSuccessor = false; |
| if (SuccBB->isPredecessorBlock(BB)) { |
| FoundSelfInSuccessor = true; |
| break; |
| } |
| require(FoundSelfInSuccessor, "Must be a predecessor of each successor."); |
| } |
| |
| for (const SILBasicBlock *PredBB : BB->getPredecessorBlocks()) { |
| bool FoundSelfInPredecessor = false; |
| if (PredBB->isSuccessorBlock(BB)) { |
| FoundSelfInPredecessor = true; |
| break; |
| } |
| require(FoundSelfInPredecessor, "Must be a successor of each predecessor."); |
| } |
| |
| SILVisitor::visitSILBasicBlock(BB); |
| } |
| |
| 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<SILFunction *> RPOT(F); |
| llvm::DenseSet<SILBasicBlock *> 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; |
| |
| assert(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. |
| verifyEntryPointArguments(&*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); |
| } |
| |
| void verify() { |
| visitSILFunction(const_cast<SILFunction*>(&F)); |
| } |
| }; |
| } // end anonymous namespace |
| |
| #undef require |
| #undef requireObjectType |
| |
| //===----------------------------------------------------------------------===// |
| // Out of Line Verifier Run Functions |
| //===----------------------------------------------------------------------===// |
| |
| /// verify - Run the SIL verifier to make sure that the SILFunction follows |
| /// invariants. |
| void SILFunction::verify(bool SingleFunction) const { |
| #ifdef NDEBUG |
| if (!getModule().getOptions().VerifyAll) |
| return; |
| #endif |
| // 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(); |
| } |
| |
| /// Verify that a vtable follows invariants. |
| void SILVTable::verify(const SILModule &M) const { |
| #ifdef NDEBUG |
| if (!M.getOptions().VerifyAll) |
| return; |
| #endif |
| 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(entry.Method); |
| ValueDecl *decl = entry.Method.getDecl(); |
| |
| assert((!isa<FuncDecl>(decl) |
| || !cast<FuncDecl>(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<ClassDecl>(decl); |
| else |
| theClass = dyn_cast<ClassDecl>(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. |
| auto c = getClass(); |
| do { |
| if (c == theClass) |
| break; |
| if (auto ty = c->getSuperclass()) |
| c = ty->getClassOrBoundGenericClass(); |
| else |
| c = nullptr; |
| } while (c); |
| assert(c && "vtable entry must refer to a member of the vtable's class"); |
| |
| // All function vtable entries must be at their natural uncurry level. |
| assert(!entry.Method.isCurried && "vtable entry must not be curried"); |
| |
| // 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<SILFunctionType>(), |
| entry.Implementation->getLoweredFunctionType(), |
| "vtable entry for " + baseName + " must be ABI-compatible"); |
| } |
| } |
| } |
| |
| /// Verify that a witness table follows invariants. |
| void SILWitnessTable::verify(const SILModule &M) const { |
| #ifdef NDEBUG |
| if (!M.getOptions().VerifyAll) |
| return; |
| #endif |
| if (isDeclaration()) |
| assert(getEntries().size() == 0 && |
| "A witness table declaration should not have any entries."); |
| |
| auto *protocol = getConformance()->getProtocol(); |
| |
| 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((!isLessVisibleThan(F->getLinkage(), getLinkage()) || |
| (F->isSerialized() && |
| hasSharedVisibility(F->getLinkage()))) && |
| "Fragile witness tables should not reference " |
| "less visible functions."); |
| } |
| |
| assert(F->getLoweredFunctionType()->getRepresentation() == |
| SILFunctionTypeRepresentation::WitnessMethod && |
| "Witnesses must have witness_method representation."); |
| auto *witnessSelfProtocol = F->getLoweredFunctionType() |
| ->getDefaultWitnessMethodProtocol(*M.getSwiftModule()); |
| assert((witnessSelfProtocol == nullptr || |
| witnessSelfProtocol == protocol) && |
| "Witnesses must either have a concrete Self, or an " |
| "an abstract Self that is constrained to their " |
| "protocol."); |
| } |
| } |
| } |
| |
| /// Verify that a default witness table follows invariants. |
| void SILDefaultWitnessTable::verify(const SILModule &M) const { |
| #ifndef NDEBUG |
| for (const Entry &E : getEntries()) { |
| if (!E.isValid()) |
| continue; |
| |
| SILFunction *F = E.getWitness(); |
| // FIXME |
| #if 0 |
| assert(!isLessVisibleThan(F->getLinkage(), getLinkage()) && |
| "Default witness tables should not reference " |
| "less visible functions."); |
| #endif |
| assert(F->getLoweredFunctionType()->getRepresentation() == |
| SILFunctionTypeRepresentation::WitnessMethod && |
| "Default witnesses must have witness_method representation."); |
| auto *witnessSelfProtocol = F->getLoweredFunctionType() |
| ->getDefaultWitnessMethodProtocol(*M.getSwiftModule()); |
| assert(witnessSelfProtocol == getProtocol() && |
| "Default witnesses must have an abstract Self parameter " |
| "constrained to their protocol."); |
| } |
| #endif |
| } |
| |
| /// Verify that a global variable follows invariants. |
| void SILGlobalVariable::verify() const { |
| #ifdef NDEBUG |
| if (!getModule().getOptions().VerifyAll) |
| return; |
| #endif |
| 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"); |
| if (&I == &StaticInitializerBlock.back()) { |
| assert(I.use_empty() && "Init value must not have another use"); |
| } else { |
| assert(!I.use_empty() && "dead instruction in static initializer"); |
| assert(!isa<ObjectInst>(&I) && |
| "object instruction is only allowed for final initial value"); |
| } |
| assert(I.getParent() == &StaticInitializerBlock); |
| } |
| } |
| |
| /// Verify the module. |
| void SILModule::verify() const { |
| #ifdef NDEBUG |
| if (!getOptions().VerifyAll) |
| return; |
| #endif |
| // Uniquing set to catch symbol name collisions. |
| llvm::StringSet<> 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<ClassDecl*> 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. |
| DEBUG(llvm::dbgs() << "*** Checking witness tables for duplicates ***\n"); |
| llvm::DenseSet<NormalProtocolConformance*> wtableConformances; |
| for (const SILWitnessTable &wt : getWitnessTables()) { |
| 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. |
| DEBUG(llvm::dbgs() << "*** Checking default witness tables for duplicates ***\n"); |
| llvm::DenseSet<const ProtocolDecl *> defaultWitnessTables; |
| for (const SILDefaultWitnessTable &wt : getDefaultWitnessTables()) { |
| 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); |
| } |
| } |
| |
| /// Determine whether an instruction may not have a SILDebugScope. |
| bool swift::maybeScopeless(SILInstruction &I) { |
| if (I.getFunction()->isBare()) |
| return true; |
| return !isa<DebugValueInst>(I) && !isa<DebugValueAddrInst>(I); |
| } |