diff --git a/CHANGELOG.md b/CHANGELOG.md index 49acbfa2e7bf9..fc79f27731e14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2891,7 +2891,7 @@ Latest @_silgen_name("foo") @objc ``` - The `,` was vestigial when the attribute syntax consisted of bracked lists. + The `,` was vestigial when the attribute syntax consisted of bracket lists. * `switch` now always requires a statement after a `case` or `default`. @@ -3190,7 +3190,7 @@ Latest ``` * The compiler now warns about cases where a variable is inferred to have - `AnyObject`, `AnyClass`, or `()` type, since type inferrence can turn a simple + `AnyObject`, `AnyClass`, or `()` type, since type inference can turn a simple mistake (e.g. failing to cast an `AnyObject` when you meant to) into something with ripple effects. Here is a simple example: @@ -3386,7 +3386,7 @@ Latest `#if` block. Target configurations are tested against their values via a pseudo-function - invocation expression, taking a single argument expressed as an identitifer. + invocation expression, taking a single argument expressed as an identifier. The argument represents certain static build-time information. There are currently two supported target configurations: @@ -4830,7 +4830,7 @@ Latest ``` checks whether `object` has a value and, if so, asks for the length of - its title. `titleLength` wil have type `Int?`, and if `object` was + its title. `titleLength` will have type `Int?`, and if `object` was missing, the variable will be initialized to None. * Objects with type `id` can now be used as the receiver of property diff --git a/README.md b/README.md index b7e513dc5f16e..2979b70647574 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ uploading SSH keys to GitHub): git clone git@github.com:apple/swift.git cd swift - ./utils/update-checkout --clone-via-ssh + ./utils/update-checkout --clone-with-ssh [CMake](http://cmake.org) is the core infrastructure used to configure builds of Swift and its companion projects; at least version 2.8.12.2 is required. Your diff --git a/docs/doxygen.css b/docs/doxygen.css index 83951f673db8d..d5691ed7e62d1 100644 --- a/docs/doxygen.css +++ b/docs/doxygen.css @@ -313,8 +313,8 @@ HR { height: 1px; /* * LLVM Modifications. - * Note: Everything above here is generated with "doxygen -w htlm" command. See - * "doxygen --help" for details. What follows are CSS overrides for LLVM + * Note: Everything above here is generated with "doxygen -w html" command. See + * "doxygen --help" for details. What follows are CSS overrides for LLVM * specific formatting. We want to keep the above so it can be replaced with * subsequent doxygen upgrades. */ diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index 694e0d54b64e0..393b8154936d2 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -674,6 +674,7 @@ class EnumElementPattern : public Pattern { } SourceRange getSourceRange() const { return {getStartLoc(), getEndLoc()}; } + TypeLoc &getParentType() { return ParentType; } TypeLoc getParentType() const { return ParentType; } static bool classof(const Pattern *P) { diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index 7e965b5180e60..3bcf51cf1ab80 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -45,6 +45,23 @@ namespace swift { class SILLocation; class AnyFunctionRef; +/// How a method is dispatched. +enum class MethodDispatch { + // The method implementation can be referenced statically. + Static, + // The method implementation uses class_method dispatch. + Class, +}; + +/// Get the method dispatch mechanism for a method. +MethodDispatch getMethodDispatch(AbstractFunctionDecl *method); + +/// True if calling the given method or property should use ObjC dispatch. +bool requiresObjCDispatch(ValueDecl *vd); + +/// True if the entry point is natively foreign. +bool requiresForeignToNativeThunk(ValueDecl *vd); + enum ForDefinition_t : bool { NotForDefinition = false, ForDefinition = true diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index e177cf38fa0ea..d3b8af9670362 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -871,7 +871,7 @@ class ApplyInst : public ApplyInstBase { return V->getKind() == ValueKind::ApplyInst; } - /// Returns true if the called function has an error result but is not actully + /// Returns true if the called function has an error result but is not actually /// throwing an error. bool isNonThrowing() const { return isNonThrowingApply(); diff --git a/include/swift/SILPasses/PassManager.h b/include/swift/SILPasses/PassManager.h index 6bbb04dc762f3..21082f20f163c 100644 --- a/include/swift/SILPasses/PassManager.h +++ b/include/swift/SILPasses/PassManager.h @@ -160,6 +160,11 @@ class SILPassManager { /// if the pass manager requested to stop the execution /// of the optimization cycle (this is a debug feature). bool runFunctionPasses(PassList FuncTransforms); + + /// Displays the call graph in an external dot-viewer. + /// This function is meant for use from the debugger. + /// When asserts are disabled, this is a NoOp. + void viewCallGraph(); }; } // end namespace swift diff --git a/include/swift/Sema/TypeCheckRequest.h b/include/swift/Sema/TypeCheckRequest.h index b5767e54cf93b..880d5da1b2c3b 100644 --- a/include/swift/Sema/TypeCheckRequest.h +++ b/include/swift/Sema/TypeCheckRequest.h @@ -70,6 +70,7 @@ class TypeCheckRequest { /// The payload of the request, which differs based on the request kind. union PayloadType { PayloadType() { } + PayloadType(const PayloadType &) {} #define TYPE_CHECK_REQUEST_PAYLOAD(PayloadName,...) \ __VA_ARGS__ PayloadName; diff --git a/lib/AST/ArchetypeBuilder.cpp b/lib/AST/ArchetypeBuilder.cpp index fe7aafddc3f21..f3305d27e3c48 100644 --- a/lib/AST/ArchetypeBuilder.cpp +++ b/lib/AST/ArchetypeBuilder.cpp @@ -854,7 +854,7 @@ bool ArchetypeBuilder::addSameTypeRequirementBetweenArchetypes( T1 = T1->getRepresentative(); T2 = T2->getRepresentative(); - // If the representives are already the same, we're done. + // If the representatives are already the same, we're done. if (T1 == T2) return false; diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index e109506048cf4..a063ac5977c9a 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -543,7 +543,7 @@ ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( bool &diagnoseSuperseded) { // If one entry is fixed and the other is not, we have our answer. if (lhs->isFixed() != rhs->isFixed()) { - // If the non-fixed conformance is not replacable, we have a failure to + // If the non-fixed conformance is not replaceable, we have a failure to // diagnose. diagnoseSuperseded = (lhs->isFixed() && !isReplaceable(rhs->getRankingKind())) || diff --git a/lib/AST/Verifier.cpp b/lib/AST/Verifier.cpp index f657341cf8882..e5361121f59c2 100644 --- a/lib/AST/Verifier.cpp +++ b/lib/AST/Verifier.cpp @@ -436,7 +436,7 @@ struct ASTNodeBase {}; return false; // We should know about archetypes corresponding to opened - // existerntial archetypes. + // existential archetypes. if (archetype->getOpenedExistentialType()) { if (OpenedExistentialArchetypes.count(archetype) == 0) { Out << "Found opened existential archetype " diff --git a/lib/IRGen/GenEnum.cpp b/lib/IRGen/GenEnum.cpp index 39d6ac0649348..472fac3f3ef76 100644 --- a/lib/IRGen/GenEnum.cpp +++ b/lib/IRGen/GenEnum.cpp @@ -4068,29 +4068,30 @@ namespace { return storeDynamicTag(IGF, enumAddr, tag, T); } - auto &C = IGF.IGM.getLLVMContext(); - auto payloadBB = llvm::BasicBlock::Create(C); - auto noPayloadBB = llvm::BasicBlock::Create(C); - auto endBB = llvm::BasicBlock::Create(C); - + // If there are no empty cases, don't need a conditional. if (ElementsWithNoPayload.empty()) { storePayloadTag(IGF, enumAddr, tag, T); return; } + auto &C = IGF.IGM.getLLVMContext(); + auto noPayloadBB = llvm::BasicBlock::Create(C); + auto payloadBB = llvm::BasicBlock::Create(C); + auto endBB = llvm::BasicBlock::Create(C); + llvm::Value *numPayloadCases = llvm::ConstantInt::get(IGF.IGM.Int32Ty, ElementsWithPayload.size()); - llvm::Value *cond = IGF.Builder.CreateICmpULE(tag, numPayloadCases); - IGF.Builder.CreateCondBr(cond, payloadBB, noPayloadBB); + llvm::Value *cond = IGF.Builder.CreateICmpUGE(tag, numPayloadCases); + IGF.Builder.CreateCondBr(cond, noPayloadBB, payloadBB); - IGF.Builder.emitBlock(payloadBB); - storePayloadTag(IGF, enumAddr, tag, T); + IGF.Builder.emitBlock(noPayloadBB); + llvm::Value *noPayloadTag = IGF.Builder.CreateSub(tag, numPayloadCases); + storeNoPayloadTag(IGF, enumAddr, noPayloadTag, T); IGF.Builder.CreateBr(endBB); - IGF.Builder.emitBlock(noPayloadBB); - tag = IGF.Builder.CreateSub(tag, numPayloadCases); - storeNoPayloadTag(IGF, enumAddr, tag, T); + IGF.Builder.emitBlock(payloadBB); + storePayloadTag(IGF, enumAddr, tag, T); IGF.Builder.CreateBr(endBB); IGF.Builder.emitBlock(endBB); diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 59e53a7c75d33..45afe79015076 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -25,6 +25,85 @@ #include "clang/AST/DeclObjC.h" using namespace swift; +/// Get the method dispatch mechanism for a method. +MethodDispatch +swift::getMethodDispatch(AbstractFunctionDecl *method) { + // Final methods can be statically referenced. + if (method->isFinal()) + return MethodDispatch::Static; + // Some methods are forced to be statically dispatched. + if (method->hasForcedStaticDispatch()) + return MethodDispatch::Static; + + // If this declaration is in a class but not marked final, then it is + // always dynamically dispatched. + auto dc = method->getDeclContext(); + if (isa(dc)) + return MethodDispatch::Class; + + // Class extension methods are only dynamically dispatched if they're + // dispatched by objc_msgSend, which happens if they're foreign or dynamic. + if (dc->isClassOrClassExtensionContext()) { + if (method->hasClangNode()) + return MethodDispatch::Class; + if (auto fd = dyn_cast(method)) { + if (fd->isAccessor() && fd->getAccessorStorageDecl()->hasClangNode()) + return MethodDispatch::Class; + } + if (method->getAttrs().hasAttribute()) + return MethodDispatch::Class; + } + + // Otherwise, it can be referenced statically. + return MethodDispatch::Static; +} + +bool swift::requiresForeignToNativeThunk(ValueDecl *vd) { + // Functions imported from C, Objective-C methods imported from Objective-C, + // as well as methods in @objc protocols (even protocols defined in Swift) + // require a foreign to native thunk. + auto dc = vd->getDeclContext(); + if (auto proto = dyn_cast(dc)) + if (proto->isObjC()) + return true; + + if (auto fd = dyn_cast(vd)) + return fd->hasClangNode(); + + return false; +} + +/// FIXME: merge requiresObjCDispatch() into getMethodDispatch() and add +/// an ObjectiveC case to the MethodDispatch enum. +bool swift::requiresObjCDispatch(ValueDecl *vd) { + // Final functions never require ObjC dispatch. + if (vd->isFinal()) + return false; + + if (requiresForeignToNativeThunk(vd)) + return true; + + if (auto *fd = dyn_cast(vd)) { + // Property accessors should be generated alongside the property. + if (fd->isGetterOrSetter()) + return requiresObjCDispatch(fd->getAccessorStorageDecl()); + + return fd->getAttrs().hasAttribute(); + } + + if (auto *cd = dyn_cast(vd)) { + if (cd->hasClangNode()) + return true; + + return cd->getAttrs().hasAttribute(); + } + + if (auto *asd = dyn_cast(vd)) + return asd->requiresObjCGetterAndSetter(); + + return vd->getAttrs().hasAttribute(); +} + static unsigned getFuncNaturalUncurryLevel(AnyFunctionRef AFR) { assert(AFR.getBodyParamPatterns().size() >= 1 && "no arguments for func?!"); unsigned Level = AFR.getBodyParamPatterns().size() - 1; @@ -338,14 +417,8 @@ bool SILDeclRef::isForeignToNativeThunk() const { // have a foreign-to-native thunk. if (!hasDecl()) return false; - // Otherwise, match whether we have a clang node with whether we're foreign. - if (isa(getDecl()) && getDecl()->hasClangNode()) + if (requiresForeignToNativeThunk(getDecl())) return !isForeign; - // Objective-C protocol methods also require a foreign to native thunk. - auto dc = getDecl()->getDeclContext(); - if (auto proto = dyn_cast(dc)) - if (proto->isObjC()) - return !isForeign; // ObjC initializing constructors and factories are foreign. // We emit a special native allocating constructor though. if (isa(getDecl()) diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index fee1db006494b..f8f86de5138bd 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -323,13 +323,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { /// True if the given constructor requires an entry point for ObjC method /// dispatch. bool requiresObjCMethodEntryPoint(ConstructorDecl *constructor); - - /// True if calling the given method or property should use ObjC dispatch. - bool requiresObjCDispatch(ValueDecl *vd); - - /// True if super-calling the given method from a subclass should use ObjC - /// dispatch. - bool requiresObjCSuperDispatch(ValueDecl *vd); /// Emit a global initialization. void emitGlobalInitialization(PatternBindingDecl *initializer, unsigned elt); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 857a754168057..1b6955b05e427 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -29,40 +29,6 @@ using namespace swift; using namespace Lowering; -/// Get the method dispatch mechanism for a method. -MethodDispatch -SILGenFunction::getMethodDispatch(AbstractFunctionDecl *method) { - // Final methods can be statically referenced. - if (method->isFinal()) - return MethodDispatch::Static; - // Some methods are forced to be statically dispatched. - if (method->hasForcedStaticDispatch()) - return MethodDispatch::Static; - - // If this declaration is in a class but not marked final, then it is - // always dynamically dispatched. - auto dc = method->getDeclContext(); - if (isa(dc)) - return MethodDispatch::Class; - - // Class extension methods are only dynamically dispatched if they're - // dispatched by objc_msgSend, which happens if they're foreign or dynamic. - if (auto declaredType = dc->getDeclaredTypeInContext()) - if (declaredType->getClassOrBoundGenericClass()) { - if (method->hasClangNode()) - return MethodDispatch::Class; - if (auto fd = dyn_cast(method)) { - if (fd->isAccessor() && fd->getAccessorStorageDecl()->hasClangNode()) - return MethodDispatch::Class; - } - if (method->getAttrs().hasAttribute()) - return MethodDispatch::Class; - } - - // Otherwise, it can be referenced statically. - return MethodDispatch::Static; -} - static SILDeclRef::Loc getLocForFunctionRef(AnyFunctionRef fn) { if (auto afd = fn.getAbstractFunctionDecl()) { return afd; @@ -539,7 +505,7 @@ class Callee { // make sure we emit the right set of thunks. if (constant->isCurried && Constant.hasDecl()) if (auto func = Constant.getAbstractFunctionDecl()) - if (gen.getMethodDispatch(func) == MethodDispatch::Class) + if (getMethodDispatch(func) == MethodDispatch::Class) constant = constant->asDirectReference(true); constantInfo = gen.getConstantInfo(*constant); @@ -1102,7 +1068,7 @@ class SILGenApply : public Lowering::ExprVisitor { if (e->getAccessSemantics() != AccessSemantics::Ordinary) { isDynamicallyDispatched = false; } else { - switch (SGF.getMethodDispatch(afd)) { + switch (getMethodDispatch(afd)) { case MethodDispatch::Class: isDynamicallyDispatched = true; break; @@ -1121,7 +1087,7 @@ class SILGenApply : public Lowering::ExprVisitor { if (ctor->isRequired() && thisCallSite->getArg()->getType()->is() && !thisCallSite->getArg()->isStaticallyDerivedMetatype()) { - if (SGF.SGM.requiresObjCDispatch(afd)) { + if (requiresObjCDispatch(afd)) { // When we're performing Objective-C dispatch, we don't have an // allocating constructor to call. So, perform an alloc_ref_dynamic // and pass that along to the initializer. @@ -1171,7 +1137,7 @@ class SILGenApply : public Lowering::ExprVisitor { SILDeclRef constant(afd, kind.getValue(), SILDeclRef::ConstructAtBestResilienceExpansion, SILDeclRef::ConstructAtNaturalUncurryLevel, - SGF.SGM.requiresObjCDispatch(afd)); + requiresObjCDispatch(afd)); setCallee(Callee::forClassMethod(SGF, selfValue, constant, getSubstFnType(), e)); @@ -1203,7 +1169,7 @@ class SILGenApply : public Lowering::ExprVisitor { SILDeclRef constant(e->getDecl(), SILDeclRef::ConstructAtBestResilienceExpansion, SILDeclRef::ConstructAtNaturalUncurryLevel, - SGF.SGM.requiresObjCDispatch(e->getDecl())); + requiresObjCDispatch(e->getDecl())); // Otherwise, we have a statically-dispatched call. CanFunctionType substFnType = getSubstFnType(); @@ -1345,7 +1311,7 @@ class SILGenApply : public Lowering::ExprVisitor { constant = SILDeclRef(ctorRef->getDecl(), SILDeclRef::Kind::Initializer, SILDeclRef::ConstructAtBestResilienceExpansion, SILDeclRef::ConstructAtNaturalUncurryLevel, - SGF.SGM.requiresObjCSuperDispatch(ctorRef->getDecl())); + requiresObjCDispatch(ctorRef->getDecl())); if (ctorRef->getDeclRef().isSpecialized()) substitutions = ctorRef->getDeclRef().getSubstitutions(); @@ -1354,7 +1320,7 @@ class SILGenApply : public Lowering::ExprVisitor { constant = SILDeclRef(declRef->getDecl(), SILDeclRef::ConstructAtBestResilienceExpansion, SILDeclRef::ConstructAtNaturalUncurryLevel, - SGF.SGM.requiresObjCSuperDispatch(declRef->getDecl())); + requiresObjCDispatch(declRef->getDecl())); if (declRef->getDeclRef().isSpecialized()) substitutions = declRef->getDeclRef().getSubstitutions(); @@ -1536,12 +1502,12 @@ class SILGenApply : public Lowering::ExprVisitor { : SILDeclRef::Kind::Initializer, SILDeclRef::ConstructAtBestResilienceExpansion, SILDeclRef::ConstructAtNaturalUncurryLevel, - SGF.SGM.requiresObjCDispatch(ctorRef->getDecl())); + requiresObjCDispatch(ctorRef->getDecl())); setCallee(Callee::forArchetype(SGF, SILValue(), self.getType().getSwiftRValueType(), constant, cast(expr->getType()->getCanonicalType()), expr)); - } else if (SGF.getMethodDispatch(ctorRef->getDecl()) + } else if (getMethodDispatch(ctorRef->getDecl()) == MethodDispatch::Class) { // Dynamic dispatch to the initializer. setCallee(Callee::forClassMethod( @@ -1553,7 +1519,7 @@ class SILGenApply : public Lowering::ExprVisitor { : SILDeclRef::Kind::Initializer, SILDeclRef::ConstructAtBestResilienceExpansion, SILDeclRef::ConstructAtNaturalUncurryLevel, - SGF.SGM.requiresObjCDispatch(ctorRef->getDecl())), + requiresObjCDispatch(ctorRef->getDecl())), getSubstFnType(), fn)); } else { // Directly call the peer constructor. @@ -3799,7 +3765,7 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &gen, bool isClassDispatch = false; if (!isDirectUse) { - switch (gen.getMethodDispatch(decl)) { + switch (getMethodDispatch(decl)) { case MethodDispatch::Class: isClassDispatch = true; break; diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index a1ad3a965b8fd..66b46a1e001e1 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -147,14 +147,8 @@ SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, // If the function is fully uncurried and natively foreign, reference its // foreign entry point. if (!next.isCurried) { - if (vd->hasClangNode()) + if (requiresForeignToNativeThunk(vd)) next = next.asForeign(); - - // Objective-C protocol methods also require a foreign to native thunk. - auto dc = vd->getDeclContext(); - if (auto proto = dyn_cast(dc)) - if (proto->isObjC()) - next = next.asForeign(); } // Preserve whether the curry thunks lead to a direct reference to the @@ -620,7 +614,7 @@ static SILValue getNextUncurryLevelRef(SILGenFunction &gen, thisArg = curriedArgs.back(); if (isa(next.getDecl()) && - gen.getMethodDispatch(cast(next.getDecl())) + getMethodDispatch(cast(next.getDecl())) == MethodDispatch::Class) { SILValue thisArg = curriedArgs.back(); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 2692559bc6ad1..182e5b42ce801 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -31,14 +31,6 @@ class LValue; class ManagedValue; class RValue; class TemporaryInitialization; - -/// How a method is dispatched. -enum class MethodDispatch { - // The method implementation can be referenced statically. - Static, - // The method implementation uses class_method dispatch. - Class, -}; /// Internal context information for the SILGenFunction visitor. /// @@ -1540,9 +1532,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// intrinsic. Substitution getPointerSubstitution(Type pointerType, ArchetypeType *archetype); - - /// Get the method dispatch mechanism for a method. - MethodDispatch getMethodDispatch(AbstractFunctionDecl *method); }; diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index fc87a4f37db4c..9a4044bd97715 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -136,47 +136,6 @@ bool SILGenModule::requiresObjCMethodEntryPoint(ConstructorDecl *constructor) { return constructor->isObjC(); } -bool SILGenModule::requiresObjCDispatch(ValueDecl *vd) { - // Final functions never require ObjC dispatch. - if (vd->isFinal()) - return false; - - // If the decl is an @objc protocol requirement, then the only witness is - // objc. - if (auto *proto = dyn_cast(vd->getDeclContext())) - if (proto->isObjC()) - return true; - - if (auto *fd = dyn_cast(vd)) { - // If a function has an associated Clang node, it's foreign and only has - // an ObjC entry point. - if (vd->hasClangNode()) - return true; - - // Property accessors should be generated alongside the property. - if (fd->isGetterOrSetter()) - return requiresObjCDispatch(fd->getAccessorStorageDecl()); - - return fd->getAttrs().hasAttribute(); - } - if (auto *cd = dyn_cast(vd)) { - // If a function has an associated Clang node, it's foreign and only has - // an ObjC entry point. - if (vd->hasClangNode()) - return true; - - return cd->getAttrs().hasAttribute(); - } - if (auto *asd = dyn_cast(vd)) - return asd->requiresObjCGetterAndSetter(); - - return vd->getAttrs().hasAttribute(); -} - -bool SILGenModule::requiresObjCSuperDispatch(ValueDecl *vd) { - return requiresObjCDispatch(vd); -} - namespace { /// An ASTVisitor for populating SILVTable entries from ClassDecl members. diff --git a/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp b/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp index f7e3dcd726ec2..8f8af66121c17 100644 --- a/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp +++ b/lib/SILPasses/EarlySIL/DefiniteInitialization.cpp @@ -1094,6 +1094,8 @@ void LifetimeChecker::handleEscapeUse(const DIMemoryUse &Use) { DiagMessage = diag::variable_closure_use_uninit; else DiagMessage = diag::variable_function_use_uninit; + } else if (isa(Inst)) { + DiagMessage = diag::variable_used_before_initialized; } else { DiagMessage = diag::variable_closure_use_uninit; } diff --git a/lib/SILPasses/PassManager/PassManager.cpp b/lib/SILPasses/PassManager/PassManager.cpp index a5cfc575401b4..2ca03b7e2bb2e 100644 --- a/lib/SILPasses/PassManager/PassManager.cpp +++ b/lib/SILPasses/PassManager/PassManager.cpp @@ -12,6 +12,7 @@ #define DEBUG_TYPE "sil-passmanager" +#include "swift/Basic/DemangleWrappers.h" #include "swift/SILPasses/PassManager.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILModule.h" @@ -20,9 +21,11 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSwitch.h" #include "swift/SILAnalysis/FunctionOrder.h" +#include "swift/SILAnalysis/BasicCalleeAnalysis.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/TimeValue.h" +#include "llvm/Support/GraphWriter.h" using namespace swift; @@ -445,4 +448,193 @@ void SILPassManager::addPassForName(StringRef Name) { addPass(P); } +//===----------------------------------------------------------------------===// +// View Call-Graph Implementation +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG + +namespace { + + /// An explicit graph data structure for the call graph. + /// Used for viewing the callgraph as dot file with llvm::ViewGraph. + struct CallGraph { + + struct Node; + + struct Edge { + FullApplySite FAS; + Node *Child; + bool Incomplete; + }; + + struct Node { + SILFunction *F; + CallGraph *CG; + int NumCallSites = 0; + SmallVector Children; + }; + + struct child_iterator : public std::iterator { + SmallVectorImpl::iterator baseIter; + + child_iterator(SmallVectorImpl::iterator baseIter) : + baseIter(baseIter) + { } + + child_iterator &operator++() { baseIter++; return *this; } + child_iterator operator++(int) { auto tmp = *this; baseIter++; return tmp; } + Node *operator*() const { return baseIter->Child; } + bool operator==(const child_iterator &RHS) const { + return baseIter == RHS.baseIter; + } + bool operator!=(const child_iterator &RHS) const { + return baseIter != RHS.baseIter; + } + difference_type operator-(const child_iterator &RHS) const { + return baseIter - RHS.baseIter; + } + }; + + CallGraph(SILModule *M, BasicCalleeAnalysis *BCA); + + std::vector Nodes; + + /// The SILValue IDs which are printed as edge source labels. + llvm::DenseMap InstToIDMap; + + typedef std::vector::iterator iterator; + }; + + CallGraph::CallGraph(SILModule *M, BasicCalleeAnalysis *BCA) { + Nodes.resize(M->getFunctionList().size()); + llvm::DenseMap NodeMap; + int idx = 0; + for (SILFunction &F : *M) { + Node &Nd = Nodes[idx++]; + Nd.F = &F; + Nd.CG = this; + NodeMap[&F] = &Nd; + + F.numberValues(InstToIDMap); + } + + for (Node &Nd : Nodes) { + for (SILBasicBlock &BB : *Nd.F) { + for (SILInstruction &I : BB) { + if (FullApplySite FAS = FullApplySite::isa(&I)) { + auto CList = BCA->getCalleeList(FAS); + for (SILFunction *Callee : CList) { + Node *CalleeNode = NodeMap[Callee]; + Nd.Children.push_back({FAS, CalleeNode,CList.isIncomplete()}); + } + } + } + } + } + } + +} // end swift namespace + +namespace llvm { + + /// Wraps a dot node label string to multiple lines. The \p NumEdgeLabels + /// gives an estimate on the minimum width of the node shape. + static void wrap(std::string &Str, int NumEdgeLabels) { + unsigned ColNum = 0; + unsigned LastSpace = 0; + unsigned MaxColumns = std::max(60, NumEdgeLabels * 8); + for (unsigned i = 0; i != Str.length(); ++i) { + if (ColNum == MaxColumns) { + if (!LastSpace) + LastSpace = i; + Str.insert(LastSpace + 1, "\\l"); + ColNum = i - LastSpace - 1; + LastSpace = 0; + } else + ++ColNum; + if (Str[i] == ' ' || Str[i] == '.') + LastSpace = i; + } + } + /// CallGraph GraphTraits specialization so the CallGraph can be + /// iterable by generic graph iterators. + template <> struct GraphTraits { + typedef CallGraph::Node NodeType; + typedef CallGraph::child_iterator ChildIteratorType; + + static NodeType *getEntryNode(NodeType *N) { return N; } + static inline ChildIteratorType child_begin(NodeType *N) { + return N->Children.begin(); + } + static inline ChildIteratorType child_end(NodeType *N) { + return N->Children.end(); + } + }; + + template <> struct GraphTraits + : public GraphTraits { + typedef CallGraph *GraphType; + + static NodeType *getEntryNode(GraphType F) { return nullptr; } + + typedef CallGraph::iterator nodes_iterator; + static nodes_iterator nodes_begin(GraphType CG) { + return CG->Nodes.begin(); + } + static nodes_iterator nodes_end(GraphType CG) { return CG->Nodes.end(); } + static unsigned size(GraphType CG) { return CG->Nodes.size(); } + }; + + /// This is everything the llvm::GraphWriter needs to write the call graph in + /// a dot file. + template <> + struct DOTGraphTraits : public DefaultDOTGraphTraits { + + DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} + + std::string getNodeLabel(const CallGraph::Node *Node, + const CallGraph *Graph) { + std::string Label = Node->F->getName(); + wrap(Label, Node->NumCallSites); + return Label; + } + + std::string getNodeDescription(const CallGraph::Node *Node, + const CallGraph *Graph) { + std::string Label = demangle_wrappers:: + demangleSymbolAsString(Node->F->getName()); + wrap(Label, Node->NumCallSites); + return Label; + } + + static std::string getEdgeSourceLabel(const CallGraph::Node *Node, + CallGraph::child_iterator I) { + std::string Label; + raw_string_ostream O(Label); + SILInstruction *Inst = I.baseIter->FAS.getInstruction(); + O << '%' << Node->CG->InstToIDMap[Inst]; + return Label; + } + + static std::string getEdgeAttributes(const CallGraph::Node *Node, + CallGraph::child_iterator I, + const CallGraph *Graph) { + CallGraph::Edge *Edge = I.baseIter; + if (Edge->Incomplete) + return "color=\"red\""; + return ""; + } + }; +} // end llvm namespace +#endif + +void SILPassManager::viewCallGraph() { + /// When asserts are disabled, this should be a NoOp. +#ifndef NDEBUG + CallGraph OCG(getModule(), getAnalysis()); + llvm::ViewGraph(&OCG, "callgraph"); +#endif +} diff --git a/lib/SILPasses/Scalar/StackPromotion.cpp b/lib/SILPasses/Scalar/StackPromotion.cpp index d17ec071b3fda..73752b08e17f5 100644 --- a/lib/SILPasses/Scalar/StackPromotion.cpp +++ b/lib/SILPasses/Scalar/StackPromotion.cpp @@ -251,7 +251,8 @@ SILFunction *StackPromoter::getBufferAllocFunc(SILFunction *OrigFunc, "swift_bufferAllocateOnStack", OrigFunc->getLinkage(), OrigFunc->getLoweredFunctionType(), - OrigFunc->isBare(), IsNotTransparent, IsNotFragile); + OrigFunc->isBare(), IsNotTransparent, + OrigFunc->isFragile()); } return BufferAllocFunc; } @@ -279,7 +280,7 @@ SILFunction *StackPromoter::getBufferDeallocFunc(SILFunction *OrigFunc, "swift_bufferDeallocateFromStack", OrigFunc->getLinkage(), FunTy, - OrigFunc->isBare(), IsNotTransparent, IsNotFragile); + OrigFunc->isBare(), IsNotTransparent, OrigFunc->isFragile()); } return BufferDeallocFunc; } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1fb4a3a89fc59..e60a149900478 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -295,6 +295,7 @@ namespace { DeclContext *dc; const Solution &solution; bool SuppressDiagnostics; + bool SkipClosures; private: /// \brief Coerce the given tuple to another tuple type. @@ -1468,9 +1469,10 @@ namespace { public: ExprRewriter(ConstraintSystem &cs, const Solution &solution, - bool suppressDiagnostics) + bool suppressDiagnostics, bool skipClosures) : cs(cs), dc(cs.DC), solution(solution), - SuppressDiagnostics(suppressDiagnostics) { } + SuppressDiagnostics(suppressDiagnostics), + SkipClosures(skipClosures) { } ConstraintSystem &getConstraintSystem() const { return cs; } @@ -5646,6 +5648,11 @@ namespace { ExprWalker(ExprRewriter &Rewriter) : Rewriter(Rewriter) { } ~ExprWalker() { + // If we're re-typechecking an expression for diagnostics, don't + // visit closures that have non-single expression bodies. + if (Rewriter.SkipClosures) + return; + auto &cs = Rewriter.getConstraintSystem(); auto &tc = cs.getTypeChecker(); for (auto *closure : closuresToTypeCheck) @@ -6126,7 +6133,8 @@ bool ConstraintSystem::applySolutionFix(Expr *expr, Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, Type convertType, bool discardedExpr, - bool suppressDiagnostics) { + bool suppressDiagnostics, + bool skipClosures) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { @@ -6141,7 +6149,7 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, return nullptr; } - ExprRewriter rewriter(*this, solution, suppressDiagnostics); + ExprRewriter rewriter(*this, solution, suppressDiagnostics, skipClosures); ExprWalker walker(rewriter); // Apply the solution to the expression. @@ -6171,7 +6179,8 @@ Expr *ConstraintSystem::applySolution(Solution &solution, Expr *expr, Expr *ConstraintSystem::applySolutionShallow(const Solution &solution, Expr *expr, bool suppressDiagnostics) { - ExprRewriter rewriter(*this, solution, suppressDiagnostics); + ExprRewriter rewriter(*this, solution, suppressDiagnostics, + /*skipClosures=*/false); rewriter.walkToExprPre(expr); Expr *result = rewriter.walkToExprPost(expr); if (result) @@ -6183,7 +6192,9 @@ Expr *Solution::coerceToType(Expr *expr, Type toType, ConstraintLocator *locator, bool ignoreTopLevelInjection) const { auto &cs = getConstraintSystem(); - ExprRewriter rewriter(cs, *this, /*suppressDiagnostics=*/false); + ExprRewriter rewriter(cs, *this, + /*suppressDiagnostics=*/false, + /*skipClosures=*/false); Expr *result = rewriter.coerceToType(expr, toType, locator); if (!result) return nullptr; @@ -6309,7 +6320,9 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, } Solution &solution = solutions.front(); - ExprRewriter rewriter(cs, solution, /*suppressDiagnostics=*/false); + ExprRewriter rewriter(cs, solution, + /*suppressDiagnostics=*/false, + /*skipClosures=*/false); auto memberRef = rewriter.buildMemberRef(base, openedFullType, base->getStartLoc(), diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 4cae243df7772..3f0fb751239ec 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -1702,7 +1702,11 @@ bool CalleeCandidateInfo::diagnoseAnyStructuralArgumentError(Expr *fnExpr, auto args = decomposeArgParamType(argExpr->getType()); - auto params = decomposeArgParamType(candidates[0].getArgumentType()); + + auto argTy = candidates[0].getArgumentType(); + if (!argTy) return false; + + auto params = decomposeArgParamType(argTy); // It is a somewhat common error to try to access an instance method as a // curried member on the type, instead of using an instance, e.g. the user @@ -2897,6 +2901,10 @@ typeCheckChildIndependently(Expr *subExpr, Type convertType, // the context is missing). TypeCheckExprOptions TCEOptions = TypeCheckExprFlags::DisableStructuralChecks; + // Don't walk into non-single expression closure bodies, because + // ExprTypeSaver and TypeNullifier skip them too. + TCEOptions |= TypeCheckExprFlags::SkipMultiStmtClosures; + // Claim that the result is discarded to preserve the lvalue type of // the expression. if (options.contains(TCC_AllowLValue)) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c416e400ef85e..9f0fdb7ad1172 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2368,9 +2368,14 @@ class ConstraintSystem { /// /// \param convertType the contextual type to which the /// expression should be converted, if any. + /// \param discardedExpr if true, the result of the expression + /// is contextually ignored. + /// \param skipClosures if true, don't descend into bodies of + /// non-single expression closures. Expr *applySolution(Solution &solution, Expr *expr, Type convertType, bool discardedExpr, - bool suppressDiagnostics); + bool suppressDiagnostics, + bool skipClosures); /// \brief Apply a given solution to the expression to the top-level /// expression, producing a fully type-checked expression. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index fa365becc67db..7de87eb8b1abb 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1188,8 +1188,9 @@ bool TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // Apply the solution to the expression. auto &solution = viable[0]; bool isDiscarded = options.contains(TypeCheckExprFlags::IsDiscarded); + bool skipClosures = options.contains(TypeCheckExprFlags::SkipMultiStmtClosures); auto result = cs.applySolution(solution, expr, convertType, isDiscarded, - suppressDiagnostics); + suppressDiagnostics, skipClosures); if (!result) { // Failure already diagnosed, above, as part of applying the solution. return true; diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index e212e119d444d..3ba60bcec6457 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1280,17 +1280,17 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type, // type as `.Foo`), resolve it now that we have a type. Optional castKind; + EnumElementDecl *elt = EEP->getElementDecl(); + Type enumTy; - if (!EEP->getElementDecl()) { - EnumElementDecl *element = nullptr; + if (!elt) { if (type->getAnyNominal()) - element = lookupEnumMemberElement(*this, dc, type, EEP->getName()); - if (!element) { + elt = lookupEnumMemberElement(*this, dc, type, EEP->getName()); + if (!elt) { diagnose(EEP->getLoc(), diag::enum_element_pattern_member_not_found, EEP->getName().str(), type); return true; } - EEP->setElementDecl(element); enumTy = type; } else { // Check if the explicitly-written enum type matches the type we're @@ -1337,8 +1337,6 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type, } } - EnumElementDecl *elt = EEP->getElementDecl(); - // If there is a subpattern, push the enum element type down onto it. if (EEP->hasSubPattern()) { Type elementType; @@ -1355,6 +1353,8 @@ bool TypeChecker::coercePatternToType(Pattern *&P, DeclContext *dc, Type type, return true; EEP->setSubPattern(sub); } + + EEP->setElementDecl(elt); EEP->setType(enumTy); // Ensure that the type of our TypeLoc is fully resolved. If an unbound diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 131fae43db351..53d55d969d2b4 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -194,6 +194,10 @@ enum class TypeCheckExprFlags { /// statement. This should only be used for syntactic restrictions, and should /// not affect type checking itself. IsExprStmt = 0x20, + + /// If set, this expression is being re-type checked as part of diagnostics, + /// and so we should not visit bodies of non-single expression closures. + SkipMultiStmtClosures = 0x40, }; typedef OptionSet TypeCheckExprOptions; diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 0a6deac3158f9..65bfd2a3539a8 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -167,7 +167,6 @@ namespace { << " for layout " << Layout::Code << "\n"); } - // TODO: this is not required anymore. Remove it. bool ShouldSerializeAll; /// Helper function to update ListOfValues for MethodInst. Format: diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index b173e82783b7d..7d94e4f59b5d5 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -162,3 +162,14 @@ func r15998821() { var _: (Int,Int)-> Int = {$0+$1+$2} // expected-error {{contextual closure type '(Int, Int) -> Int' expects 2 arguments, but 3 were used in closure body}} +// Crash when re-typechecking bodies of non-single expression closures + +struct CC {} +func callCC(f: CC -> U) -> () {} + +func typeCheckMultiStmtClosureCrash() { + callCC { // expected-error {{cannot invoke 'callCC' with an argument list of type '((CC) -> _)'}} + _ = $0 // expected-note@-1 {{expected an argument list of type '(CC -> U)'}} + return 1 + } +} diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 696e869727d45..1a677cdb08734 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -117,7 +117,7 @@ func rdar20142523() { map(0..<10, { x in // expected-error{{cannot invoke 'map' with an argument list of type '(Range, (_) -> _)'}} // expected-note @-1 {{overloads for 'map' exist with these partially matching parameter lists: (C, (C.Generator.Element) -> T), (T?, @noescape (T) -> U)}} () - return x // expected-error {{type of expression is ambiguous without more context}} + return x }) } @@ -425,8 +425,7 @@ func f20371273() { // FIXME: Should complain about not having a return type annotation in the closure. [0].map { _ in let r = (1,2).0; return r } // expected-error @-1 {{cannot invoke 'map' with an argument list of type '(@noescape (Int) throws -> _)'}} -// expected-error @-2 {{cannot convert return expression of type 'Int' to return type 'T'}} -// expected-note @-3 {{expected an argument list of type '(@noescape Int throws -> T)'}} +// expected-note @-2 {{expected an argument list of type '(@noescape Int throws -> T)'}} // Less than useful error message when using map on optional dictionary type func rdar21078316() { diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 6b1b4b8df0041..c7a0c3ef6f0ef 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -176,5 +176,54 @@ var (let y) = 42 // expected-error {{'let' cannot appear nested inside another let (var z) = 42 // expected-error {{'var' cannot appear nested inside another 'var' or 'let' pattern}} +// Crash when re-typechecking EnumElementPattern. +// FIXME: This should actually type-check -- the diagnostics are bogus. But +// at least we don't crash anymore. +protocol PP { + typealias E +} + +struct A : PP { + typealias E = T +} + +extension PP { + func map(f: Self.E -> T) -> T {} +} + +enum EE { + case A + case B +} + +func good(a: A) -> Int { + return a.map { + switch $0 { + case .A: + return 1 + default: + return 2 + } + } +} +func bad(a: A) { + a.map { // expected-error {{cannot invoke 'map' with an argument list of type '((EE) -> _)'}} + // expected-note@-1 {{expected an argument list of type '(EE -> T)'}} + let _: EE = $0 + return 1 + } +} + +func ugly(a: A) { + a.map { // expected-error {{cannot invoke 'map' with an argument list of type '((EE) -> _)'}} + // expected-note@-1 {{expected an argument list of type '(EE -> T)'}} + switch $0 { + case .A: + return 1 + default: + return 2 + } + } +} diff --git a/test/Constraints/subscript.swift b/test/Constraints/subscript.swift index 21d015635ec65..0dd8afcc8af05 100644 --- a/test/Constraints/subscript.swift +++ b/test/Constraints/subscript.swift @@ -74,7 +74,7 @@ let _ = 1["1"] // expected-error {{ambiguous use of 'subscript'}} // rdar://17687826 - QoI: error message when reducing to an untyped dictionary isn't helpful let squares = [ 1, 2, 3 ].reduce([:]) { (dict, n) in // expected-error {{cannot invoke 'reduce' with an argument list of type '([_ : _], @noescape (_, Int) throws -> _)'}} // expected-note @-1 {{expected an argument list of type '(T, combine: @noescape (T, Int) throws -> T)'}} - var dict = dict // expected-error {{type of expression is ambiguous without more context}} + var dict = dict dict[n] = n * n return dict diff --git a/test/IRGen/enum_value_semantics.sil b/test/IRGen/enum_value_semantics.sil index 4bf74df07cd7f..d56de80e57e67 100644 --- a/test/IRGen/enum_value_semantics.sil +++ b/test/IRGen/enum_value_semantics.sil @@ -358,17 +358,8 @@ bb0(%0 : $SinglePayloadNontrivial): // -- MultiPayloadTrivial destructiveInjectEnumTag // CHECK-LABEL: define linkonce_odr hidden void @_TwuiO20enum_value_semantics19MultiPayloadTrivial // CHECK: [[SELF:%.*]] = bitcast %swift.opaque* %value to %O20enum_value_semantics19MultiPayloadTrivial* -// CHECK-NEXT: [[IS_PAYLOAD:%.*]] = icmp ule i32 %tag, 2 -// CHECK-NEXT: br i1 [[IS_PAYLOAD]], label %[[HAS_PAYLOAD:.*]], label %[[HAS_NO_PAYLOAD:.*]] - -// CHECK: ;