From 362a25ae84ca4f56b4dfb810b6f9ecc438c0439a Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 15 Jan 2020 16:15:34 -0800 Subject: [PATCH 01/47] [AST] Support SILFunctionType/SILBlockStorageType in ClangTypeConverter. This translation is a pre-requisite to storing Clang types in SILFunctionType. --- lib/AST/ClangTypeConverter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index d4ccea512f069..2a08f78fb282e 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -576,12 +576,19 @@ clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type) { } clang::QualType ClangTypeConverter::visitSILFunctionType(SILFunctionType *type) { - llvm::report_fatal_error("Expected only AST types but found a SIL function."); + // We must've already computed it before if applicable. + return clang::QualType(type->getClangTypeInfo().getType(), 0); } clang::QualType ClangTypeConverter::visitSILBlockStorageType(SILBlockStorageType *type) { - llvm::report_fatal_error("Expected only AST types but found a SIL block."); + // We'll select (void)(^)(). This isn't correct for all blocks, but block + // storage type should only be converted for function signature lowering, + // where the parameter types do not matter. + auto &clangCtx = ClangASTContext; + auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy); + auto blockTy = clangCtx.getBlockPointerType(fnTy); + return clangCtx.getCanonicalType(blockTy); } clang::QualType From d287963c4d175dfdad824d2628df525f87295bea Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 26 Aug 2020 19:27:21 -0700 Subject: [PATCH 02/47] [sil-mode] Change viewcfg integration to specify dot as a renderer. --- utils/sil-mode.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/sil-mode.el b/utils/sil-mode.el index fe29c99d9b58b..59cc44ba0c4c7 100644 --- a/utils/sil-mode.el +++ b/utils/sil-mode.el @@ -259,6 +259,7 @@ ;; *NOTE* viewcfg must be in the $PATH and .dot files should be associated with ;; the graphviz app. (defvar sil-mode-viewcfg-program-name "viewcfg") +(defvar sil-mode-viewcfg-renderer "dot") (defvar sil-mode-viewcfg-buffer-name "*viewcfg*") (defcustom sil-mode-viewcfg-command-default "viewcfg" @@ -281,7 +282,8 @@ (process-connection-type nil)) (let ((p (start-process sil-mode-viewcfg-program-name sil-mode-viewcfg-buffer-name - sil-mode-viewcfg-command))) + sil-mode-viewcfg-command + (concat "--renderer=" sil-mode-viewcfg-renderer)))) (process-send-region p brace-start brace-end) (process-send-eof p))))) From 80560dab2582172e0684080382586591a55cb342 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 27 Aug 2020 22:17:09 +0300 Subject: [PATCH 03/47] CSGen: Infer generic arguments in explicit closure result types --- lib/Sema/CSGen.cpp | 14 +++++++++----- test/Constraints/closures.swift | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 0891e23c6474c..37bc51c12508b 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2073,12 +2073,14 @@ namespace { return resolveTypeReferenceInExpression( closure->getExplicitResultTypeRepr(), - TypeResolverContext::InExpression, nullptr); + TypeResolverContext::InExpression, + // Introduce type variables for unbound generics. + OpenUnboundGenericType( + CS, CS.getConstraintLocator(closure, + ConstraintLocator::ClosureResult))); }; Type resultTy; - auto *resultLoc = - CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult); if (auto explicityTy = getExplicitResultType()) { resultTy = explicityTy; } else { @@ -2099,9 +2101,11 @@ namespace { // If this is a multi-statement closure, let's mark result // as potential hole right away. resultTy = CS.createTypeVariable( - resultLoc, + CS.getConstraintLocator(closure, + ConstraintLocator::ClosureResult), shouldTypeCheckInEnclosingExpression(closure) - ? 0 : TVO_CanBindToHole); + ? 0 + : TVO_CanBindToHole); } } diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index b6f99b7350729..0b77ce6ac87b2 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1030,3 +1030,15 @@ func sr12815() { .doesntExist2() { $0 } } } + +// Make sure we can infer generic arguments in an explicit result type. +let explicitUnboundResult1 = { () -> Array in [0] } +let explicitUnboundResult2: (Array) -> Array = { + (arr: Array) -> Array in [0] +} +// FIXME: Should we prioritize the contextual result type and infer Array +// rather than using a type variable in these cases? +// expected-error@+1 {{unable to infer closure type in the current context}} +let explicitUnboundResult3: (Array) -> Array = { + (arr: Array) -> Array in [true] +} From 61d86d5fd2652103cb09d6b2fe7dcb48c3696104 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 27 Aug 2020 22:23:19 +0300 Subject: [PATCH 04/47] [NFC] CSGen: Clean up some flow in inferClosureType --- lib/Sema/CSGen.cpp | 70 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 37bc51c12508b..f4dff643717aa 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2062,52 +2062,38 @@ namespace { // parameter or return type is omitted, a fresh type variable is used to // stand in for that parameter or return type, allowing it to be inferred // from context. - auto getExplicitResultType = [&]() -> Type { - if (!closure->hasExplicitResultType()) { - return Type(); - } + Type resultTy = [&] { + if (closure->hasExplicitResultType()) { + if (auto declaredTy = closure->getExplicitResultType()) { + return declaredTy; + } - if (auto declaredTy = closure->getExplicitResultType()) { - return declaredTy; + const auto resolvedTy = resolveTypeReferenceInExpression( + closure->getExplicitResultTypeRepr(), + TypeResolverContext::InExpression, + // Introduce type variables for unbound generics. + OpenUnboundGenericType( + CS, CS.getConstraintLocator( + closure, ConstraintLocator::ClosureResult))); + if (resolvedTy) + return resolvedTy; } - return resolveTypeReferenceInExpression( - closure->getExplicitResultTypeRepr(), - TypeResolverContext::InExpression, - // Introduce type variables for unbound generics. - OpenUnboundGenericType( - CS, CS.getConstraintLocator(closure, - ConstraintLocator::ClosureResult))); - }; - - Type resultTy; - if (auto explicityTy = getExplicitResultType()) { - resultTy = explicityTy; - } else { - auto getContextualResultType = [&]() -> Type { - if (auto contextualType = CS.getContextualType(closure)) { - if (auto fnType = contextualType->getAs()) - return fnType->getResult(); - } - return Type(); - }; - - if (auto contextualResultTy = getContextualResultType()) { - resultTy = contextualResultTy; - } else { - // If no return type was specified, create a fresh type - // variable for it and mark it as possible hole. - // - // If this is a multi-statement closure, let's mark result - // as potential hole right away. - resultTy = CS.createTypeVariable( - CS.getConstraintLocator(closure, - ConstraintLocator::ClosureResult), - shouldTypeCheckInEnclosingExpression(closure) - ? 0 - : TVO_CanBindToHole); + if (auto contextualType = CS.getContextualType(closure)) { + if (auto fnType = contextualType->getAs()) + return fnType->getResult(); } - } + + // If no return type was specified, create a fresh type + // variable for it and mark it as possible hole. + // + // If this is a multi-statement closure, let's mark result + // as potential hole right away. + return Type(CS.createTypeVariable( + CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult), + shouldTypeCheckInEnclosingExpression(closure) ? 0 + : TVO_CanBindToHole)); + }(); return FunctionType::get(closureParams, resultTy, extInfo); } From 84c50655472e5bf5329ddc5e3d9f0386acfbd07e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 27 Aug 2020 12:48:41 -0700 Subject: [PATCH 05/47] [SR-13461] Relax An Assert This assert doesn't consider reference storage types in its predicate. Look through them since they're not relevant to the type consistency check it's trying to pick out. --- lib/SILGen/SILGenConstructor.cpp | 8 +++----- .../compiler_crashers_2_fixed/sr13461.swift | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 validation-test/compiler_crashers_2_fixed/sr13461.swift diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 0aef4de8034b3..0f4866879fb90 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -216,11 +216,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, .forwardInto(SGF, Loc, init.get()); ++elti; } else { -#ifndef NDEBUG - assert( - field->getType()->isEqual(field->getParentInitializer()->getType()) - && "Checked by sema"); -#endif + assert(field->getType()->getReferenceStorageReferent()->isEqual( + field->getParentInitializer()->getType()) && + "Initialization of field with mismatched type!"); // Cleanup after this initialization. FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); diff --git a/validation-test/compiler_crashers_2_fixed/sr13461.swift b/validation-test/compiler_crashers_2_fixed/sr13461.swift new file mode 100644 index 0000000000000..b6bda64bdce53 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr13461.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -o /dev/null %s +// REQUIRES: asserts + +final class Klass { + static var current: Klass { + fatalError() + } +} +private struct Build { + let val: T + unowned let unownedBinding = Klass.current + unowned(unsafe) let unownedUnsafeBinding = Klass.current + weak var weakBinding = Klass.current +} +private func phase(_ val: T) -> Build { + return Build(val: val) +} From c890cdd1a379db55e51d7ec8657d55b6dc2e7ba4 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 27 Jul 2020 22:01:54 -0700 Subject: [PATCH 06/47] [AST] Add functionality for computing Clang types for SIL functions. --- include/swift/AST/ASTContext.h | 14 ++++++++++ lib/AST/ASTContext.cpp | 24 ++++++++++++---- lib/AST/ClangTypeConverter.cpp | 51 ++++++++++++++++++++++++++++++++++ lib/AST/ClangTypeConverter.h | 5 ++++ lib/IRGen/GenClangType.cpp | 10 +++---- 5 files changed, 94 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 9281ebcf55c2b..e3ecfbb158dfb 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -62,6 +62,7 @@ namespace swift { class BoundGenericType; class ClangModuleLoader; class ClangNode; + class ClangTypeConverter; class ConcreteDeclRef; class ConstructorDecl; class Decl; @@ -586,6 +587,10 @@ class ASTContext final { Type getBridgedToObjC(const DeclContext *dc, Type type, Type *bridgedValueType = nullptr) const; +private: + ClangTypeConverter &getClangTypeConverter(); + +public: /// Get the Clang type corresponding to a Swift function type. /// /// \param params The function parameters. @@ -595,6 +600,15 @@ class ASTContext final { getClangFunctionType(ArrayRef params, Type resultTy, FunctionTypeRepresentation trueRep); + /// Get the canonical Clang type corresponding to a SIL function type. + /// + /// SIL analog of \c ASTContext::getClangFunctionType . + const clang::Type * + getCanonicalClangFunctionType( + ArrayRef params, Optional result, + const SILFunctionType::ExtInfo incompleteExtInfo, + SILFunctionType::Representation trueRep); + /// Get the Swift declaration that a Clang declaration was exported from, /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 633527ccf7735..b8a0f69ccd747 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4457,16 +4457,30 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, return Type(); } -const clang::Type * -ASTContext::getClangFunctionType(ArrayRef params, - Type resultTy, - FunctionTypeRepresentation trueRep) { +ClangTypeConverter &ASTContext::getClangTypeConverter() { auto &impl = getImpl(); if (!impl.Converter) { auto *cml = getClangModuleLoader(); impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); } - return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep); + return impl.Converter.getValue(); +} + +const clang::Type * +ASTContext::getClangFunctionType(ArrayRef params, + Type resultTy, + FunctionTypeRepresentation trueRep) { + return getClangTypeConverter().getFunctionType(params, resultTy, trueRep); +} + +const clang::Type * +ASTContext::getCanonicalClangFunctionType( + ArrayRef params, + Optional result, + SILFunctionType::ExtInfo incompleteExtInfo, + SILFunctionType::Representation trueRep) { + auto *ty = getClangTypeConverter().getFunctionType(params, result, trueRep); + return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; } const Decl * diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 2a08f78fb282e..9fd13f797798d 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -158,6 +158,57 @@ const clang::Type *ClangTypeConverter::getFunctionType( llvm_unreachable("invalid representation"); } +const clang::Type *ClangTypeConverter::getFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation repr) { + + // Using the interface type is sufficient as type parameters get mapped to + // `id`, since ObjC lightweight generics use type erasure. (See also: SE-0057) + auto resultClangTy = result.hasValue() + ? convert(result.getValue().getInterfaceType()) + : ClangASTContext.VoidTy; + + if (resultClangTy.isNull()) + return nullptr; + + SmallVector extParamInfos; + SmallVector paramsClangTy; + bool someParamIsConsumed = false; + for (auto &p : params) { + auto pc = convert(p.getInterfaceType()); + if (pc.isNull()) + return nullptr; + clang::FunctionProtoType::ExtParameterInfo extParamInfo; + if (p.isConsumed()) { + someParamIsConsumed = true; + extParamInfo = extParamInfo.withIsConsumed(true); + } + extParamInfos.push_back(extParamInfo); + paramsClangTy.push_back(pc); + } + + clang::FunctionProtoType::ExtProtoInfo info(clang::CallingConv::CC_C); + if (someParamIsConsumed) + info.ExtParameterInfos = extParamInfos.begin(); + auto fn = ClangASTContext.getFunctionType(resultClangTy, paramsClangTy, info); + if (fn.isNull()) + return nullptr; + + switch (repr) { + case SILFunctionType::Representation::CFunctionPointer: + return ClangASTContext.getPointerType(fn).getTypePtr(); + case SILFunctionType::Representation::Block: + return ClangASTContext.getBlockPointerType(fn).getTypePtr(); + case SILFunctionType::Representation::Thick: + case SILFunctionType::Representation::Thin: + case SILFunctionType::Representation::Method: + case SILFunctionType::Representation::ObjCMethod: + case SILFunctionType::Representation::WitnessMethod: + case SILFunctionType::Representation::Closure: + llvm_unreachable("Expected a C-compatible representation."); + } +} + clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, StringRef memberName) { auto memberTypeDecl = cast( diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index 450b9e30cf19a..e41a824163312 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -74,6 +74,11 @@ class ClangTypeConverter : ArrayRef params, Type resultTy, AnyFunctionType::Representation repr); + /// Compute the C function type for a SIL function type. + const clang::Type *getFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation repr); + /// Check whether the given Clang declaration is an export of a Swift /// declaration introduced by this converter, and if so, return the original /// Swift declaration. diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index bc788fab6904a..8bc35dd99c134 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -132,10 +132,10 @@ namespace { /// ABI. class GenClangType : public CanTypeVisitor { IRGenModule &IGM; - ClangTypeConverter &Converter; + irgen::ClangTypeConverter &Converter; public: - GenClangType(IRGenModule &IGM, ClangTypeConverter &converter) + GenClangType(IRGenModule &IGM, irgen::ClangTypeConverter &converter) : IGM(IGM), Converter(converter) {} const clang::ASTContext &getClangASTContext() const { @@ -264,8 +264,8 @@ static clang::CanQualType getClangBuiltinTypeFromTypedef( } clang::CanQualType -ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, - CanStructType type) { +irgen::ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, + CanStructType type) { // Handle builtin types by adding entries to the cache that reverse // the mapping done by the importer. We could try to look at the // members of the struct instead, but even if that's ABI-equivalent @@ -748,7 +748,7 @@ clang::CanQualType GenClangType::visitType(CanType type) { llvm_unreachable("Unexpected type in Clang type generation."); } -clang::CanQualType ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { +clang::CanQualType irgen::ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { // Look in the cache. auto it = Cache.find(type); if (it != Cache.end()) { From 1807412e85f5e9fc274e254ad029ebb199f8a3fc Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 14:44:13 -0700 Subject: [PATCH 07/47] [Serialization] Allow different Clang types in deserialization. We could've stored a function pointer or a block pointer or a function reference. --- lib/Serialization/Deserialization.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index ed98ea9976f26..724aef5da6b76 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5413,16 +5413,12 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); - const clang::FunctionType *clangFunctionType = nullptr; + const clang::Type *clangFunctionType = nullptr; if (clangFunctionTypeID) { auto clangType = MF.getClangType(clangFunctionTypeID); if (!clangType) return clangType.takeError(); - // FIXME: allow block pointers here. - clangFunctionType = - dyn_cast_or_null(clangType.get()); - if (!clangFunctionType) - MF.fatal(); + clangFunctionType = clangType.get(); } auto extInfo = From eaac23fdd4dbb2f35c160dabf89ea54557701950 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 15:14:51 -0700 Subject: [PATCH 08/47] [NFC] Remove ASTExtInfo::assertIsFunctionType in favor of checkInvariants. Invariants should be checked only when calling build(), not when the builder itself is created. --- include/swift/AST/ExtInfo.h | 10 +--------- lib/AST/ExtInfo.cpp | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 51592181aabf2..02c78efccf68f 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -194,16 +194,8 @@ class ASTExtInfoBuilder { using Representation = FunctionTypeRepresentation; - static void assertIsFunctionType(const clang::Type *); - ASTExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) - : bits(bits), clangTypeInfo(clangTypeInfo) { - // TODO: [clang-function-type-serialization] Once we start serializing - // the Clang type, we should also assert that the pointer is non-null. - auto Rep = Representation(bits & RepresentationMask); - if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) - assertIsFunctionType(clangTypeInfo.type); - } + : bits(bits), clangTypeInfo(clangTypeInfo) {} public: // Constructor with all defaults. diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp index a75a3ff1d9f5e..5b779920ace0b 100644 --- a/lib/AST/ExtInfo.cpp +++ b/lib/AST/ExtInfo.cpp @@ -19,6 +19,20 @@ #include "clang/AST/Type.h" +static void assertIsFunctionType(const clang::Type *type) { +#ifndef NDEBUG + if (!(type->isFunctionPointerType() || type->isBlockPointerType() || + type->isFunctionReferenceType())) { + llvm::SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Expected a Clang function type wrapped in a pointer type or " + << "a block pointer type but found:\n"; + type->dump(os); + llvm_unreachable(os.str().data()); + } +#endif +} + namespace swift { // MARK: - ClangTypeInfo @@ -53,23 +67,12 @@ void ClangTypeInfo::dump(llvm::raw_ostream &os) const { // MARK: - ASTExtInfoBuilder -void ASTExtInfoBuilder::assertIsFunctionType(const clang::Type *type) { -#ifndef NDEBUG - if (!(type->isFunctionPointerType() || type->isBlockPointerType() || - type->isFunctionReferenceType())) { - SmallString<256> buf; - llvm::raw_svector_ostream os(buf); - os << "Expected a Clang function type wrapped in a pointer type or " - << "a block pointer type but found:\n"; - type->dump(os); - llvm_unreachable(os.str().data()); - } -#endif - return; -} - void ASTExtInfoBuilder::checkInvariants() const { - // TODO: Add validation checks here while making sure things don't blow up. + // TODO: [clang-function-type-serialization] Once we start serializing + // the Clang type, we should also assert that the pointer is non-null. + auto Rep = Representation(bits & RepresentationMask); + if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) + assertIsFunctionType(clangTypeInfo.type); } // MARK: - ASTExtInfo From 8da4d53d2c3a0b3976b14d0e0dc38d1144191fe8 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 16:09:32 -0700 Subject: [PATCH 09/47] [NFC] Use ClangTypeInfo's implicit null state instead of an extra Optional. --- include/swift/AST/ExtInfo.h | 19 +++++-------------- include/swift/AST/Types.h | 2 +- lib/AST/ASTContext.cpp | 12 ++++++------ lib/AST/ASTPrinter.cpp | 6 +++--- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 02c78efccf68f..6b2bc1b8bf7c5 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -246,10 +246,7 @@ class ASTExtInfoBuilder { DifferentiabilityKind::NonDifferentiable; } - /// Get the underlying ClangTypeInfo value if it is not the default value. - Optional getClangTypeInfo() const { - return clangTypeInfo.empty() ? Optional() : clangTypeInfo; - } + ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; } constexpr SILFunctionTypeRepresentation getSILRepresentation() const { unsigned rawRep = bits & RepresentationMask; @@ -396,9 +393,7 @@ class ASTExtInfo { constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } - Optional getClangTypeInfo() const { - return builder.getClangTypeInfo(); - } + ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); } constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } @@ -558,10 +553,8 @@ class SILExtInfoBuilder { DifferentiabilityKind::NonDifferentiable; } - /// Get the underlying ClangTypeInfo value if it is not the default value. - Optional getClangTypeInfo() const { - return clangTypeInfo.empty() ? Optional() : clangTypeInfo; - } + /// Get the underlying ClangTypeInfo value. + ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; } constexpr bool hasSelfParam() const { switch (getRepresentation()) { @@ -689,9 +682,7 @@ class SILExtInfo { constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } - Optional getClangTypeInfo() const { - return builder.getClangTypeInfo(); - } + ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); } constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 1dd1aed9bd772..863cce2503988 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2872,7 +2872,7 @@ class AnyFunctionType : public TypeBase { unsigned NumParams, ExtInfo Info) : TypeBase(Kind, CanTypeContext, properties), Output(Output) { Bits.AnyFunctionType.ExtInfoBits = Info.getBits(); - Bits.AnyFunctionType.HasClangTypeInfo = Info.getClangTypeInfo().hasValue(); + Bits.AnyFunctionType.HasClangTypeInfo = !Info.getClangTypeInfo().empty(); Bits.AnyFunctionType.NumParams = NumParams; assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); // The use of both assert() and static_assert() is intentional. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b8a0f69ccd747..ce4f9c980bf6f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3131,16 +3131,16 @@ FunctionType *FunctionType::get(ArrayRef params, return funcTy; } - Optional clangTypeInfo = info.getClangTypeInfo(); + auto clangTypeInfo = info.getClangTypeInfo(); size_t allocSize = totalSizeToAlloc( - params.size(), clangTypeInfo.hasValue() ? 1 : 0); + params.size(), clangTypeInfo.empty() ? 0 : 1); void *mem = ctx.Allocate(allocSize, alignof(FunctionType), arena); bool isCanonical = isFunctionTypeCanonical(params, result); - if (clangTypeInfo.hasValue()) { + if (!clangTypeInfo.empty()) { if (ctx.LangOpts.UseClangFunctionTypes) - isCanonical &= clangTypeInfo->type->isCanonicalUnqualified(); + isCanonical &= clangTypeInfo.getType()->isCanonicalUnqualified(); else isCanonical = false; } @@ -3162,8 +3162,8 @@ FunctionType::FunctionType(ArrayRef params, std::uninitialized_copy(params.begin(), params.end(), getTrailingObjects()); auto clangTypeInfo = info.getClangTypeInfo(); - if (clangTypeInfo.hasValue()) - *getTrailingObjects() = clangTypeInfo.getValue(); + if (!clangTypeInfo.empty()) + *getTrailingObjects() = clangTypeInfo; } void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 58a220d54c776..a2eea55434aa4 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3629,7 +3629,7 @@ void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { auto *cml = Ctx.getClangModuleLoader(); SmallString<64> buf; llvm::raw_svector_ostream os(buf); - info.getClangTypeInfo().getValue().printType(cml, os); + info.getClangTypeInfo().printType(cml, os); Printer << ", cType: " << QuotedString(os.str()); } @@ -4054,7 +4054,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; // [TODO: Clang-type-plumbing] Remove the second check. - if (printNameOnly || !info.getClangTypeInfo().hasValue()) + if (printNameOnly || info.getClangTypeInfo().empty()) break; printCType(Ctx, Printer, info); break; @@ -4120,7 +4120,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; // [TODO: Clang-type-plumbing] Remove the second check. - if (printNameOnly || !info.getClangTypeInfo().hasValue()) + if (printNameOnly || info.getClangTypeInfo().empty()) break; printCType(Ctx, Printer, info); break; From eead4ae8f05a91e2a087d027e341712d500f263b Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 16:18:38 -0700 Subject: [PATCH 10/47] [NFC] Remove unused function parameter. --- include/swift/AST/ASTContext.h | 1 - lib/AST/ASTContext.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index e3ecfbb158dfb..7697baf4d1d01 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -606,7 +606,6 @@ class ASTContext final { const clang::Type * getCanonicalClangFunctionType( ArrayRef params, Optional result, - const SILFunctionType::ExtInfo incompleteExtInfo, SILFunctionType::Representation trueRep); /// Get the Swift declaration that a Clang declaration was exported from, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ce4f9c980bf6f..ca9d3ed0f34e8 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4477,7 +4477,6 @@ const clang::Type * ASTContext::getCanonicalClangFunctionType( ArrayRef params, Optional result, - SILFunctionType::ExtInfo incompleteExtInfo, SILFunctionType::Representation trueRep) { auto *ty = getClangTypeConverter().getFunctionType(params, result, trueRep); return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; From eeec16f14322e96577e21a9e0d2b554fb98dd313 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 19 Aug 2020 18:06:00 -0700 Subject: [PATCH 11/47] [NFC] Remove redundant ExtInfo parameter for getBridgedFunctionType. At all call-sites, the extInfo passed as the third argument is computed directly from the second argument, so we compute it directly in getBridgedFunctionType. --- include/swift/SIL/TypeLowering.h | 1 - lib/SIL/IR/SILFunctionType.cpp | 13 ++++--------- lib/SIL/IR/TypeLowering.cpp | 2 +- lib/SILGen/SILGenApply.cpp | 1 - lib/SILGen/SILGenBridging.cpp | 3 +-- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index aba919ee0090c..344087f0a1aaf 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -992,7 +992,6 @@ class TypeConverter { /// Given a function type, yield its bridged formal type. CanAnyFunctionType getBridgedFunctionType(AbstractionPattern fnPattern, CanAnyFunctionType fnType, - AnyFunctionType::ExtInfo extInfo, Bridgeability bridging); /// Given a referenced value and the substituted formal type of a diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 2d3e10054313d..794706f5c15b2 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -3984,7 +3984,6 @@ SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, CanAnyFunctionType TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, CanAnyFunctionType t, - AnyFunctionType::ExtInfo extInfo, Bridgeability bridging) { // Pull out the generic signature. CanGenericSignature genericSig = t.getOptGenericSignature(); @@ -3996,12 +3995,8 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, case SILFunctionTypeRepresentation::Closure: case SILFunctionTypeRepresentation::WitnessMethod: { // No bridging needed for native functions. - if (t->getExtInfo().isEqualTo(extInfo, useClangTypes(t))) - return t; - return CanAnyFunctionType::get(genericSig, t.getParams(), t.getResult(), - extInfo); + return t; } - case SILFunctionTypeRepresentation::CFunctionPointer: case SILFunctionTypeRepresentation::Block: case SILFunctionTypeRepresentation::ObjCMethod: { @@ -4016,7 +4011,7 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, suppressOptional); return CanAnyFunctionType::get(genericSig, llvm::makeArrayRef(params), - result, extInfo); + result, t->getExtInfo()); } } llvm_unreachable("bad calling convention"); @@ -4098,7 +4093,6 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, auto bridging = Bridgeability::Full; unsigned numParameterLists = constant.getParameterListCount(); - auto extInfo = fnType->getExtInfo(); // Form an abstraction pattern for bridging purposes. AbstractionPattern bridgingFnPattern = @@ -4108,12 +4102,13 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, // Fast path: no uncurrying required. if (numParameterLists == 1) { auto bridgedFnType = - getBridgedFunctionType(bridgingFnPattern, fnType, extInfo, bridging); + getBridgedFunctionType(bridgingFnPattern, fnType, bridging); bridgingFnPattern.rewriteType(bridgingFnPattern.getGenericSignature(), bridgedFnType); return { bridgingFnPattern, bridgedFnType }; } + auto extInfo = fnType->getExtInfo(); SILFunctionTypeRepresentation rep = extInfo.getSILRepresentation(); assert(rep != SILFunctionType::Representation::Block && "objc blocks cannot be curried"); diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index e1d4417e42dd6..45a725074cb8d 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -1964,7 +1964,7 @@ TypeConverter::computeLoweredRValueType(TypeExpansionContext forExpansion, // Bridge the parameters and result of the function type. auto bridgedFnType = - TC.getBridgedFunctionType(origType, substFnType, extInfo, bridging); + TC.getBridgedFunctionType(origType, substFnType, bridging); substFnType = bridgedFnType; // Also rewrite the type of the abstraction pattern. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index bffecc2c2c187..22d3136ca776c 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -76,7 +76,6 @@ getIndirectApplyAbstractionPattern(SILGenFunction &SGF, // bridged to a foreign type. auto bridgedType = SGF.SGM.Types.getBridgedFunctionType(pattern, fnType, - fnType->getExtInfo(), Bridgeability::Full); pattern.rewriteType(CanGenericSignature(), bridgedType); return pattern; diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 7eedc3b1975fa..0c1bb7d417a89 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -350,8 +350,7 @@ getParameterTypes(AnyFunctionType::CanParamArrayRef params) { static CanAnyFunctionType getBridgedBlockType(SILGenModule &SGM, CanAnyFunctionType blockType) { return SGM.Types.getBridgedFunctionType(AbstractionPattern(blockType), - blockType, blockType->getExtInfo(), - Bridgeability::Full); + blockType, Bridgeability::Full); } static void buildFuncToBlockInvokeBody(SILGenFunction &SGF, From fdbcd1236fd6d94239eb37b07cb729c4eae466aa Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 20 Aug 2020 17:11:48 -0700 Subject: [PATCH 12/47] [NFC] Bridge based on SILFunctionLanguage instead of Representation. --- lib/SIL/IR/SILFunctionType.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 794706f5c15b2..29a40a911d107 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -3988,18 +3988,13 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, // Pull out the generic signature. CanGenericSignature genericSig = t.getOptGenericSignature(); - switch (auto rep = t->getExtInfo().getSILRepresentation()) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::WitnessMethod: { + auto rep = t->getExtInfo().getSILRepresentation(); + switch (getSILFunctionLanguage(rep)) { + case SILFunctionLanguage::Swift: { // No bridging needed for native functions. return t; } - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Block: - case SILFunctionTypeRepresentation::ObjCMethod: { + case SILFunctionLanguage::C: { SmallVector params; getBridgedParams(rep, pattern, t->getParams(), params, bridging); From f86aad9b294819a84aa1493330e07b0675ac90e6 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 20 Aug 2020 18:00:27 -0700 Subject: [PATCH 13/47] [NFC] Extract computation of SILExtInfoBuilder bits into new method. --- include/swift/AST/ExtInfo.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 6b2bc1b8bf7c5..0850870f6ce45 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -505,6 +505,15 @@ class SILExtInfoBuilder { SILExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) : bits(bits), clangTypeInfo(clangTypeInfo) {} + static constexpr unsigned makeBits(Representation rep, bool isPseudogeneric, + bool isNoEscape, bool isAsync, + DifferentiabilityKind diffKind) { + return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | + (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | + (((unsigned)diffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask); + } + public: // Constructor with all defaults. SILExtInfoBuilder() : bits(0), clangTypeInfo(ClangTypeInfo(nullptr)) {} @@ -513,12 +522,9 @@ class SILExtInfoBuilder { SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, bool isAsync, DifferentiabilityKind diffKind, const clang::Type *type) - : SILExtInfoBuilder( - ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | - (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | - (((unsigned)diffKind << DifferentiabilityMaskOffset) & - DifferentiabilityMask), - ClangTypeInfo(type)) {} + : SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape, isAsync, + diffKind), + ClangTypeInfo(type)) {} void checkInvariants() const; From c4ad840ad3dbc40c885af117105a3a92326df149 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 20 Aug 2020 17:40:41 -0700 Subject: [PATCH 14/47] [NFC] Reuse SILExtInfoBuilder's main constructor in default constructor. --- include/swift/AST/ExtInfo.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 0850870f6ce45..55ea27bc47dc8 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -516,7 +516,7 @@ class SILExtInfoBuilder { public: // Constructor with all defaults. - SILExtInfoBuilder() : bits(0), clangTypeInfo(ClangTypeInfo(nullptr)) {} + SILExtInfoBuilder() : SILExtInfoBuilder(0, ClangTypeInfo(nullptr)) {} // Constructor for polymorphic type. SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, @@ -526,6 +526,12 @@ class SILExtInfoBuilder { diffKind), ClangTypeInfo(type)) {} + SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric) + : SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric, + info.isNoEscape(), info.isAsync(), + info.getDifferentiabilityKind()), + info.getClangTypeInfo()) {} + void checkInvariants() const; /// Check if \c this is well-formed and create an ExtInfo. From 4c7ccf5abe79563907bbfc0cd80899f0a3287476 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 25 Aug 2020 15:12:50 -0700 Subject: [PATCH 15/47] [NFC] Clean Up FrontendTool Try to impose a simple structure that splits performing actions from the pre and post-pipeline conditions. Wherever actions would take more than a simple return, split them into functions. Refine functions that perform effects to return status codes when they fail. Finally, delineate functions that need semantic analysis from those that do not. Overall this should be NFC. --- include/swift/Frontend/Frontend.h | 2 +- lib/Frontend/Frontend.cpp | 3 +- lib/FrontendTool/FrontendTool.cpp | 356 ++++++++++++++------------ lib/FrontendTool/ImportedModules.cpp | 4 +- lib/FrontendTool/ImportedModules.h | 3 +- lib/FrontendTool/ScanDependencies.cpp | 5 +- lib/FrontendTool/ScanDependencies.h | 3 +- 7 files changed, 204 insertions(+), 172 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index f5f75f13e7694..39ab041dbcc1b 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -621,7 +621,7 @@ class CompilerInstance { /// /// This is similar to a parse-only invocation, but module imports will also /// be processed. - void performParseAndResolveImportsOnly(); + bool performParseAndResolveImportsOnly(); /// Performs mandatory, diagnostic, and optimization passes over the SIL. /// \param silModule The SIL module that was generated during SILGen. diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 0d6263b2e544c..688acbf0b29dd 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -848,7 +848,7 @@ void CompilerInstance::setMainModule(ModuleDecl *newMod) { Context->addLoadedModule(newMod); } -void CompilerInstance::performParseAndResolveImportsOnly() { +bool CompilerInstance::performParseAndResolveImportsOnly() { FrontendStatsTracer tracer(getStatsReporter(), "parse-and-resolve-imports"); // Resolve imports for all the source files. @@ -867,6 +867,7 @@ void CompilerInstance::performParseAndResolveImportsOnly() { mainModule->setHasResolvedImports(); bindExtensions(*mainModule); + return Context->hadError(); } void CompilerInstance::performSema() { diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 4221b53394e5f..743205b9ccb44 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1229,7 +1229,7 @@ static void verifyGenericSignaturesIfNeeded(const CompilerInvocation &Invocation GenericSignatureBuilder::verifyGenericSignaturesInModule(module); } -static void dumpAndPrintScopeMap(const CompilerInstance &Instance, +static bool dumpAndPrintScopeMap(const CompilerInstance &Instance, SourceFile *SF) { // Not const because may require reexpansion ASTScope &scope = SF->getScope(); @@ -1239,13 +1239,14 @@ static void dumpAndPrintScopeMap(const CompilerInstance &Instance, llvm::errs() << "***Complete scope map***\n"; scope.buildFullyExpandedTree(); scope.print(llvm::errs()); - return; + return Instance.getASTContext().hadError(); } // Probe each of the locations, and dump what we find. for (auto lineColumn : opts.DumpScopeMapLocations) { scope.buildFullyExpandedTree(); scope.dumpOneScopeMapLocation(lineColumn); } + return Instance.getASTContext().hadError(); } static SourceFile * @@ -1260,7 +1261,7 @@ getPrimaryOrMainSourceFile(const CompilerInstance &Instance) { /// Dumps the AST of all available primary source files. If corresponding output /// files were specified, use them; otherwise, dump the AST to stdout. -static void dumpAST(CompilerInstance &Instance) { +static bool dumpAST(CompilerInstance &Instance) { auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { @@ -1275,55 +1276,7 @@ static void dumpAST(CompilerInstance &Instance) { auto *SF = getPrimaryOrMainSourceFile(Instance); SF->dump(llvm::outs(), /*parseIfNeeded*/ true); } -} - -/// We may have been told to dump the AST (either after parsing or -/// type-checking, which is already differentiated in -/// CompilerInstance::performSema()), so dump or print the main source file and -/// return. - -static Optional dumpASTIfNeeded(CompilerInstance &Instance) { - const auto &opts = Instance.getInvocation().getFrontendOptions(); - const FrontendOptions::ActionType Action = opts.RequestedAction; - ASTContext &Context = Instance.getASTContext(); - switch (Action) { - default: - return None; - - case FrontendOptions::ActionType::PrintAST: - getPrimaryOrMainSourceFile(Instance) - ->print(llvm::outs(), PrintOptions::printEverything()); - break; - - case FrontendOptions::ActionType::DumpScopeMaps: - dumpAndPrintScopeMap(Instance, getPrimaryOrMainSourceFile(Instance)); - break; - - case FrontendOptions::ActionType::DumpTypeRefinementContexts: - getPrimaryOrMainSourceFile(Instance) - ->getTypeRefinementContext() - ->dump(llvm::errs(), Context.SourceMgr); - break; - - case FrontendOptions::ActionType::DumpInterfaceHash: - getPrimaryOrMainSourceFile(Instance)->dumpInterfaceHash(llvm::errs()); - break; - - case FrontendOptions::ActionType::EmitSyntax: - emitSyntax(getPrimaryOrMainSourceFile(Instance), - opts.InputsAndOutputs.getSingleOutputFilename()); - break; - - case FrontendOptions::ActionType::DumpParse: - case FrontendOptions::ActionType::DumpAST: - dumpAST(Instance); - break; - - case FrontendOptions::ActionType::EmitImportedModules: - emitImportedModules(Context, Instance.getMainModule(), opts); - break; - } - return Context.hadError(); + return Instance.getASTContext().hadError(); } static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( @@ -1747,40 +1700,187 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { emitCompiledSourceForAllPrimaryInputsIfNeeded(Instance); } -/// Performs the compile requested by the user. -/// \param Instance Will be reset after performIRGeneration when the verifier -/// mode is NoVerify and there were no errors. -/// \returns true on error -static bool performCompile(CompilerInvocation &Invok, - CompilerInstance &Instance, - ArrayRef Args, - int &ReturnValue, - FrontendObserver *observer) { +static bool printSwiftVersion(const CompilerInvocation &Invocation) { + llvm::outs() << version::getSwiftFullVersion( + version::Version::getCurrentLanguageVersion()) + << '\n'; + llvm::outs() << "Target: " << Invocation.getLangOptions().Target.str() + << '\n'; + return false; +} + +static bool +withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer, + llvm::function_ref cont) { const auto &Invocation = Instance.getInvocation(); const auto &opts = Invocation.getFrontendOptions(); - const FrontendOptions::ActionType Action = opts.RequestedAction; + assert(!FrontendOptions::shouldActionOnlyParse(opts.RequestedAction) && + "Action may only parse, but has requested semantic analysis!"); + + Instance.performSema(); + if (observer) + observer->performedSemanticAnalysis(Instance); + + switch (opts.CrashMode) { + case FrontendOptions::DebugCrashMode::AssertAfterParse: + debugFailWithAssertion(); + return true; + case FrontendOptions::DebugCrashMode::CrashAfterParse: + debugFailWithCrash(); + return true; + case FrontendOptions::DebugCrashMode::None: + break; + } + + (void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance); + + if (Instance.getASTContext().hadError()) + return true; + + return cont(Instance); +} + +static bool performScanDependencies(CompilerInstance &Instance) { + auto batchScanInput = + Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; + if (batchScanInput.empty()) { + return scanDependencies(Instance); + } else { + return batchScanModuleDependencies(Instance, batchScanInput); + } +} + +static bool performParseOnly(ModuleDecl &MainModule) { + // A -parse invocation only cares about the side effects of parsing, so + // force the parsing of all the source files. + for (auto *file : MainModule.getFiles()) { + if (auto *SF = dyn_cast(file)) + (void)SF->getTopLevelDecls(); + } + return MainModule.getASTContext().hadError(); +} + +static bool performAction(CompilerInstance &Instance, + int &ReturnValue, + FrontendObserver *observer) { + const auto &opts = Instance.getInvocation().getFrontendOptions(); + auto &Context = Instance.getASTContext(); + switch (Instance.getInvocation().getFrontendOptions().RequestedAction) { + // MARK: Trivial Actions + case FrontendOptions::ActionType::NoneAction: + return Context.hadError(); + case FrontendOptions::ActionType::PrintVersion: + return printSwiftVersion(Instance.getInvocation()); + case FrontendOptions::ActionType::REPL: + llvm::report_fatal_error("Compiler-internal integrated REPL has been " + "removed; use the LLDB-enhanced REPL instead."); + // MARK: Actions for Clang and Clang Modules // We've been asked to precompile a bridging header or module; we want to // avoid touching any other inputs and just parse, emit and exit. - if (Action == FrontendOptions::ActionType::EmitPCH) + case FrontendOptions::ActionType::EmitPCH: return precompileBridgingHeader(Instance); - if (Action == FrontendOptions::ActionType::EmitPCM) + case FrontendOptions::ActionType::EmitPCM: return precompileClangModule(Instance); - if (Action == FrontendOptions::ActionType::DumpPCM) + case FrontendOptions::ActionType::DumpPCM: return dumpPrecompiledClangModule(Instance); - if (Action == FrontendOptions::ActionType::PrintVersion) { - llvm::outs() << version::getSwiftFullVersion( - version::Version::getCurrentLanguageVersion()) << '\n'; - llvm::outs() << "Target: " - << Invocation.getLangOptions().Target.str() << '\n'; - return false; - } - if (Action == FrontendOptions::ActionType::CompileModuleFromInterface || - Action == FrontendOptions::ActionType::TypecheckModuleFromInterface) + + // MARK: Module Interface Actions + case FrontendOptions::ActionType::CompileModuleFromInterface: + case FrontendOptions::ActionType::TypecheckModuleFromInterface: return buildModuleFromInterface(Instance); - if (Invocation.getInputKind() == InputFileKind::LLVM) - return compileLLVMIR(Instance); + // MARK: Actions that Dump + case FrontendOptions::ActionType::DumpParse: { + return dumpAST(Instance); + } + case FrontendOptions::ActionType::DumpAST: + return withSemanticAnalysis( + Instance, observer, + [](CompilerInstance &Instance) { return dumpAST(Instance); }); + case FrontendOptions::ActionType::PrintAST: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance)->print( + llvm::outs(), PrintOptions::printEverything()); + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::DumpScopeMaps: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + return dumpAndPrintScopeMap(Instance, + getPrimaryOrMainSourceFile(Instance)); + }); + case FrontendOptions::ActionType::DumpTypeRefinementContexts: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance) + ->getTypeRefinementContext() + ->dump(llvm::errs(), Instance.getASTContext().SourceMgr); + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::DumpInterfaceHash: + getPrimaryOrMainSourceFile(Instance)->dumpInterfaceHash(llvm::errs()); + return Context.hadError(); + case FrontendOptions::ActionType::EmitSyntax: + return emitSyntax(getPrimaryOrMainSourceFile(Instance), + opts.InputsAndOutputs.getSingleOutputFilename()); + case FrontendOptions::ActionType::EmitImportedModules: + return emitImportedModules(Instance.getMainModule(), opts); + + // MARK: Dependency Scanning Actions + case FrontendOptions::ActionType::ScanDependencies: + return performScanDependencies(Instance); + case FrontendOptions::ActionType::ScanClangDependencies: + return scanClangDependencies(Instance); + + // MARK: General Compilation Actions + case FrontendOptions::ActionType::Parse: + return performParseOnly(*Instance.getMainModule()); + case FrontendOptions::ActionType::ResolveImports: + return Instance.performParseAndResolveImportsOnly(); + case FrontendOptions::ActionType::Typecheck: + return withSemanticAnalysis(Instance, observer, + [](CompilerInstance &Instance) { + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::EmitSILGen: + case FrontendOptions::ActionType::EmitSIBGen: + case FrontendOptions::ActionType::EmitSIL: + case FrontendOptions::ActionType::EmitSIB: + case FrontendOptions::ActionType::EmitModuleOnly: + case FrontendOptions::ActionType::MergeModules: + case FrontendOptions::ActionType::Immediate: + case FrontendOptions::ActionType::EmitAssembly: + case FrontendOptions::ActionType::EmitIR: + case FrontendOptions::ActionType::EmitBC: + case FrontendOptions::ActionType::EmitObject: + case FrontendOptions::ActionType::DumpTypeInfo: + return withSemanticAnalysis( + Instance, observer, [&](CompilerInstance &Instance) { + assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) && + "All actions not requiring SILGen must have been handled!"); + if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) + return compileLLVMIR(Instance); + + return performCompileStepsPostSema(Instance, ReturnValue, observer); + }); + } + + assert(false && "Unhandled case in performCompile!"); + return Context.hadError(); +} + +/// Performs the compile requested by the user. +/// \param Instance Will be reset after performIRGeneration when the verifier +/// mode is NoVerify and there were no errors. +/// \returns true on error +static bool performCompile(CompilerInstance &Instance, + int &ReturnValue, + FrontendObserver *observer) { + const auto &Invocation = Instance.getInvocation(); + const auto &opts = Invocation.getFrontendOptions(); + const FrontendOptions::ActionType Action = opts.RequestedAction; // If we aren't in a parse-only context and expect an implicit stdlib import, // load in the standard library. If we either fail to find it or encounter an @@ -1793,95 +1893,27 @@ static bool performCompile(CompilerInvocation &Invok, return true; } - bool didFinishPipeline = false; - SWIFT_DEFER { - assert(didFinishPipeline && "Returned without calling finishPipeline"); - }; - - auto finishPipeline = [&](bool hadError) -> bool { - // We might have freed the ASTContext already, but in that case we would - // have already performed these actions. - if (Instance.hasASTContext()) { - performEndOfPipelineActions(Instance); - hadError |= Instance.getASTContext().hadError(); - } - didFinishPipeline = true; - return hadError; - }; - - auto &Context = Instance.getASTContext(); - if (FrontendOptions::shouldActionOnlyParse(Action)) { - // Parsing gets triggered lazily, but let's make sure we have the right - // input kind. - auto kind = Invocation.getInputKind(); - assert((kind == InputFileKind::Swift || - kind == InputFileKind::SwiftLibrary || - kind == InputFileKind::SwiftModuleInterface) && - "Only supports parsing .swift files"); - (void)kind; - } else if (Action == FrontendOptions::ActionType::ResolveImports) { - Instance.performParseAndResolveImportsOnly(); - return finishPipeline(Context.hadError()); - } else { - Instance.performSema(); - } - - if (Action == FrontendOptions::ActionType::Parse) { - // A -parse invocation only cares about the side effects of parsing, so - // force the parsing of all the source files. - for (auto *file : Instance.getMainModule()->getFiles()) { - if (auto *SF = dyn_cast(file)) - (void)SF->getTopLevelDecls(); + assert([&]() -> bool { + if (FrontendOptions::shouldActionOnlyParse(Action)) { + // Parsing gets triggered lazily, but let's make sure we have the right + // input kind. + auto kind = Invocation.getInputKind(); + return kind == InputFileKind::Swift || + kind == InputFileKind::SwiftLibrary || + kind == InputFileKind::SwiftModuleInterface; } - return finishPipeline(Context.hadError()); - } - - if (Action == FrontendOptions::ActionType::ScanDependencies) { - auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; - if (batchScanInput.empty()) - return finishPipeline(scanDependencies(Instance)); - else - return finishPipeline(batchScanModuleDependencies(Invok, - Instance, - batchScanInput)); - } - - if (Action == FrontendOptions::ActionType::ScanClangDependencies) - return finishPipeline(scanClangDependencies(Instance)); - - if (observer) - observer->performedSemanticAnalysis(Instance); - - { - FrontendOptions::DebugCrashMode CrashMode = opts.CrashMode; - if (CrashMode == FrontendOptions::DebugCrashMode::AssertAfterParse) - debugFailWithAssertion(); - else if (CrashMode == FrontendOptions::DebugCrashMode::CrashAfterParse) - debugFailWithCrash(); - } + return true; + }() && "Only supports parsing .swift files"); - (void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance); + bool hadError = performAction(Instance, ReturnValue, observer); - if (Action == FrontendOptions::ActionType::REPL) { - llvm::report_fatal_error("Compiler-internal integrated REPL has been " - "removed; use the LLDB-enhanced REPL instead."); + // We might have freed the ASTContext already, but in that case we would + // have already performed these actions. + if (Instance.hasASTContext()) { + performEndOfPipelineActions(Instance); + hadError |= Instance.getASTContext().hadError(); } - - if (auto r = dumpASTIfNeeded(Instance)) - return finishPipeline(*r); - - if (Context.hadError()) - return finishPipeline(/*hadError*/ true); - - // We've just been told to perform a typecheck, so we can return now. - if (Action == FrontendOptions::ActionType::Typecheck) - return finishPipeline(/*hadError*/ false); - - assert(FrontendOptions::doesActionGenerateSIL(Action) && - "All actions not requiring SILGen must have been handled!"); - - return finishPipeline( - performCompileStepsPostSema(Instance, ReturnValue, observer)); + return hadError; } static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs, @@ -2655,7 +2687,7 @@ int swift::performFrontend(ArrayRef Args, } int ReturnValue = 0; - bool HadError = performCompile(Invocation, *Instance, Args, ReturnValue, observer); + bool HadError = performCompile(*Instance, ReturnValue, observer); if (verifierEnabled) { DiagnosticEngine &diags = Instance->getDiags(); diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 90a33895ee52a..d750417737264 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -42,9 +42,9 @@ static void findAllClangImports(const clang::Module *module, } } -bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, +bool swift::emitImportedModules(ModuleDecl *mainModule, const FrontendOptions &opts) { - + auto &Context = mainModule->getASTContext(); std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); std::error_code EC; llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); diff --git a/lib/FrontendTool/ImportedModules.h b/lib/FrontendTool/ImportedModules.h index 42b4e76babaf4..510fa4ccdedde 100644 --- a/lib/FrontendTool/ImportedModules.h +++ b/lib/FrontendTool/ImportedModules.h @@ -20,8 +20,7 @@ class FrontendOptions; class ModuleDecl; /// Emit the names of the modules imported by \c mainModule. -bool emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, - const FrontendOptions &opts); +bool emitImportedModules(ModuleDecl *mainModule, const FrontendOptions &opts); } // end namespace swift #endif diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index fb0cd4b47a0bc..34e99395e1b17 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -690,9 +690,10 @@ bool swift::scanClangDependencies(CompilerInstance &instance) { .InputsAndOutputs.getSingleOutputFilename()); } -bool swift::batchScanModuleDependencies(CompilerInvocation &invok, - CompilerInstance &instance, +bool swift::batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile) { + const CompilerInvocation &invok = instance.getInvocation(); + (void)instance.getMainModule(); llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); diff --git a/lib/FrontendTool/ScanDependencies.h b/lib/FrontendTool/ScanDependencies.h index 4604496ba66dc..fab7d26bbb190 100644 --- a/lib/FrontendTool/ScanDependencies.h +++ b/lib/FrontendTool/ScanDependencies.h @@ -21,8 +21,7 @@ class CompilerInvocation; class CompilerInstance; /// Batch scan the dependencies for modules specified in \c batchInputFile. -bool batchScanModuleDependencies(CompilerInvocation &invok, - CompilerInstance &instance, +bool batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile); /// Scans the dependencies of the main module of \c instance. From fe7444ffa3e3ac61da5194102c57e6c451156c88 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 27 Aug 2020 12:10:13 -0700 Subject: [PATCH 16/47] Add doesActionRequireSwiftStandardLibrary --- include/swift/Frontend/FrontendOptions.h | 6 ++- lib/Frontend/FrontendOptions.cpp | 59 ++++++++++++++++++++---- lib/FrontendTool/FrontendTool.cpp | 2 +- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 724cf7e6efa31..0d3a0e68de7f1 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -294,9 +294,13 @@ class FrontendOptions { /// '.../lib/swift', otherwise '.../lib/swift_static'. bool UseSharedResourceFolder = true; - /// \return true if action only parses without doing other compilation steps. + /// \return true if the given action only parses without doing other compilation steps. static bool shouldActionOnlyParse(ActionType); + /// \return true if the given action requires the standard library to be + /// loaded before it is run. + static bool doesActionRequireSwiftStandardLibrary(ActionType); + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 83718a733666e..013a83157bf9d 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -73,20 +73,63 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { bool FrontendOptions::shouldActionOnlyParse(ActionType action) { switch (action) { - case FrontendOptions::ActionType::Parse: - case FrontendOptions::ActionType::DumpParse: - case FrontendOptions::ActionType::EmitSyntax: - case FrontendOptions::ActionType::DumpInterfaceHash: - case FrontendOptions::ActionType::EmitImportedModules: - case FrontendOptions::ActionType::ScanDependencies: - case FrontendOptions::ActionType::ScanClangDependencies: - case FrontendOptions::ActionType::PrintVersion: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::PrintVersion: return true; default: return false; } } +bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { + switch (action) { + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::PrintVersion: + case ActionType::EmitPCH: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::CompileModuleFromInterface: + case ActionType::TypecheckModuleFromInterface: + return false; + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpAST: + case ActionType::PrintAST: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::EmitSILGen: + case ActionType::EmitSIL: + case ActionType::EmitModuleOnly: + case ActionType::MergeModules: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitAssembly: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitObject: + case ActionType::DumpTypeInfo: + assert(!FrontendOptions::shouldActionOnlyParse(action) && + "Parse-only actions should not load modules!"); + return true; + } + llvm_unreachable("Unknown ActionType"); +} + void FrontendOptions::forAllOutputPaths( const InputFile &input, llvm::function_ref fn) const { if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly && diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 743205b9ccb44..0d28ccf3d2ace 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1888,7 +1888,7 @@ static bool performCompile(CompilerInstance &Instance, // trigger a bunch of other errors due to the stdlib being missing, or at // worst crash downstream as many call sites don't currently handle a missing // stdlib. - if (!FrontendOptions::shouldActionOnlyParse(Action)) { + if (FrontendOptions::doesActionRequireSwiftStandardLibrary(Action)) { if (Instance.loadStdlibIfNeeded()) return true; } From 4c56c95674a12342d4321ab78003a9fe983581c7 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 27 Aug 2020 16:13:47 -0700 Subject: [PATCH 17/47] Define doesActionRequireInputs --- include/swift/Frontend/FrontendOptions.h | 3 ++ .../ArgsToFrontendOptionsConverter.cpp | 3 +- lib/Frontend/FrontendOptions.cpp | 41 +++++++++++++++++++ lib/FrontendTool/FrontendTool.cpp | 34 ++++++++++----- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 0d3a0e68de7f1..c961794d35582 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -301,6 +301,9 @@ class FrontendOptions { /// loaded before it is run. static bool doesActionRequireSwiftStandardLibrary(ActionType); + /// \return true if the given action requires input files to be provided. + static bool doesActionRequireInputs(ActionType action); + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 4e12495bb35df..c876a44f87383 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -406,8 +406,7 @@ bool ArgsToFrontendOptionsConverter::setUpInputKindAndImmediateArgs() { if (Opts.InputsAndOutputs.verifyInputs( Diags, treatAsSIL, Opts.RequestedAction == FrontendOptions::ActionType::REPL, - (Opts.RequestedAction == FrontendOptions::ActionType::NoneAction || - Opts.RequestedAction == FrontendOptions::ActionType::PrintVersion))){ + !FrontendOptions::doesActionRequireInputs(Opts.RequestedAction))) { return true; } if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate) { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 013a83157bf9d..16fe1877a6d25 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -130,6 +130,47 @@ bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { llvm_unreachable("Unknown ActionType"); } +bool FrontendOptions::doesActionRequireInputs(ActionType action) { + switch (action) { + case ActionType::NoneAction: + case ActionType::PrintVersion: + return false; + case ActionType::REPL: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::EmitPCH: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::CompileModuleFromInterface: + case ActionType::TypecheckModuleFromInterface: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpAST: + case ActionType::PrintAST: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::EmitSILGen: + case ActionType::EmitSIL: + case ActionType::EmitModuleOnly: + case ActionType::MergeModules: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::Immediate: + case ActionType::EmitAssembly: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitObject: + case ActionType::DumpTypeInfo: + return true; + } + llvm_unreachable("Unknown ActionType"); +} + void FrontendOptions::forAllOutputPaths( const InputFile &input, llvm::function_ref fn) const { if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly && diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 0d28ccf3d2ace..f4b42016d7d46 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1653,6 +1653,7 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { // it's -emit-imported-modules, which can load modules. auto action = opts.RequestedAction; if (FrontendOptions::shouldActionOnlyParse(action) && + !ctx.getLoadedModules().empty() && action != FrontendOptions::ActionType::EmitImportedModules) { assert(ctx.getNumLoadedModules() == 1 && "Loaded a module during parse-only"); @@ -1689,9 +1690,15 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { } } - // Emit dependencies and index data. + // FIXME: This predicate matches the status quo, but there's no reason + // indexing cannot run for actions that do not require stdlib e.g. to better + // facilitate tests. + if (FrontendOptions::doesActionRequireSwiftStandardLibrary(action)) { + emitIndexData(Instance); + } + + // Emit dependencies. emitReferenceDependenciesForAllPrimaryInputsIfNeeded(Instance); - emitIndexData(Instance); emitMakeDependenciesIfNeeded(Instance.getDiags(), Instance.getDependencyTracker(), opts); @@ -1791,13 +1798,16 @@ static bool performAction(CompilerInstance &Instance, return buildModuleFromInterface(Instance); // MARK: Actions that Dump - case FrontendOptions::ActionType::DumpParse: { + case FrontendOptions::ActionType::DumpParse: + return dumpAST(Instance); + case FrontendOptions::ActionType::DumpAST: { + // FIXME: -dump-ast expects to be able to write output even if type checking + // fails which does not cleanly fit the model \c withSemanticAnalysis is + // trying to impose. Once there is a request for the "semantic AST", this + // point is moot. + Instance.performSema(); return dumpAST(Instance); } - case FrontendOptions::ActionType::DumpAST: - return withSemanticAnalysis( - Instance, observer, - [](CompilerInstance &Instance) { return dumpAST(Instance); }); case FrontendOptions::ActionType::PrintAST: return withSemanticAnalysis( Instance, observer, [](CompilerInstance &Instance) { @@ -1860,9 +1870,6 @@ static bool performAction(CompilerInstance &Instance, Instance, observer, [&](CompilerInstance &Instance) { assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) && "All actions not requiring SILGen must have been handled!"); - if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) - return compileLLVMIR(Instance); - return performCompileStepsPostSema(Instance, ReturnValue, observer); }); } @@ -1882,6 +1889,10 @@ static bool performCompile(CompilerInstance &Instance, const auto &opts = Invocation.getFrontendOptions(); const FrontendOptions::ActionType Action = opts.RequestedAction; + // To compile LLVM IR, just pass it off unmodified. + if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) + return compileLLVMIR(Instance); + // If we aren't in a parse-only context and expect an implicit stdlib import, // load in the standard library. If we either fail to find it or encounter an // error while loading it, bail early. Continuing the compilation will at best @@ -1909,7 +1920,8 @@ static bool performCompile(CompilerInstance &Instance, // We might have freed the ASTContext already, but in that case we would // have already performed these actions. - if (Instance.hasASTContext()) { + if (Instance.hasASTContext() && + FrontendOptions::doesActionRequireInputs(Action)) { performEndOfPipelineActions(Instance); hadError |= Instance.getASTContext().hadError(); } From 16876fbf6617c40df326df7d3fe00a15ff77448b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Aug 2020 13:56:23 -0700 Subject: [PATCH 18/47] [Clang importer] Drop unused parameter from getParamOptionality(). This operation is not actually dependent on the version. --- lib/ClangImporter/ClangAdapter.cpp | 3 +-- lib/ClangImporter/ClangAdapter.h | 6 +----- lib/ClangImporter/ImportName.cpp | 5 +---- lib/ClangImporter/ImportType.cpp | 6 ++---- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index eddac5d46b3bf..a2ff1426bf477 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -714,8 +714,7 @@ bool importer::isUnavailableInSwift( return false; } -OptionalTypeKind importer::getParamOptionality(version::Version swiftVersion, - const clang::ParmVarDecl *param, +OptionalTypeKind importer::getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull) { auto &clangCtx = param->getASTContext(); diff --git a/lib/ClangImporter/ClangAdapter.h b/lib/ClangImporter/ClangAdapter.h index 0b066364bd622..9a15bbd523efa 100644 --- a/lib/ClangImporter/ClangAdapter.h +++ b/lib/ClangImporter/ClangAdapter.h @@ -161,15 +161,11 @@ bool isUnavailableInSwift(const clang::Decl *decl, const PlatformAvailability &, /// Determine the optionality of the given Clang parameter. /// -/// \param swiftLanguageVersion What version of Swift we're using, which affects -/// how optionality is inferred. -/// /// \param param The Clang parameter. /// /// \param knownNonNull Whether a function- or method-level "nonnull" attribute /// applies to this parameter. -OptionalTypeKind getParamOptionality(version::Version swiftLanguageVersion, - const clang::ParmVarDecl *param, +OptionalTypeKind getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull); } } diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 46817ce93de2a..088b62ea3d2a6 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -807,8 +807,6 @@ static bool omitNeedlessWordsInFunctionName( Optional errorParamIndex, bool returnsSelf, bool isInstanceMethod, NameImporter &nameImporter) { clang::ASTContext &clangCtx = nameImporter.getClangContext(); - const version::Version &swiftLanguageVersion = - nameImporter.getLangOpts().EffectiveLanguageVersion; // Collect the parameter type names. StringRef firstParamName; @@ -837,8 +835,7 @@ static bool omitNeedlessWordsInFunctionName( bool hasDefaultArg = ClangImporter::Implementation::inferDefaultArgument( param->getType(), - getParamOptionality(swiftLanguageVersion, param, - !nonNullArgs.empty() && nonNullArgs[i]), + getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[i]), nameImporter.getIdentifier(baseName), argumentName, i == 0, isLastParameter, nameImporter) != DefaultArgumentKind::None; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index a13fb40d28210..69034c5c3778e 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1747,8 +1747,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( // Check nullability of the parameter. OptionalTypeKind OptionalityOfParam = - getParamOptionality(SwiftContext.LangOpts.EffectiveLanguageVersion, - param, !nonNullArgs.empty() && nonNullArgs[index]); + getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[index]); ImportTypeKind importKind = ImportTypeKind::Parameter; if (param->hasAttr()) @@ -2127,8 +2126,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( // Check nullability of the parameter. OptionalTypeKind optionalityOfParam - = getParamOptionality(SwiftContext.LangOpts.EffectiveLanguageVersion, - param, + = getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[paramIndex]); bool allowNSUIntegerAsIntInParam = isFromSystemModule; From 1e5d30f5ca78204c8239cf4088fb4e1bebf141f7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Aug 2020 16:19:52 -0700 Subject: [PATCH 19/47] [Concurrency] Import Objective-C methods with completion handlers as async When a given Objective-C method has a completion handler parameter with an appropriate signature, import that Objective-C method as async. For example, consider the following CloudKit API: - (void)fetchShareParticipantWithUserRecordID:(CKRecordID *)userRecordID completionHandler:(void (^)(CKShareParticipant * _Nullable shareParticipant, NSError * _Nullable error))completionHandler; With the experimental concurrency model, this would import as: func fetchShareParticipant(withUserRecordID userRecordID: CKRecord.ID) async throws -> CKShare.Participant? The compiler will be responsible for turning the caller's continuation into a block to pass along to the completion handler. When the error parameter of the completion handler is non-null, the async call will result in that error being thrown. Otherwise, the other arguments passed to that completion handler will be returned as the result of the async call. async versions of methods are imported alongside their completion-handler versions, to maintain source compatibility with existing code that provides a completion handler. Note that this only covers the Clang importer portion of this task. --- include/swift/AST/Decl.h | 14 +- include/swift/AST/ForeignAsyncConvention.h | 81 ++++++++ include/swift/AST/ForeignErrorConvention.h | 23 ++- lib/AST/ASTContext.cpp | 23 +++ lib/AST/Decl.cpp | 5 +- lib/ClangImporter/ClangImporter.cpp | 7 + lib/ClangImporter/ImportDecl.cpp | 59 +++++- lib/ClangImporter/ImportName.cpp | 194 ++++++++++++++++++ lib/ClangImporter/ImportName.h | 61 +++++- lib/ClangImporter/ImportType.cpp | 55 +++++ lib/ClangImporter/SwiftLookupTable.cpp | 1 + test/ClangImporter/objc_async.swift | 22 ++ test/IDE/print_clang_objc_async.swift | 19 ++ .../usr/include/ObjCConcurrency.h | 14 ++ .../clang-importer-sdk/usr/include/module.map | 5 + utils/swift-api-dump.py | 4 + 16 files changed, 564 insertions(+), 23 deletions(-) create mode 100644 include/swift/AST/ForeignAsyncConvention.h create mode 100644 test/ClangImporter/objc_async.swift create mode 100644 test/IDE/print_clang_objc_async.swift create mode 100644 test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c21cac7948679..5842456999735 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -63,6 +63,7 @@ namespace swift { class Type; class Expr; class DeclRefExpr; + class ForeignAsyncConvention; class ForeignErrorConvention; class LiteralExpr; class BraceStmt; @@ -6148,7 +6149,15 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// being dropped altogether. `None` is returned for a normal function /// or method. Optional getForeignFunctionAsMethodSelfParameterIndex() const; - + + /// Set information about the foreign async convention used by this + /// declaration. + void setForeignAsyncConvention(const ForeignAsyncConvention &convention); + + /// Get information about the foreign async convention used by this + /// declaration, given that it is @objc and 'async'. + Optional getForeignAsyncConvention() const; + static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_AbstractFunctionDecl && D->getKind() <= DeclKind::Last_AbstractFunctionDecl; @@ -6277,7 +6286,8 @@ class FuncDecl : public AbstractFunctionDecl { DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, bool Throws, + DeclName Name, SourceLoc NameLoc, + bool Async, bool Throws, ParameterList *BodyParams, Type FnRetType, DeclContext *Parent, ClangNode ClangN); diff --git a/include/swift/AST/ForeignAsyncConvention.h b/include/swift/AST/ForeignAsyncConvention.h new file mode 100644 index 0000000000000..f3657766e02af --- /dev/null +++ b/include/swift/AST/ForeignAsyncConvention.h @@ -0,0 +1,81 @@ +//===--- ForeignAsyncConvention.h - Async conventions -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the ForeignAsyncConvention structure, which +// describes the rules for how to detect that a foreign API is asynchronous. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_FOREIGN_ASYNC_CONVENTION_H +#define SWIFT_FOREIGN_ASYNC_CONVENTION_H + +#include "swift/AST/Type.h" + +namespace swift { + +/// A small structure describing the async convention of a foreign declaration. +class ForeignAsyncConvention { + /// The index of the completion handler parameters. + unsigned CompletionHandlerParamIndex; + + /// When non-zero, indicates which parameter to the completion handler is the + /// Error? parameter (minus one) that makes this async function also throwing. + unsigned CompletionHandlerErrorParamIndex; +public: + ForeignAsyncConvention() + : CompletionHandlerParamIndex(0), CompletionHandlerErrorParamIndex(0) { } + + ForeignAsyncConvention(unsigned completionHandlerParamIndex, + Optional completionHandlerErrorParamIndex) + : CompletionHandlerParamIndex(completionHandlerParamIndex), + CompletionHandlerErrorParamIndex( + completionHandlerErrorParamIndex + ? *completionHandlerErrorParamIndex + 1 + : 0) {} + + /// Retrieve the index of the completion handler parameter, which will be + /// erased from the Swift signature of the imported async function. + unsigned completionHandlerParamIndex() const { + return CompletionHandlerParamIndex; + } + + /// Retrieve the index of the \c Error? parameter in the completion handler's + /// parameter list. When argument passed to this parameter is non-null, the + /// provided error will be thrown by the async function. + Optional completionHandlerErrorParamIndex() const { + if (CompletionHandlerErrorParamIndex == 0) + return None; + + return CompletionHandlerErrorParamIndex - 1; + } + + /// Whether the async function is throwing due to the completion handler + /// having an \c Error? parameter. + /// + /// Equivalent to \c static_cast(completionHandlerErrorParamIndex()). + bool isThrowing() const { + return CompletionHandlerErrorParamIndex != 0; + } + + bool operator==(ForeignAsyncConvention other) const { + return CompletionHandlerParamIndex == other.CompletionHandlerParamIndex + && CompletionHandlerErrorParamIndex == + other.CompletionHandlerErrorParamIndex; + } + bool operator!=(ForeignAsyncConvention other) const { + return !(*this == other); + } +}; + +} + +#endif diff --git a/include/swift/AST/ForeignErrorConvention.h b/include/swift/AST/ForeignErrorConvention.h index 48d65d4612856..d09a7a80c0d86 100644 --- a/include/swift/AST/ForeignErrorConvention.h +++ b/include/swift/AST/ForeignErrorConvention.h @@ -81,6 +81,10 @@ class ForeignErrorConvention { } Info() = default; + + Kind getKind() const { + return static_cast(TheKind); + } }; private: @@ -178,11 +182,24 @@ class ForeignErrorConvention { /// Returns the physical result type of the function, for functions /// that completely erase this information. CanType getResultType() const { - assert(getKind() == ZeroResult || - getKind() == NonZeroResult); + assert(resultTypeErasedToVoid(getKind())); return ResultType; } - + + /// Whether this kind of error import erases the result type to 'Void'. + static bool resultTypeErasedToVoid(Kind kind) { + switch (kind) { + case ZeroResult: + case NonZeroResult: + return true; + + case ZeroPreservedResult: + case NilResult: + case NonNilError: + return false; + } + } + bool operator==(ForeignErrorConvention other) const { return info.TheKind == other.info.TheKind && info.ErrorIsOwned == other.info.ErrorIsOwned diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 633527ccf7735..e4f59973c6a07 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -18,6 +18,7 @@ #include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" @@ -281,6 +282,10 @@ struct ASTContext::Implementation { llvm::DenseMap ForeignErrorConventions; + /// Map from declarations to foreign async conventions. + llvm::DenseMap ForeignAsyncConventions; + /// Cache of previously looked-up precedence queries. AssociativityCacheType AssociativityCache; @@ -2238,6 +2243,24 @@ AbstractFunctionDecl::getForeignErrorConvention() const { return it->second; } +void AbstractFunctionDecl::setForeignAsyncConvention( + const ForeignAsyncConvention &conv) { + assert(hasAsync() && "setting error convention on non-throwing decl"); + auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions; + assert(!conventionsMap.count(this) && "error convention already set"); + conventionsMap.insert({this, conv}); +} + +Optional +AbstractFunctionDecl::getForeignAsyncConvention() const { + if (!hasAsync()) + return None; + auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions; + auto it = conventionsMap.find(this); + if (it == conventionsMap.end()) return None; + return it->second; +} + Optional swift::getKnownFoundationEntity(StringRef name){ return llvm::StringSwitch>(name) #define FOUNDATION_ENTITY(Name) .Case(#Name, KnownFoundationEntity::Name) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4fdb7199e4ba3..f1f17cbf3c042 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7255,13 +7255,14 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, - bool Throws, ParameterList *BodyParams, + bool Async, bool Throws, + ParameterList *BodyParams, Type FnRetType, DeclContext *Parent, ClangNode ClangN) { assert(ClangN && FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - /*Async=*/false, SourceLoc(), Throws, SourceLoc(), + Async, SourceLoc(), Throws, SourceLoc(), /*GenericParams=*/nullptr, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 74a7e4f17fe8e..e1f1ad590b202 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3681,6 +3681,7 @@ void ClangImporter::Implementation::lookupValue( clangDecl->getMostRecentDecl(); CurrentVersion.forEachOtherImportNameVersion( + SwiftContext.LangOpts.EnableExperimentalConcurrency, [&](ImportNameVersion nameVersion) { if (anyMatching) return; @@ -3690,6 +3691,12 @@ void ClangImporter::Implementation::lookupValue( if (!newName.getDeclName().matchesRef(name)) return; + // If we asked for an async import and didn't find one, skip this. + // This filters out duplicates. + if (nameVersion.supportsConcurrency() && + !newName.getAsyncInfo()) + return; + const clang::DeclContext *clangDC = newName.getEffectiveContext().getAsDeclContext(); if (!clangDC || !clangDC->isFileContext()) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 21488368a4dbe..52f26c7d0eaaf 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -160,6 +160,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, DeclName name, SourceLoc nameLoc, ParameterList *bodyParams, Type resultTy, + bool async, bool throws, DeclContext *dc, ClangNode clangNode) { @@ -176,7 +177,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, bodyParams, resultTy, dc, clangNode); } else { - return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, throws, + return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, async, throws, bodyParams, resultTy, dc, clangNode); } } @@ -2254,7 +2255,7 @@ namespace { /// Whether the names we're importing are from the language version the user /// requested, or if these are decls from another version bool isActiveSwiftVersion() const { - return getVersion() == getActiveSwiftVersion(); + return getVersion().withConcurrency(false) == getActiveSwiftVersion().withConcurrency(false); } void recordMemberInContext(const DeclContext *dc, ValueDecl *member) { @@ -2300,7 +2301,7 @@ namespace { return canonicalName; } - // Special handling when we import using the older Swift name. + // Special handling when we import using the alternate Swift name. // // Import using the alternate Swift name. If that fails, or if it's // identical to the active Swift name, we won't introduce an alternate @@ -2309,6 +2310,19 @@ namespace { if (!alternateName) return ImportedName(); + // Importing for concurrency is special in that the same declaration + // is imported both with a completion handler parameter and as 'async', + // creating two separate declarations. + if (getVersion().supportsConcurrency()) { + // If the resulting name isn't special for concurrency, it's not + // different. + if (!alternateName.getAsyncInfo()) + return ImportedName(); + + // Otherwise, it's a legitimately different import. + return alternateName; + } + if (alternateName.getDeclName() == canonicalName.getDeclName() && alternateName.getEffectiveContext().equalsWithoutResolving( canonicalName.getEffectiveContext())) { @@ -2470,6 +2484,13 @@ namespace { return; } + // If this the active and current Swift versions differ based on + // concurrency, it's not actually a variant. + if (getVersion().supportsConcurrency() != + getActiveSwiftVersion().supportsConcurrency()) { + return; + } + // TODO: some versions should be deprecated instead of unavailable ASTContext &ctx = decl->getASTContext(); @@ -3837,9 +3858,10 @@ namespace { // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); - result = createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*throws*/ false, dc, decl); + result = createFuncOrAccessor( + Impl.SwiftContext, loc, accessorInfo, name, + nameLoc, bodyParams, resultTy, + /*async*/ false, /*throws*/ false, dc, decl); if (!dc->isModuleScopeContext()) { if (selfIsInOut) @@ -4410,6 +4432,16 @@ namespace { } } + // Determine whether the function is throwing and/or async. + bool throws = importedName.getErrorInfo().hasValue(); + bool async = false; + auto asyncConvention = importedName.getAsyncInfo(); + if (asyncConvention) { + async = true; + if (asyncConvention->isThrowing()) + throws = true; + } + auto resultTy = importedType.getType(); auto isIUO = importedType.isImplicitlyUnwrapped(); @@ -4439,8 +4471,7 @@ namespace { importedName.getDeclName(), /*nameLoc*/SourceLoc(), bodyParams, resultTy, - importedName.getErrorInfo().hasValue(), - dc, decl); + async, throws, dc, decl); result->setAccess(getOverridableAccessLevel(dc)); @@ -4472,6 +4503,11 @@ namespace { result->setForeignErrorConvention(*errorConvention); } + // Record the async convention. + if (asyncConvention) { + result->setForeignAsyncConvention(*asyncConvention); + } + // Handle attributes. if (decl->hasAttr() && isa(result) && @@ -4503,6 +4539,7 @@ namespace { Impl.addAlternateDecl(result, cast(imported)); } } + return result; } @@ -6443,6 +6480,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( return known->second; // Create the actual constructor. + assert(!importedName.getAsyncInfo()); auto result = Impl.createDeclWithClangNode( objcMethod, AccessLevel::Public, importedName.getDeclName(), /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), @@ -7145,6 +7183,11 @@ void SwiftDeclConverter::importMirroredProtocolMembers( if (isa(afd)) return; + // Asynch methods are also always imported without async, so don't + // record them here. + if (afd->hasAsync()) + return; + auto objcMethod = dyn_cast_or_null(member->getClangDecl()); if (!objcMethod) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 088b62ea3d2a6..791c695ff1b91 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1136,6 +1136,174 @@ Optional NameImporter::considerErrorImport( return None; } +/// Whether the given parameter name identifies a completion handler. +static bool isCompletionHandlerParamName(StringRef paramName) { + return paramName == "completionHandler" || paramName == "completion" || + paramName == "withCompletionHandler"; +} + +/// Whether the give base name implies that the first parameter is a completion +/// handler. +/// +/// \returns a trimmed base name when it does, \c None others +static Optional isCompletionHandlerInBaseName(StringRef basename) { + if (basename.endswith("WithCompletionHandler")) { + return basename.drop_back(strlen("WithCompletionHandler")); + } + + if (basename.endswith("WithCompletion")) { + return basename.drop_back(strlen("WithCompletion")); + } + + return None; +} + + +// Determine whether the given type is a nullable NSError type. +static bool isNullableNSErrorType( + clang::ASTContext &clangCtx, clang::QualType type) { + auto objcPtrType = type->getAs(); + if (!objcPtrType) + return false; + + auto iface = objcPtrType->getInterfaceDecl(); + if (!iface || iface->getName() != "NSError") + return false; + + // If nullability is specified, check it. + if (auto nullability = type->getNullability(clangCtx)) { + switch (translateNullability(*nullability)) { + case OTK_None: + return false; + + case OTK_ImplicitlyUnwrappedOptional: + case OTK_Optional: + return true; + } + } + + // Otherwise, assume it's nullable. + return true; +} + +Optional +NameImporter::considerAsyncImport( + const clang::ObjCMethodDecl *clangDecl, + StringRef &baseName, + SmallVectorImpl ¶mNames, + ArrayRef params, + bool isInitializer, bool hasCustomName, + Optional errorInfo) { + // If there are no unclaimed parameters, there's no . + unsigned errorParamAdjust = errorInfo ? 1 : 0; + if (params.size() - errorParamAdjust == 0) + return None; + + // If the # of parameter names doesn't line up with the # of parameters, + // bail out. There are extra C parameters on the method or a custom name + // was incorrect. + if (params.size() != paramNames.size() + errorParamAdjust) + return None; + + // The last parameter will be the completion handler for an async function. + unsigned completionHandlerParamIndex = params.size() - 1; + unsigned completionHandlerParamNameIndex = paramNames.size() - 1; + + // Determine whether the naming indicates that this is a completion + // handler. + Optional newBaseName; + if (isCompletionHandlerParamName(paramNames[completionHandlerParamNameIndex])) { + // The parameter itself has an appropriate name. + } else if (!hasCustomName && completionHandlerParamIndex == 0 && + (newBaseName = isCompletionHandlerInBaseName(baseName))) { + // The base name implies that the first parameter is a completion handler. + } else { + return None; + } + + // Used for returns once we've determined that the method cannot be + // imported as async, even though it has what looks like a completion handler + // parameter. + auto notAsync = [&](const char *reason) -> Optional { +#ifdef ASYNC_IMPORT_DEBUG + llvm::errs() << "*** failed async import: " << reason << "\n"; + clangDecl->dump(llvm::errs()); +#endif + + return None; + }; + + // Initializers cannot be 'async'. + // FIXME: We might eventually allow this. + if (isInitializer) + return notAsync("initializers cannot be async"); + + // Check whether we method has a suitable return type. + if (clangDecl->getReturnType()->isVoidType()) { + // 'void' is the common case; the method produces no synchronous result. + } else if (errorInfo && + ForeignErrorConvention::resultTypeErasedToVoid( + errorInfo->getKind())) { + // The method has been imported as throwing in a manner that erased the + // result type to Void. + } else { + return notAsync("method does not return void"); + } + + // The completion handler parameter must have block type. + auto completionHandlerParam = params[completionHandlerParamIndex]; + if (!isBlockParameter(completionHandlerParam)) + return notAsync("parameter is not a block"); + + // Dig out the function type of the completion handler's block type. + // If there is no prototype, (e.g., the completion handler is of type + // void (^)()), we cannot importer it. + auto completionHandlerFunctionType = + completionHandlerParam->getType()->castAs() + ->getPointeeType()->getAs(); + if (!completionHandlerFunctionType) + return notAsync("block parameter does not have a prototype"); + + // The completion handler parameter must itself return 'void'. + if (!completionHandlerFunctionType->getReturnType()->isVoidType()) + return notAsync("completion handler parameter does not return 'void'"); + + // Scan the parameters of the block type to look for a parameter of a + // nullable NSError type, which would indicate that the async method could + // throw. + Optional completionHandlerErrorParamIndex; + auto completionHandlerParamTypes = + completionHandlerFunctionType->getParamTypes(); + auto &clangCtx = clangDecl->getASTContext(); + for (unsigned paramIdx : indices(completionHandlerParamTypes)) { + auto paramType = completionHandlerParamTypes[paramIdx]; + + // We are only interested in nullable NSError parameters. + if (!isNullableNSErrorType(clangCtx, paramType)) + continue; + + // If this is the first nullable error parameter, note that. + if (!completionHandlerErrorParamIndex) { + completionHandlerErrorParamIndex = paramIdx; + continue; + } + + // More than one nullable NSError parameter. Don't import as throwing. + completionHandlerErrorParamIndex = None; + break; + } + + // Drop the completion handler parameter name. + paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex); + + // Update the base name, if needed. + if (newBaseName && !hasCustomName) + baseName = *newBaseName; + + return ForeignAsyncConvention( + completionHandlerParamIndex, completionHandlerErrorParamIndex); +} + bool NameImporter::hasErrorMethodNameCollision( const clang::ObjCMethodDecl *method, unsigned paramIndex, StringRef suffixToStrip) { @@ -1359,6 +1527,21 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.hasErrorInfo = true; result.info.errorInfo = *errorInfo; } + + if (version.supportsConcurrency()) { + if (auto asyncInfo = considerAsyncImport( + method, parsedName.BaseName, parsedName.ArgumentLabels, + params, isInitializer, /*hasCustomName=*/true, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + + // Update the name to reflect the new parameter labels. + result.declName = formDeclName( + swiftCtx, parsedName.BaseName, parsedName.ArgumentLabels, + /*isFunction=*/true, isInitializer); + } + } } return result; @@ -1604,6 +1787,16 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.errorInfo = *errorInfo; } + if (version.supportsConcurrency()) { + if (auto asyncInfo = considerAsyncImport( + objcMethod, baseName, argumentNames, params, isInitializer, + /*hasCustomName=*/false, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + } + } + isFunction = true; // Is this one of the accessors for subscripts? @@ -1892,6 +2085,7 @@ bool NameImporter::forEachDistinctImportName( seenNames.push_back(key); activeVersion.forEachOtherImportNameVersion( + swiftCtx.LangOpts.EnableExperimentalConcurrency, [&](ImportNameVersion nameVersion) { // Check to see if the name is different. ImportedName newName = importName(decl, nameVersion); diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index ddb4b0aa4105c..09d90703563c9 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -23,6 +23,7 @@ #include "swift/Basic/Version.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "clang/Sema/Sema.h" @@ -42,15 +43,18 @@ enum { NumImportedAccessorKindBits = 3 }; /// The name version class ImportNameVersion : public RelationalOperationsBase { - unsigned rawValue; + unsigned rawValue : 31; + unsigned concurrency : 1; + friend llvm::DenseMapInfo; enum AsConstExpr_t { AsConstExpr }; - constexpr ImportNameVersion() : rawValue(0) {} + constexpr ImportNameVersion() : rawValue(0), concurrency(false) {} constexpr ImportNameVersion(unsigned version, AsConstExpr_t) - : rawValue(version) {} - explicit ImportNameVersion(unsigned version) : rawValue(version) { + : rawValue(version), concurrency(false) {} + explicit ImportNameVersion(unsigned version, bool concurrency = false) + : rawValue(version), concurrency(concurrency) { assert(version >= 2 && "only Swift 2 and later are supported"); } public: @@ -67,7 +71,7 @@ class ImportNameVersion : public RelationalOperationsBase { return ImportNameVersion::swift4_2(); } unsigned major = version[0]; - return ImportNameVersion(major >= 5 ? major + 1 : major); + return ImportNameVersion(major >= 5 ? major + 1 : major, false); } unsigned majorVersionNumber() const { @@ -89,11 +93,21 @@ class ImportNameVersion : public RelationalOperationsBase { return llvm::VersionTuple(majorVersionNumber(), minorVersionNumber()); } + /// Whether to consider importing functions as 'async'. + bool supportsConcurrency() const { return concurrency; } + + ImportNameVersion withConcurrency(bool concurrency) const { + ImportNameVersion result = *this; + result.concurrency = concurrency; + return result; + } + bool operator==(ImportNameVersion other) const { - return rawValue == other.rawValue; + return rawValue == other.rawValue && concurrency == other.concurrency; } bool operator<(ImportNameVersion other) const { - return rawValue < other.rawValue; + return rawValue < other.rawValue || + (rawValue == other.rawValue && concurrency < other.concurrency); } /// Calls \p action for each name version other than this one, first going @@ -102,10 +116,19 @@ class ImportNameVersion : public RelationalOperationsBase { /// /// This is the most useful order for importing compatibility stubs. void forEachOtherImportNameVersion( + bool withConcurrency, llvm::function_ref action) const { assert(*this >= ImportNameVersion::swift2()); ImportNameVersion nameVersion = *this; + assert(!nameVersion.supportsConcurrency()); + + // If we've been asked to also consider concurrency, do so for the + // primary version (only). + if (withConcurrency) { + action(nameVersion.withConcurrency(true)); + } + while (nameVersion > ImportNameVersion::swift2()) { --nameVersion.rawValue; action(nameVersion); @@ -175,6 +198,10 @@ class ImportedName { /// throwing Swift methods, describes how the mapping is performed. ForeignErrorConvention::Info errorInfo; + /// For names that map Objective-C completion handlers into async + /// Swift methods, describes how the mapping is performed. + ForeignAsyncConvention asyncInfo; + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. unsigned selfIndex; @@ -201,11 +228,13 @@ class ImportedName { unsigned hasErrorInfo : 1; + unsigned hasAsyncInfo : 1; + Info() : errorInfo(), selfIndex(), initKind(CtorInitializerKind::Designated), accessorKind(ImportedAccessorKind::None), hasCustomName(false), droppedVariadic(false), importAsMember(false), hasSelfIndex(false), - hasErrorInfo(false) {} + hasErrorInfo(false), hasAsyncInfo(false) {} } info; public: @@ -239,6 +268,14 @@ class ImportedName { return None; } + /// For names that map Objective-C methods with completion handlers into + /// async Swift methods, describes how the mapping is performed. + Optional getAsyncInfo() const { + if (info.hasAsyncInfo) + return info.asyncInfo; + return None; + } + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. Optional getSelfIndex() const { @@ -416,6 +453,14 @@ class NameImporter { ArrayRef params, bool isInitializer, bool hasCustomName); + Optional + considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, + StringRef &baseName, + SmallVectorImpl ¶mNames, + ArrayRef params, + bool isInitializer, bool hasCustomName, + Optional errorInfo); + EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *, const clang::DeclContext *, ImportNameVersion version); diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 69034c5c3778e..e9e0599b5f8fc 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1988,6 +1988,42 @@ static Type mapGenericArgs(const DeclContext *fromDC, return type.subst(subs); } +/// Decompose the type of a completion handler parameter in a function +/// imported as 'async' and produce the result type of the 'async' function. +static Type decomposeCompletionHandlerType( + Type paramTy, ForeignAsyncConvention info) { + auto fnType = paramTy->lookThroughAllOptionalTypes()->getAs(); + if (!fnType) + return Type(); + + SmallVector resultTypeElts; + auto params = fnType->getParams(); + for (unsigned paramIdx : indices(params)) { + const auto ¶m = params[paramIdx]; + if (param.isInOut() || param.isVariadic()) + return Type(); + + // If there is an error parameter to the completion handler, it is + // not part of the result type of the asynchronous function. + if (info.completionHandlerErrorParamIndex() && + paramIdx == *info.completionHandlerErrorParamIndex()) + continue; + + resultTypeElts.push_back(param.getPlainType()); + } + + switch (resultTypeElts.size()) { + case 0: + return paramTy->getASTContext().getVoidDecl()->getDeclaredInterfaceType(); + + case 1: + return resultTypeElts.front().getType(); + + default: + return TupleType::get(resultTypeElts, paramTy->getASTContext()); + } +} + ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( const DeclContext *dc, const clang::ObjCMethodDecl *clangDecl, ArrayRef params, bool isVariadic, @@ -2024,6 +2060,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( CanType origSwiftResultTy; Optional errorInfo = importedName.getErrorInfo(); + auto asyncInfo = importedName.getAsyncInfo(); OptionalTypeKind OptionalityOfReturn; if (clangDecl->hasAttr()) { OptionalityOfReturn = OTK_None; @@ -2122,6 +2159,9 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( continue; } + bool paramIsCompletionHandler = + asyncInfo && paramIndex == asyncInfo->completionHandlerParamIndex(); + // Import the parameter type into Swift. // Check nullability of the parameter. @@ -2191,6 +2231,21 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( continue; } + // If this is a completion handler, figure out it's effect on the result + // type but don't build it into the parameter type. + if (paramIsCompletionHandler) { + if (Type replacedSwiftResultTy = + decomposeCompletionHandlerType(swiftParamTy, *asyncInfo)) { + swiftResultTy = replacedSwiftResultTy; + + // FIXME: We will need an equivalent to "error parameter is replaced" + // for asynchronous functions. Perhaps add "async: ()"? + continue; + } + + llvm_unreachable("async info computed incorrectly?"); + } + // Map __attribute__((noescape)) to @noescape. bool addNoEscapeAttr = false; if (param->hasAttr()) { diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 3ac0d0fe45d0c..6653b2d0ee38d 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -1857,6 +1857,7 @@ SwiftNameLookupExtension::hashExtension(llvm::hash_code code) const { SWIFT_LOOKUP_TABLE_VERSION_MAJOR, SWIFT_LOOKUP_TABLE_VERSION_MINOR, inferImportAsMember, + swiftCtx.LangOpts.EnableExperimentalConcurrency, version::getSwiftFullVersion()); } diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift new file mode 100644 index 0000000000000..8ec33ae643905 --- /dev/null +++ b/test/ClangImporter/objc_async.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify + +// REQUIRES: objc_interop +import Foundation +import ObjCConcurrency + +func testSlowServer(slowServer: SlowServer) async { + let _: Int = await slowServer.doSomethingSlow("mail") + let _: Bool = await slowServer.checkAvailability() + let _: String = await slowServer.findAnswer() ?? "nope" + let _: String = await slowServer.findAnswerFailingly() ?? "nope" + // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} + // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} +} + +func testSlowServerOldSchool(slowServer: SlowServer) { + var i1: Int = 0 + slowServer.doSomethingSlow("mail") { i in + i1 = i + } + print(i1) +} diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift new file mode 100644 index 0000000000000..6e4539de8825c --- /dev/null +++ b/test/IDE/print_clang_objc_async.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false -enable-experimental-concurrency > %t/ObjCConcurrency.printed.txt +// RUN: %FileCheck -input-file %t/ObjCConcurrency.printed.txt %s + +// REQUIRES: objc_interop + +// CHECK-LABEL: class SlowServer : NSObject { +// CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) +// CHECK-DAG: func doSomethingSlow(_ operation: String) async -> Int +// CHECK-DAG: func doSomethingDangerous(_ operation: String, completionHandler handler: ((String?, Error?) -> Void)? = nil) +// CHECK-DAG: func doSomethingDangerous(_ operation: String) async throws -> String? +// CHECK-DAG: func checkAvailability(completionHandler: @escaping (Bool) -> Void) +// CHECK-DAG: func checkAvailability() async -> Bool +// CHECK-DAG: func findAnswer(completionHandler handler: @escaping (String?, Error?) -> Void) +// CHECK-DAG: func findAnswer() async throws -> String? +// CHECK-DAG: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws +// CHECK-DAG: func findAnswerFailingly() async throws -> String? +// CHECK: {{^[}]$}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h new file mode 100644 index 0000000000000..9903bd0a91a64 --- /dev/null +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -0,0 +1,14 @@ +@import Foundation; +@import ctypes; + +#pragma clang assume_nonnull begin + +@interface SlowServer : NSObject +-(void)doSomethingSlow:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; +-(void)doSomethingDangerous:(NSString *)operation completionHandler:(void (^ _Nullable)(NSString *_Nullable, NSError * _Nullable))handler; +-(void)checkAvailabilityWithCompletionHandler:(void (^)(BOOL isAvailable))completionHandler; +-(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); +-(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); +@end + +#pragma clang assume_nonnull end diff --git a/test/Inputs/clang-importer-sdk/usr/include/module.map b/test/Inputs/clang-importer-sdk/usr/include/module.map index 61b062ab524eb..366101cbbb723 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/module.map +++ b/test/Inputs/clang-importer-sdk/usr/include/module.map @@ -136,3 +136,8 @@ module WinBOOL { header "winbool.h" export * } + +module ObjCConcurrency { + header "ObjCConcurrency.h" + export * +} \ No newline at end of file diff --git a/utils/swift-api-dump.py b/utils/swift-api-dump.py index 9022e9c89d5b2..d44b33ae19839 100755 --- a/utils/swift-api-dump.py +++ b/utils/swift-api-dump.py @@ -102,6 +102,8 @@ def create_parser(): parser.add_argument('--enable-infer-import-as-member', action='store_true', help='Infer when a global could be imported as a ' + 'member.') + parser.add_argument('--enable-experimental-concurrency', action='store_true', + help='Enable experimental concurrency model.') parser.add_argument('-swift-version', metavar='N', help='the Swift version to use') parser.add_argument('-show-overlay', action='store_true', @@ -328,6 +330,8 @@ def main(): extra_args = ['-skip-imports'] if args.enable_infer_import_as_member: extra_args = extra_args + ['-enable-infer-import-as-member'] + if args.enable_experimental_concurrency: + extra_args = extra_args + ['-enable-experimental-concurrency'] if args.swift_version: extra_args = extra_args + ['-swift-version', '%s' % args.swift_version] From 1132cda811369be9fa6d8d43d0ddbf0d00fd6fac Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 27 Aug 2020 23:00:48 -0700 Subject: [PATCH 20/47] [ownership] Move SemanticARCOpts into a separate folder in preparation for splitting into multiple small pseudo-passes. SemanticARCOpts keeps on growing with various optimizations attached to a single "optimization" manager. Move it to its own folder in prepation for splitting it into multiple different optimizations and utility files. --- lib/SILOptimizer/CMakeLists.txt | 1 + lib/SILOptimizer/SemanticARC/CMakeLists.txt | 2 + .../SemanticARCOpts.cpp | 43 ++++++++----------- lib/SILOptimizer/Transforms/CMakeLists.txt | 1 - 4 files changed, 22 insertions(+), 25 deletions(-) create mode 100644 lib/SILOptimizer/SemanticARC/CMakeLists.txt rename lib/SILOptimizer/{Transforms => SemanticARC}/SemanticARCOpts.cpp (99%) diff --git a/lib/SILOptimizer/CMakeLists.txt b/lib/SILOptimizer/CMakeLists.txt index 4bf45e0ebd8db..ec1440d2b06a1 100644 --- a/lib/SILOptimizer/CMakeLists.txt +++ b/lib/SILOptimizer/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(LoopTransforms) add_subdirectory(Mandatory) add_subdirectory(PassManager) add_subdirectory(SILCombiner) +add_subdirectory(SemanticARC) add_subdirectory(Transforms) add_subdirectory(UtilityPasses) add_subdirectory(Utils) diff --git a/lib/SILOptimizer/SemanticARC/CMakeLists.txt b/lib/SILOptimizer/SemanticARC/CMakeLists.txt new file mode 100644 index 0000000000000..7ae8f36a2bbde --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(swiftSILOptimizer PRIVATE + SemanticARCOpts.cpp) diff --git a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp similarity index 99% rename from lib/SILOptimizer/Transforms/SemanticARCOpts.cpp rename to lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 743dac16eaa6b..180f3882a7807 100644 --- a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -1034,8 +1034,8 @@ struct SemanticARCOptVisitor } // end anonymous namespace static llvm::cl::opt -VerifyAfterTransform("sil-semantic-arc-opts-verify-after-transform", - llvm::cl::init(false), llvm::cl::Hidden); + VerifyAfterTransform("sil-semantic-arc-opts-verify-after-transform", + llvm::cl::init(false), llvm::cl::Hidden); static bool canEliminatePhi( SemanticARCOptVisitor::FrozenMultiMapRange optimizableIntroducerRange, @@ -1436,7 +1436,8 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { // are within the borrow scope. // // TODO: This needs a better name. -bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst *cvi) { +bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization( + CopyValueInst *cvi) { // For now, do not run this optimization. This is just to be careful. if (onlyGuaranteedOpts) return false; @@ -1617,7 +1618,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst /// If cvi only has destroy value users, then cvi is a dead live range. Lets /// eliminate all such dead live ranges. -bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) { +bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue( + CopyValueInst *cvi) { // This is a cheap optimization generally. // See if we are lucky and have a simple case. @@ -1857,8 +1859,7 @@ namespace { /// written to again. In both cases, we can convert load [copy] -> load_borrow /// safely. class StorageGuaranteesLoadVisitor - : public AccessUseDefChainVisitor -{ + : public AccessUseDefChainVisitor { // The outer SemanticARCOptVisitor. SemanticARCOptVisitor &ARCOpt; @@ -1867,7 +1868,7 @@ class StorageGuaranteesLoadVisitor // The current address being visited. SILValue currentAddress; - + Optional isWritten; public: @@ -1880,11 +1881,9 @@ class StorageGuaranteesLoadVisitor currentAddress = nullptr; isWritten = written; } - - void next(SILValue address) { - currentAddress = address; - } - + + void next(SILValue address) { currentAddress = address; } + void visitNestedAccess(BeginAccessInst *access) { // First see if we have read/modify. If we do not, just look through the // nested access. @@ -1901,9 +1900,7 @@ class StorageGuaranteesLoadVisitor // scope. If so, we may be able to use a load_borrow here! SmallVector endScopeUses; transform(access->getEndAccesses(), std::back_inserter(endScopeUses), - [](EndAccessInst *eai) { - return &eai->getAllOperands()[0]; - }); + [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; }); SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); if (!checker.validateLifetime(access, endScopeUses, @@ -1930,7 +1927,7 @@ class StorageGuaranteesLoadVisitor return answer(true); } - + void visitArgumentAccess(SILFunctionArgument *arg) { // If this load_copy is from an indirect in_guaranteed argument, then we // know for sure that it will never be written to. @@ -2007,15 +2004,15 @@ class StorageGuaranteesLoadVisitor // able to also to promote load [copy] from such args to load_borrow. return answer(true); } - + void visitGlobalAccess(SILValue global) { return answer(!AccessedStorage(global, AccessedStorage::Global) - .isLetAccess(&ARCOpt.F)); + .isLetAccess(&ARCOpt.F)); } - + void visitClassAccess(RefElementAddrInst *field) { currentAddress = nullptr; - + // We know a let property won't be written to if the base object is // guaranteed for the duration of the access. // For non-let properties conservatively assume they may be written to. @@ -2071,15 +2068,13 @@ class StorageGuaranteesLoadVisitor baseObject, endScopeInsts, liveRange.getAllConsumingUses()); return answer(foundError); } - + // TODO: Handle other access kinds? void visitBase(SILValue base, AccessedStorage::Kind kind) { return answer(true); } - void visitNonAccess(SILValue addr) { - return answer(true); - } + void visitNonAccess(SILValue addr) { return answer(true); } void visitCast(SingleValueInstruction *cast, Operand *parentAddr) { return next(parentAddr->get()); diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index c51043a1910c3..ac3718b6cf20f 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -31,7 +31,6 @@ target_sources(swiftSILOptimizer PRIVATE RedundantLoadElimination.cpp RedundantOverflowCheckRemoval.cpp ReleaseDevirtualizer.cpp - SemanticARCOpts.cpp SILCodeMotion.cpp SILLowerAggregateInstrs.cpp SILMem2Reg.cpp From fd6922f92dc2a0cd6f47da0969533d30f89b186c Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 19 Aug 2020 12:30:34 -0400 Subject: [PATCH 21/47] Add error reporting when looking up types by demangled name. --- include/swift/Demangling/TypeDecoder.h | 306 ++++++++++-------- include/swift/Demangling/TypeLookupError.h | 198 ++++++++++++ include/swift/Reflection/TypeRefBuilder.h | 4 +- include/swift/Remote/MetadataReader.h | 11 +- include/swift/Runtime/Debug.h | 34 -- include/swift/Runtime/Portability.h | 39 +++ lib/AST/ASTDemangler.cpp | 11 +- lib/RemoteAST/RemoteAST.cpp | 7 +- stdlib/public/Reflection/TypeRefBuilder.cpp | 24 +- .../SwiftRemoteMirror/SwiftRemoteMirror.cpp | 2 +- .../public/runtime/CompatibilityOverride.def | 4 +- stdlib/public/runtime/Metadata.cpp | 61 ++-- stdlib/public/runtime/MetadataLookup.cpp | 255 +++++++++------ stdlib/public/runtime/Private.h | 21 +- stdlib/public/runtime/ProtocolConformance.cpp | 84 ++--- stdlib/public/runtime/ReflectionMirror.mm | 36 ++- stdlib/public/runtime/SwiftObject.mm | 3 +- stdlib/public/stubs/Assert.cpp | 1 + .../swift-reflection-dump.cpp | 11 +- unittests/runtime/CompatibilityOverride.cpp | 8 +- 20 files changed, 743 insertions(+), 377 deletions(-) create mode 100644 include/swift/Demangling/TypeLookupError.h diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index e591e978dccd1..da2ec53c6ccd4 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -18,10 +18,12 @@ #ifndef SWIFT_DEMANGLING_TYPEDECODER_H #define SWIFT_DEMANGLING_TYPEDECODER_H +#include "TypeLookupError.h" #include "swift/ABI/MetadataValues.h" +#include "swift/Basic/LLVM.h" #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" -#include "swift/Basic/LLVM.h" +#include "swift/Runtime/Portability.h" #include "swift/Runtime/Unreachable.h" #include "swift/Strings.h" #include "llvm/ADT/ArrayRef.h" @@ -317,6 +319,14 @@ getObjCClassOrProtocolName(NodePointer node) { } #endif +#define MAKE_NODE_TYPE_ERROR(Node, Fmt, ...) \ + TypeLookupError("TypeDecoder.h:%d: Node kind %u \"%.*s\" - " Fmt, __LINE__, \ + Node->getKind(), \ + Node->hasText() ? (int)Node->getText().size() : 0, \ + Node->hasText() ? Node->getText().data() : "", __VA_ARGS__) + +#define MAKE_NODE_TYPE_ERROR0(Node, Str) MAKE_NODE_TYPE_ERROR(Node, "%s", Str) + /// Decode a mangled type to construct an abstract type, forming such /// types by invoking a custom builder. template @@ -333,24 +343,25 @@ class TypeDecoder { : Builder(Builder) {} /// Given a demangle tree, attempt to turn it into a type. - BuiltType decodeMangledType(NodePointer Node) { - if (!Node) return BuiltType(); + TypeLookupErrorOr decodeMangledType(NodePointer Node) { + if (!Node) + return TypeLookupError("Node is NULL"); using NodeKind = Demangle::Node::Kind; switch (Node->getKind()) { case NodeKind::Global: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::TypeMangling: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::Type: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::Class: @@ -369,8 +380,8 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (!decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) - return BuiltType(); + if (auto error = decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) + return *error; if (typeAlias) return Builder.createTypeAliasType(typeDecl, parent); @@ -384,19 +395,21 @@ class TypeDecoder { case NodeKind::BoundGenericTypeAlias: case NodeKind::BoundGenericOtherNominalType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); llvm::SmallVector args; const auto &genericArgs = Node->getChild(1); if (genericArgs->getKind() != NodeKind::TypeList) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(genericArgs, "is not TypeList"); for (auto genericArg : *genericArgs) { auto paramType = decodeMangledType(genericArg); - if (!paramType) - return BuiltType(); - args.push_back(paramType); + if (paramType.isError()) + return paramType; + args.push_back(paramType.getType()); } auto ChildNode = Node->getChild(0); @@ -413,9 +426,9 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (!decodeMangledTypeDecl(ChildNode, typeDecl, - parent, typeAlias)) - return BuiltType(); + if (auto error = + decodeMangledTypeDecl(ChildNode, typeDecl, parent, typeAlias)) + return *error; return Builder.createBoundGenericType(typeDecl, args, parent); } @@ -445,11 +458,15 @@ class TypeDecoder { // But when resolving it to a type, we want to *keep* the argument // so that the parent type becomes 'S' and not 'P'. if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); const auto &genericArgs = Node->getChild(1); if (genericArgs->getNumChildren() != 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(genericArgs, + "expected 1 generic argument, saw %u", + genericArgs->getNumChildren()); return decodeMangledType(genericArgs->getChild(0)); } @@ -469,7 +486,7 @@ class TypeDecoder { auto reprNode = Node->getChild(i++); if (reprNode->getKind() != NodeKind::MetatypeRepresentation || !reprNode->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(reprNode, "wrong node kind or no text"); if (reprNode->getText() == "@thin") repr = ImplMetatypeRepresentation::Thin; else if (reprNode->getText() == "@thick") @@ -477,26 +494,28 @@ class TypeDecoder { else if (reprNode->getText() == "@objc_metatype") repr = ImplMetatypeRepresentation::ObjC; } else if (Node->getNumChildren() < 1) { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); } auto instance = decodeMangledType(Node->getChild(i)); - if (!instance) - return BuiltType(); + if (instance.isError()) + return instance; if (Node->getKind() == NodeKind::Metatype) { - return Builder.createMetatypeType(instance, repr); + return Builder.createMetatypeType(instance.getType(), repr); } else if (Node->getKind() == NodeKind::ExistentialMetatype) { - return Builder.createExistentialMetatypeType(instance, repr); + return Builder.createExistentialMetatypeType(instance.getType(), repr); } else { assert(false); - return nullptr; + return MAKE_NODE_TYPE_ERROR0(Node, + "Metatype/ExistentialMetatype Node " + "had a different kind when re-checked"); } } case NodeKind::ProtocolList: case NodeKind::ProtocolListWithAnyObject: case NodeKind::ProtocolListWithClass: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); // Find the protocol list. llvm::SmallVector Protocols; @@ -511,7 +530,8 @@ class TypeDecoder { if (auto Protocol = decodeMangledProtocolType(componentType)) Protocols.push_back(Protocol); else - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(componentType, + "failed to decode protocol type"); } // Superclass or AnyObject, if present. @@ -519,11 +539,15 @@ class TypeDecoder { auto Superclass = BuiltType(); if (Node->getKind() == NodeKind::ProtocolListWithClass) { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto superclassNode = Node->getChild(1); - Superclass = decodeMangledType(superclassNode); - if (!Superclass) return BuiltType(); + auto result = decodeMangledType(superclassNode); + if (result.isError()) + return result; + Superclass = result.getType(); IsClassBound = true; } else if (Node->getKind() == NodeKind::ProtocolListWithAnyObject) { @@ -541,17 +565,18 @@ class TypeDecoder { /*IsClassBound=*/false); } - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "failed to decode protocol type"); } case NodeKind::DynamicSelf: { if (Node->getNumChildren() != 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, "expected 1 child, saw %u", + Node->getNumChildren()); auto selfType = decodeMangledType(Node->getChild(0)); - if (!selfType) - return BuiltType(); + if (selfType.isError()) + return selfType; - return Builder.createDynamicSelfType(selfType); + return Builder.createDynamicSelfType(selfType.getType()); } case NodeKind::DependentGenericParamType: { auto depth = Node->getChild(0)->getIndex(); @@ -571,7 +596,9 @@ class TypeDecoder { case NodeKind::EscapingLinearFunctionType: case NodeKind::FunctionType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); FunctionTypeFlags flags; if (Node->getKind() == NodeKind::ObjCBlock || @@ -611,13 +638,16 @@ class TypeDecoder { flags = flags.withAsync(isAsync).withThrows(isThrow); if (Node->getNumChildren() < firstChildIdx + 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (%u)", + Node->getNumChildren(), firstChildIdx + 2); bool hasParamFlags = false; llvm::SmallVector, 8> parameters; if (!decodeMangledFunctionInputType(Node->getChild(firstChildIdx), parameters, hasParamFlags)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node->getChild(firstChildIdx), + "failed to decode function type"); flags = flags.withNumParameters(parameters.size()) .withParameterFlags(hasParamFlags) @@ -631,8 +661,9 @@ class TypeDecoder { NodeKind::EscapingLinearFunctionType); auto result = decodeMangledType(Node->getChild(firstChildIdx+1)); - if (!result) return BuiltType(); - return Builder.createFunctionType(parameters, result, flags); + if (result.isError()) + return result; + return Builder.createFunctionType(parameters, result.getType(), flags); } case NodeKind::ImplFunctionType: { auto calleeConvention = ImplParameterConvention::Direct_Unowned; @@ -646,7 +677,7 @@ class TypeDecoder { if (child->getKind() == NodeKind::ImplConvention) { if (!child->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); if (child->getText() == "@convention(thin)") { flags = @@ -656,7 +687,7 @@ class TypeDecoder { } } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { if (!child->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); StringRef text = child->getText(); if (text == "@convention(c)") { @@ -676,15 +707,18 @@ class TypeDecoder { flags = flags.withEscaping(); } else if (child->getKind() == NodeKind::ImplParameter) { if (decodeImplFunctionParam(child, parameters)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplResult) { if (decodeImplFunctionParam(child, results)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplErrorResult) { if (decodeImplFunctionPart(child, errorResults)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function part"); } else { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "unexpected kind"); } } @@ -696,7 +730,8 @@ class TypeDecoder { errorResult = errorResults.front(); break; default: - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, "got %zu errors", + errorResults.size()); } // TODO: Some cases not handled above, but *probably* they cannot @@ -711,13 +746,13 @@ class TypeDecoder { case NodeKind::ArgumentTuple: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); return decodeMangledType(Node->getChild(0)); case NodeKind::ReturnType: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); return decodeMangledType(Node->getChild(0)); @@ -726,12 +761,13 @@ class TypeDecoder { std::string labels; for (auto &element : *Node) { if (element->getKind() != NodeKind::TupleElement) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind"); // If the tuple element is labeled, add its label to 'labels'. unsigned typeChildIndex = 0; if (element->getChild(typeChildIndex)->getKind() == NodeKind::VariadicMarker) { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(element->getChild(typeChildIndex), + "no children"); } if (element->getChild(typeChildIndex)->getKind() == NodeKind::TupleElementName) { // Add spaces to terminate all the previous labels if this @@ -749,22 +785,23 @@ class TypeDecoder { } // Decode the element type. - BuiltType elementType = - decodeMangledType(element->getChild(typeChildIndex)); - if (!elementType) - return BuiltType(); + auto elementType = decodeMangledType(element->getChild(typeChildIndex)); + if (elementType.isError()) + return elementType; - elements.push_back(elementType); + elements.push_back(elementType.getType()); } return Builder.createTupleType(elements, std::move(labels)); } case NodeKind::TupleElement: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); if (Node->getChild(0)->getKind() == NodeKind::TupleElementName) { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } @@ -772,68 +809,75 @@ class TypeDecoder { case NodeKind::DependentGenericType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } case NodeKind::DependentMemberType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; auto assocTypeChild = Node->getChild(1); auto member = assocTypeChild->getFirstChild()->getText(); if (assocTypeChild->getNumChildren() < 2) - return Builder.createDependentMemberType(member.str(), base); + return Builder.createDependentMemberType(member.str(), base.getType()); auto protocol = decodeMangledProtocolType(assocTypeChild->getChild(1)); if (!protocol) return BuiltType(); - return Builder.createDependentMemberType(member.str(), base, protocol); + return Builder.createDependentMemberType(member.str(), base.getType(), + protocol); } case NodeKind::DependentAssociatedTypeRef: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } case NodeKind::Unowned: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createUnownedStorageType(base); + if (base.isError()) + return base; + return Builder.createUnownedStorageType(base.getType()); } case NodeKind::Unmanaged: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createUnmanagedStorageType(base); + if (base.isError()) + return base; + return Builder.createUnmanagedStorageType(base.getType()); } case NodeKind::Weak: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createWeakStorageType(base); + if (base.isError()) + return base; + return Builder.createWeakStorageType(base.getType()); } case NodeKind::SILBoxType: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createSILBoxType(base); + if (base.isError()) + return base; + return Builder.createSILBoxType(base.getType()); } case NodeKind::SILBoxTypeWithLayout: { // TODO: Implement SILBoxTypeRefs with layout. As a stopgap, specify the @@ -842,57 +886,62 @@ class TypeDecoder { } case NodeKind::SugaredOptional: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createOptionalType(base); + return Builder.createOptionalType(base.getType()); } case NodeKind::SugaredArray: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createArrayType(base); + return Builder.createArrayType(base.getType()); } case NodeKind::SugaredDictionary: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto key = decodeMangledType(Node->getChild(0)); - if (!key) - return BuiltType(); + if (key.isError()) + return key; auto value = decodeMangledType(Node->getChild(1)); - if (!key) - return BuiltType(); + if (value.isError()) + return value; - return Builder.createDictionaryType(key, value); + return Builder.createDictionaryType(key.getType(), value.getType()); } case NodeKind::SugaredParen: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createParenType(base); + return Builder.createParenType(base.getType()); } case NodeKind::OpaqueType: { if (Node->getNumChildren() < 3) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (3)", + Node->getNumChildren()); auto descriptor = Node->getChild(0); auto ordinalNode = Node->getChild(1); if (ordinalNode->getKind() != NodeKind::Index || !ordinalNode->hasIndex()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(ordinalNode, + "unexpected kind or no index"); auto ordinal = ordinalNode->getIndex(); std::vector genericArgsBuf; @@ -905,9 +954,9 @@ class TypeDecoder { break; for (auto argNode : *genericsNode) { auto arg = decodeMangledType(argNode); - if (!arg) - return BuiltType(); - genericArgsBuf.push_back(arg); + if (arg.isError()) + return arg; + genericArgsBuf.push_back(arg.getType()); } } genericArgsLevels.push_back(genericArgsBuf.size()); @@ -923,7 +972,7 @@ class TypeDecoder { // TODO: Handle OpaqueReturnType, when we're in the middle of reconstructing // the defining decl default: - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind"); } } @@ -943,11 +992,11 @@ class TypeDecoder { T::getConventionFromString(conventionString); if (!convention) return true; - BuiltType type = decodeMangledType(node->getChild(1)); - if (!type) + auto type = decodeMangledType(node->getChild(1)); + if (type.isError()) return true; - results.emplace_back(type, *convention); + results.emplace_back(type.getType(), *convention); return false; } @@ -968,8 +1017,8 @@ class TypeDecoder { auto convention = T::getConventionFromString(conventionString); if (!convention) return true; - BuiltType type = decodeMangledType(typeNode); - if (!type) + auto result = decodeMangledType(typeNode); + if (result.isError()) return true; auto diffKind = T::DifferentiabilityType::DifferentiableOrNotApplicable; @@ -984,14 +1033,13 @@ class TypeDecoder { diffKind = *optDiffKind; } - results.emplace_back(type, *convention, diffKind); + results.emplace_back(result.getType(), *convention, diffKind); return false; } - bool decodeMangledTypeDecl(Demangle::NodePointer node, - BuiltTypeDecl &typeDecl, - BuiltType &parent, - bool &typeAlias) { + llvm::Optional + decodeMangledTypeDecl(Demangle::NodePointer node, BuiltTypeDecl &typeDecl, + BuiltType &parent, bool &typeAlias) { if (node->getKind() == NodeKind::Type) return decodeMangledTypeDecl(node->getChild(0), typeDecl, parent, typeAlias); @@ -1002,7 +1050,9 @@ class TypeDecoder { declNode = node; } else { if (node->getNumChildren() < 2) - return false; + return MAKE_NODE_TYPE_ERROR( + node, "Number of node children (%u) less than required (2)", + node->getNumChildren()); auto parentContext = node->getChild(0); @@ -1018,11 +1068,14 @@ class TypeDecoder { case Node::Kind::Extension: // Decode the type being extended. if (parentContext->getNumChildren() < 2) - return false; + return MAKE_NODE_TYPE_ERROR(parentContext, + "Number of parentContext children (%u) " + "less than required (2)", + node->getNumChildren()); parentContext = parentContext->getChild(1); LLVM_FALLTHROUGH; default: - parent = decodeMangledType(parentContext); + parent = decodeMangledType(parentContext).getType(); // Remove any generic arguments from the context node, producing a // node that references the nominal type declaration. declNode = Demangle::getUnspecialized(node, Builder.getNodeFactory()); @@ -1030,9 +1083,10 @@ class TypeDecoder { } } typeDecl = Builder.createTypeDecl(declNode, typeAlias); - if (!typeDecl) return false; + if (!typeDecl) + return TypeLookupError("Failed to create type decl"); - return true; + return llvm::None; } BuiltProtocolDecl decodeMangledProtocolType(Demangle::NodePointer node) { @@ -1097,10 +1151,10 @@ class TypeDecoder { } auto paramType = decodeMangledType(node); - if (!paramType) + if (paramType.isError()) return false; - param.setType(paramType); + param.setType(paramType.getType()); return true; }; @@ -1158,14 +1212,12 @@ class TypeDecoder { } }; -template -inline typename BuilderType::BuiltType -decodeMangledType(BuilderType &Builder, - NodePointer Node) { +template +inline TypeLookupErrorOr +decodeMangledType(BuilderType &Builder, NodePointer Node) { return TypeDecoder(Builder).decodeMangledType(Node); } - SWIFT_END_INLINE_NAMESPACE } // end namespace Demangle } // end namespace swift diff --git a/include/swift/Demangling/TypeLookupError.h b/include/swift/Demangling/TypeLookupError.h new file mode 100644 index 0000000000000..c67fc35598be8 --- /dev/null +++ b/include/swift/Demangling/TypeLookupError.h @@ -0,0 +1,198 @@ +//===--- TypeLookupError.h - Type lookup error value. -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// +// +// Provides the TypeLookupError class, which represents errors when demangling +// or looking up types. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DEMANGLING_TypeLookupError_H +#define SWIFT_DEMANGLING_TypeLookupError_H + +#include "swift/Basic/TaggedUnion.h" +#include "swift/Runtime/Portability.h" + +namespace swift { + +/// An error that occurred while looking up a type at runtime from a mangled +/// name. +/// /// +/// This ultimately just provides a string, but is built to take up minimal +/// space when passed around, and perform as much work lazily as possible. We +/// don't want to spend a lot of time building strings when the caller is going +/// to handle the error gracefully and try a fallback. We only want to waste +/// time/space on that when the error message is actually relevant, so build it +/// as late as possible. +/// /// +/// To be as compact as possible, this type holds a context pointer and a +/// callback function. The callback function uses the context pointer to return +/// an error string when requested. The callback function also does quadruple +/// duty to copy/destroy the context as needed, and free the returned error +/// string if needed. Commands are passed to the callback to request the +/// different operations, which means we only have to store one function pointer +/// instead of four. +class TypeLookupError { +public: + /// The commands that can be passed to the callback function. + enum class Command { + /// Return the error string to the caller, as a char *. + CopyErrorString, + + /// Destroy the error string returned from CopyErrorString, if necessary. + /// The return value is ignored. + DestroyErrorString, + + /// Return a copy of the context pointer (used for copying TypeLookupError + /// objects.) + CopyContext, + + /// Destroy the context pointer. The return value is ignored. + DestroyContext, + }; + + /// The callback used to respond to the commands. The parameters are: + /// - `context`: the context that the value was initialized with, or the + /// context returned from a CopyContext call + /// - `command`: the command to respond to + /// - `param`: when `command` is `DestroyErrorString`, the string pointer to + /// destroy, otherwise NULL + using Callback = void *(*)(void *context, Command command, void *param); + +private: + void *Context; + Callback Fn; + + /// A no-op callback used to avoid a bunch of `if (Fn)` checks. + static void *nop(void *context, Command command, void *param) { + return nullptr; + } + + /// Helper functions for getting a C string from a lambda. These allow us to + /// wrap lambdas returning `char *` or `std::string` and standardize them on + /// `char *`. + static char *getCString(char *str) { return str; } + + static char *getCString(const std::string &str) { + return strdup(str.c_str()); + } + +public: + TypeLookupError(const TypeLookupError &other) { + Fn = other.Fn; + Context = other.Fn(other.Context, Command::CopyContext, nullptr); + } + + TypeLookupError(TypeLookupError &&other) { + Fn = other.Fn; + Context = other.Context; + + other.Fn = nop; + other.Context = nullptr; + } + + ~TypeLookupError() { Fn(Context, Command::DestroyContext, nullptr); } + + TypeLookupError(void *context, Callback fn) : Context(context), Fn(fn ? fn : nop) {} + + TypeLookupError &operator=(const TypeLookupError &other) { + if (this == &other) + return *this; + + Fn(Context, Command::DestroyContext, nullptr); + Fn = other.Fn; + Context = Fn(Context, Command::CopyContext, nullptr); + + return *this; + } + + /// Construct a TypeLookupError that just returns a constant C string. + TypeLookupError(const char *str) + : TypeLookupError([=] { return const_cast(str); }) {} + + /// Construct a TypeLookupError that creates a string using asprintf. The passed-in + /// format string and arguments are passed directly to swift_asprintf when + /// the string is requested. The arguments are captured and the string is only + /// formatted when needed. + template + TypeLookupError(const char *fmt, Args... args) + : TypeLookupError([=] { + char *str; + swift_asprintf(&str, fmt, args...); + return str; + }) {} + + /// Construct a TypeLookupError that wraps a function returning a string. The + /// passed-in function can return either a `std::string` or `char *`. If it + /// returns `char *` then the string will be destroyed with `free()`. + template TypeLookupError(const F &fn) { + Context = new F(fn); + Fn = [](void *context, Command command, void *param) -> void * { + auto castContext = reinterpret_cast(context); + switch (command) { + case Command::CopyErrorString: { + return TypeLookupError::getCString((*castContext)()); + } + case Command::DestroyErrorString: + free(param); + return nullptr; + case Command::CopyContext: + return new F(*castContext); + case Command::DestroyContext: + delete castContext; + return nullptr; + } + }; + } + + /// Get the error string from the error value. The value must be passed to + /// `freeErrorString` when done. (Unless you're just calling a `fatalError` + /// in which case there's no point.) + char *copyErrorString() { + return reinterpret_cast( + Fn(Context, Command::CopyErrorString, nullptr)); + } + + /// Free an error string previously obtained from `copyErrorString`. + void freeErrorString(char *str) { + Fn(Context, Command::DestroyErrorString, str); + } +}; + +/// A value that's either a `TypeLookupError` or some parameterized type value `T`. A +/// convenience wrapper around `TaggedUnion`. +template class TypeLookupErrorOr { + TaggedUnion Value; + +public: + TypeLookupErrorOr(const T &t) : Value(t) { + if (!t) + Value = TypeLookupError("unknown error"); + } + + TypeLookupErrorOr(const TypeLookupError &te) : Value(te) {} + + T getType() { + if (auto *ptr = Value.template dyn_cast()) + return *ptr; + return T(); + } + + TypeLookupError *getError() { + return Value.template dyn_cast(); + } + + bool isError() { return getError() != nullptr; } +}; + +} // namespace swift + +#endif // SWIFT_DEMANGLING_TypeLookupError_H diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index f375918a5ff5d..3914810cbfd8f 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -618,8 +618,8 @@ class TypeRefBuilder { }), OpaqueUnderlyingTypeReader( [&reader](uint64_t descriptorAddr, unsigned ordinal) -> const TypeRef* { - return reader.readUnderlyingTypeForOpaqueTypeDescriptor(descriptorAddr, - ordinal); + return reader.readUnderlyingTypeForOpaqueTypeDescriptor( + descriptorAddr, ordinal).getType(); }) {} diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 4450066283afc..0090ae0746ed6 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -463,7 +463,8 @@ class MetadataReader { } /// Given a demangle tree, attempt to turn it into a type. - BuiltType decodeMangledType(NodePointer Node) { + TypeLookupErrorOr + decodeMangledType(NodePointer Node) { return swift::Demangle::decodeMangledType(Builder, Node); } @@ -925,8 +926,8 @@ class MetadataReader { swift_runtime_unreachable("Unhandled MetadataKind in switch"); } - BuiltType readTypeFromMangledName(const char *MangledTypeName, - size_t Length) { + TypeLookupErrorOr + readTypeFromMangledName(const char *MangledTypeName, size_t Length) { Demangle::Demangler Dem; Demangle::NodePointer Demangled = Dem.demangleSymbol(StringRef(MangledTypeName, Length)); @@ -1183,14 +1184,14 @@ class MetadataReader { MangledNameKind::Type, Dem); } - BuiltType + TypeLookupErrorOr readUnderlyingTypeForOpaqueTypeDescriptor(StoredPointer contextAddr, unsigned ordinal) { Demangle::Demangler Dem; auto node = readUnderlyingTypeManglingForOpaqueTypeDescriptor(contextAddr, ordinal, Dem); if (!node) - return BuiltType(); + return TypeLookupError("Failed to read type mangling for descriptor."); return decodeMangledType(node); } diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 86e19642e51a7..f14e8d16f1554 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -21,7 +21,6 @@ #include "swift/Runtime/Unreachable.h" #include #include -#include #include #include @@ -248,39 +247,6 @@ std::atomic _swift_debug_metadataAllocationBacktraceList; SWIFT_RUNTIME_STDLIB_SPI const void * const _swift_debug_protocolConformanceStatePointer; -SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE -inline static int swift_asprintf(char **strp, const char *fmt, ...) { - va_list args; - va_start(args, fmt); -#if defined(_WIN32) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" - int len = _vscprintf(fmt, args); -#pragma GCC diagnostic pop - if (len < 0) { - va_end(args); - return -1; - } - char *buffer = static_cast(malloc(len + 1)); - if (!buffer) { - va_end(args); - return -1; - } - int result = vsprintf(buffer, fmt, args); - if (result < 0) { - va_end(args); - free(buffer); - return -1; - } - *strp = buffer; -#else - int result = vasprintf(strp, fmt, args); -#endif - va_end(args); - return result; -} - - // namespace swift } diff --git a/include/swift/Runtime/Portability.h b/include/swift/Runtime/Portability.h index 9e4fc418e162f..cd19b2c4193ba 100644 --- a/include/swift/Runtime/Portability.h +++ b/include/swift/Runtime/Portability.h @@ -16,8 +16,47 @@ #ifndef SWIFT_RUNTIME_PORTABILITY_H #define SWIFT_RUNTIME_PORTABILITY_H + +#include #include +#include +#include size_t _swift_strlcpy(char *dst, const char *src, size_t maxlen); +// Skip the attribute when included by the compiler. +#ifdef SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE +SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE +#endif +inline static int swift_asprintf(char **strp, const char *fmt, ...) { + va_list args; + va_start(args, fmt); +#if defined(_WIN32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + int len = _vscprintf(fmt, args); +#pragma GCC diagnostic pop + if (len < 0) { + va_end(args); + return -1; + } + char *buffer = static_cast(malloc(len + 1)); + if (!buffer) { + va_end(args); + return -1; + } + int result = vsprintf(buffer, fmt, args); + if (result < 0) { + va_end(args); + free(buffer); + return -1; + } + *strp = buffer; +#else + int result = vasprintf(strp, fmt, args); +#endif + va_end(args); + return result; +} + #endif diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 6e44fd70da0ef..b613d94f17a7d 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -44,7 +44,7 @@ Type swift::Demangle::getTypeForMangling(ASTContext &ctx, return Type(); ASTBuilder builder(ctx); - return swift::Demangle::decodeMangledType(builder, node); + return swift::Demangle::decodeMangledType(builder, node).getType(); } TypeDecl *swift::Demangle::getTypeDeclForMangling(ASTContext &ctx, @@ -847,8 +847,8 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( if (child->getNumChildren() != 2) return CanGenericSignature(); - auto subjectType = swift::Demangle::decodeMangledType( - *this, child->getChild(0)); + auto subjectType = + swift::Demangle::decodeMangledType(*this, child->getChild(0)).getType(); if (!subjectType) return CanGenericSignature(); @@ -857,8 +857,9 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( Demangle::Node::Kind::DependentGenericConformanceRequirement || child->getKind() == Demangle::Node::Kind::DependentGenericSameTypeRequirement) { - constraintType = swift::Demangle::decodeMangledType( - *this, child->getChild(1)); + constraintType = + swift::Demangle::decodeMangledType(*this, child->getChild(1)) + .getType(); if (!constraintType) return CanGenericSignature(); } diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 686a8421f642d..0f937e95cec77 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -632,9 +632,10 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl { SubstitutionMap substitutions, unsigned ordinal) override { auto underlyingType = Reader - .readUnderlyingTypeForOpaqueTypeDescriptor(opaqueDescriptor.getAddressData(), - ordinal); - + .readUnderlyingTypeForOpaqueTypeDescriptor( + opaqueDescriptor.getAddressData(), ordinal) + .getType(); + if (!underlyingType) return getFailure(); diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index 772afeb1e98f2..d811f53d89036 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -135,7 +135,8 @@ lookupTypeWitness(const std::string &MangledTypeName, auto SubstitutedTypeName = readTypeRef(AssocTy, AssocTy->SubstitutedTypeName); auto Demangled = demangleTypeRef(SubstitutedTypeName); - auto *TypeWitness = swift::Demangle::decodeMangledType(*this, Demangled); + auto *TypeWitness = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); AssociatedTypeCache.insert(std::make_pair(key, TypeWitness)); return TypeWitness; @@ -155,7 +156,8 @@ lookupSuperclass(const TypeRef *TR) { return nullptr; auto Demangled = demangleTypeRef(readTypeRef(FD, FD->Superclass)); - auto Unsubstituted = swift::Demangle::decodeMangledType(*this, Demangled); + auto Unsubstituted = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); if (!Unsubstituted) return nullptr; @@ -226,7 +228,8 @@ bool TypeRefBuilder::getFieldTypeRefs( } auto Demangled = demangleTypeRef(readTypeRef(Field,Field->MangledTypeName)); - auto Unsubstituted = swift::Demangle::decodeMangledType(*this, Demangled); + auto Unsubstituted = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); if (!Unsubstituted) return false; @@ -304,7 +307,7 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { if (CR->hasMangledTypeName()) { auto MangledName = readTypeRef(CR, CR->MangledTypeName); auto DemangleTree = demangleTypeRef(MangledName); - TR = swift::Demangle::decodeMangledType(*this, DemangleTree); + TR = swift::Demangle::decodeMangledType(*this, DemangleTree).getType(); } Info.CaptureTypes.push_back(TR); } @@ -316,7 +319,7 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { if (MSR->hasMangledTypeName()) { auto MangledName = readTypeRef(MSR, MSR->MangledTypeName); auto DemangleTree = demangleTypeRef(MangledName); - TR = swift::Demangle::decodeMangledType(*this, DemangleTree); + TR = swift::Demangle::decodeMangledType(*this, DemangleTree).getType(); } const MetadataSource *MS = nullptr; @@ -344,12 +347,17 @@ TypeRefBuilder::dumpTypeRef(RemoteRef MangledName, auto DemangleTree = demangleTypeRef(MangledName); auto TypeName = nodeToString(DemangleTree); fprintf(file, "%s\n", TypeName.c_str()); - auto TR = swift::Demangle::decodeMangledType(*this, DemangleTree); - if (!TR) { + auto Result = swift::Demangle::decodeMangledType(*this, DemangleTree); + if (Result.isError()) { + auto *Error = Result.getError(); + char *ErrorStr = Error->copyErrorString(); auto str = getTypeRefString(MangledName); - fprintf(file, "!!! Invalid typeref: %s\n", std::string(str.begin(), str.end()).c_str()); + fprintf(file, "!!! Invalid typeref: %s - %s\n", + std::string(str.begin(), str.end()).c_str(), ErrorStr); + Error->freeErrorString(ErrorStr); return; } + auto TR = Result.getType(); TR->dump(file); fprintf(file, "\n"); } diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 114cf058512d0..eccfd625425a5 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -298,7 +298,7 @@ swift_reflection_typeRefForMangledTypeName(SwiftReflectionContextRef ContextRef, const char *MangledTypeName, uint64_t Length) { auto Context = ContextRef->nativeContext; - auto TR = Context->readTypeFromMangledName(MangledTypeName, Length); + auto TR = Context->readTypeFromMangledName(MangledTypeName, Length).getType(); return reinterpret_cast(TR); } diff --git a/stdlib/public/runtime/CompatibilityOverride.def b/stdlib/public/runtime/CompatibilityOverride.def index 92e32430251cd..de154bb44d50f 100644 --- a/stdlib/public/runtime/CompatibilityOverride.def +++ b/stdlib/public/runtime/CompatibilityOverride.def @@ -138,7 +138,7 @@ OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::, (const void *pattern, const void *arguments), (pattern, arguments)) -OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift::, +OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeLookupErrorOr, , SWIFT_CC(swift), swift::, (MetadataRequest request, Demangler &demangler, Demangle::NodePointer node, @@ -146,7 +146,7 @@ OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift SubstGenericParameterFn substGenericParam, SubstDependentWitnessTableFn substWitnessTable), (request, demangler, node, arguments, substGenericParam, substWitnessTable)) -OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeInfo, , SWIFT_CC(swift), swift::, +OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeLookupErrorOr, , SWIFT_CC(swift), swift::, (MetadataRequest request, StringRef typeName, const void * const *arguments, diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 1dfe45dd8c62f..a8e9f2a1b81eb 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2916,24 +2916,22 @@ getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self) { StringRef superclassName = Demangle::makeSymbolicMangledNameStringRef(superclassNameBase); SubstGenericParametersFromMetadata substitutions(self); - MetadataResponse response = - swift_getTypeByMangledName(request, superclassName, - substitutions.getGenericArgs(), + auto result = swift_getTypeByMangledName( + request, superclassName, substitutions.getGenericArgs(), [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getResponse(); - auto superclass = response.Value; - if (!superclass) { - fatalError(0, - "failed to demangle superclass of %s from mangled name '%s'\n", - self->getDescription()->Name.get(), - superclassName.str().c_str()); + }); + if (auto *error = result.getError()) { + fatalError( + 0, "failed to demangle superclass of %s from mangled name '%s': %s\n", + self->getDescription()->Name.get(), superclassName.str().c_str(), + error->copyErrorString()); } - return response; + return result.getType().getResponse(); } else { return MetadataResponse(); } @@ -4928,12 +4926,12 @@ swift_getAssociatedTypeWitnessSlowImpl( Demangle::makeSymbolicMangledNameStringRef(mangledNameBase); // Demangle the associated type. - MetadataResponse response; + TypeLookupErrorOr result = TypeInfo(); if (inProtocolContext) { // The protocol's Self is the only generic parameter that can occur in the // type. - response = - swift_getTypeByMangledName(request, mangledName, nullptr, + result = swift_getTypeByMangledName( + request, mangledName, nullptr, [conformingType](unsigned depth, unsigned index) -> const Metadata * { if (depth == 0 && index == 0) return conformingType; @@ -4950,7 +4948,7 @@ swift_getAssociatedTypeWitnessSlowImpl( return swift_getAssociatedConformanceWitness(wtable, conformingType, type, reqBase, dependentDescriptor); - }).getResponse(); + }); } else { // The generic parameters in the associated type name are those of the // conforming type. @@ -4960,29 +4958,30 @@ swift_getAssociatedTypeWitnessSlowImpl( auto originalConformingType = findConformingSuperclass(conformingType, conformance); SubstGenericParametersFromMetadata substitutions(originalConformingType); - response = swift_getTypeByMangledName(request, mangledName, - substitutions.getGenericArgs(), - [&substitutions](unsigned depth, unsigned index) { - return substitutions.getMetadata(depth, index); - }, - [&substitutions](const Metadata *type, unsigned index) { - return substitutions.getWitnessTable(type, index); - }).getResponse(); + result = swift_getTypeByMangledName( + request, mangledName, substitutions.getGenericArgs(), + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }); } + auto *error = result.getError(); + MetadataResponse response = result.getType().getResponse(); auto assocTypeMetadata = response.Value; - - if (!assocTypeMetadata) { + if (error || !assocTypeMetadata) { + const char *errStr = error ? error->copyErrorString() + : "NULL metadata but no error was provided"; auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); StringRef conformingTypeName(conformingTypeNameInfo.data, conformingTypeNameInfo.length); StringRef assocTypeName = findAssociatedTypeName(protocol, assocType); fatalError(0, "failed to demangle witness for associated type '%s' in " - "conformance '%s: %s' from mangled name '%s'\n", - assocTypeName.str().c_str(), - conformingTypeName.str().c_str(), - protocol->Name.get(), - mangledName.str().c_str()); + "conformance '%s: %s' from mangled name '%s' - %s\n", + assocTypeName.str().c_str(), conformingTypeName.str().c_str(), + protocol->Name.get(), mangledName.str().c_str(), errStr); } assert((uintptr_t(assocTypeMetadata) & @@ -5935,7 +5934,7 @@ void swift::verifyMangledNameRoundtrip(const Metadata *metadata) { nullptr, [](unsigned, unsigned){ return nullptr; }, [](const Metadata *, unsigned) { return nullptr; }) - .getMetadata(); + .getType().getMetadata(); if (metadata != result) swift::warning(RuntimeErrorFlagNone, "Metadata mangled name failed to roundtrip: %p -> %s -> %p\n", diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 93ed4227928eb..c0c2f7c0c4c9d 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -961,13 +961,54 @@ getLocalGenericParams(const ContextDescriptor *context) { return genericContext->getGenericParams().slice(startParamIndex); } -static bool +static llvm::Optional _gatherGenericParameters(const ContextDescriptor *context, llvm::ArrayRef genericArgs, const Metadata *parent, llvm::SmallVectorImpl &genericParamCounts, llvm::SmallVectorImpl &allGenericArgsVec, Demangler &demangler) { + auto makeCommonErrorStringGetter = [&] { + auto metadataVector = genericArgs.vec(); + return [=] { + std::string str; + + str += "_gatherGenericParameters: context: "; + + SymbolInfo contextInfo; + if (lookupSymbol(context, &contextInfo)) { + str += contextInfo.symbolName.get(); + str += " "; + } + + char *contextStr; + swift_asprintf(&contextStr, "%p", context); + str += contextStr; + free(contextStr); + + str += " <"; + + bool first = true; + for (const Metadata *metadata : genericArgs) { + if (!first) + str += ", "; + first = false; + str += nameForMetadata(metadata); + } + + str += "> "; + + str += "parent: "; + if (parent) + str += nameForMetadata(parent); + else + str += ""; + str += " - "; + + return str; + }; + }; + // Figure out the various levels of generic parameters we have in // this type. (void)_gatherGenericParameterCounts(context, @@ -981,7 +1022,15 @@ _gatherGenericParameters(const ContextDescriptor *context, } else if (genericArgs.size() == numTotalGenericParams && !parent) { // Okay: genericArgs is the complete set of generic arguments. } else { - return false; + auto commonString = makeCommonErrorStringGetter(); + auto genericArgsSize = genericArgs.size(); + return TypeLookupError([=] { + return commonString() + "incorrect number of generic args (" + + std::to_string(genericArgsSize) + "), " + + std::to_string(getLocalGenericParams(context).size()) + + " local params, " + std::to_string(numTotalGenericParams) + + " total params"; + }); } // If there are generic parameters at any level, check the generic @@ -1008,15 +1057,30 @@ _gatherGenericParameters(const ContextDescriptor *context, auto genericParams = generics->getGenericParams(); unsigned n = genericParams.size(); if (allGenericArgs.size() != n) { - return false; + auto commonString = makeCommonErrorStringGetter(); + auto argsVecSize = allGenericArgsVec.size(); + return TypeLookupError([=] { + return commonString() + "have " + std::to_string(argsVecSize) + + "generic args, expected " + std::to_string(n); + }); } for (unsigned i = 0; i != n; ++i) { const auto ¶m = genericParams[i]; - if (param.getKind() != GenericParamKind::Type) - return false; - if (param.hasExtraArgument()) - return false; - + if (param.getKind() != GenericParamKind::Type) { + auto commonString = makeCommonErrorStringGetter(); + return TypeLookupError([=] { + return commonString() + "param " + std::to_string(i) + + " has unexpected kind " + + std::to_string(static_cast(param.getKind())); + }); + } + if (param.hasExtraArgument()) { + auto commonString = makeCommonErrorStringGetter(); + return TypeLookupError([=] { + return commonString() + "param " + std::to_string(i) + + "has extra argument"; + }); + } if (param.hasKeyArgument()) allGenericArgsVec.push_back(allGenericArgs[i]); } @@ -1028,26 +1092,33 @@ _gatherGenericParameters(const ContextDescriptor *context, // any extra arguments we need for the instantiation function. SubstGenericParametersFromWrittenArgs substitutions(allGenericArgs, genericParamCounts); - bool failed = - _checkGenericRequirements(generics->getGenericRequirements(), - allGenericArgsVec, + auto error = _checkGenericRequirements( + generics->getGenericRequirements(), allGenericArgsVec, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); - if (failed) - return false; + if (error) + return *error; // If we still have the wrong number of generic arguments, this is // some kind of metadata mismatch. if (generics->getGenericContextHeader().getNumArguments() != - allGenericArgsVec.size()) - return false; + allGenericArgsVec.size()) { + auto commonString = makeCommonErrorStringGetter(); + auto argsVecSize = allGenericArgsVec.size(); + return TypeLookupError([=] { + return commonString() + "generic argument count mismatch, expected " + + std::to_string( + generics->getGenericContextHeader().getNumArguments()) + + ", have " + std::to_string(argsVecSize); + }); + } } - return true; + return llvm::None; } namespace { @@ -1175,7 +1246,7 @@ class DecodedMetadataBuilder { Demangle::NodeFactory &getNodeFactory() { return demangler; } - BuiltType + TypeLookupErrorOr resolveOpaqueType(NodePointer opaqueDecl, llvm::ArrayRef> genericArgs, unsigned ordinal) { @@ -1193,12 +1264,10 @@ class DecodedMetadataBuilder { llvm::SmallVector genericParamCounts; llvm::SmallVector allGenericArgsVec; - if (!_gatherGenericParameters(outerContext, - allGenericArgs, - BuiltType(), /* no parent */ - genericParamCounts, allGenericArgsVec, - demangler)) - return BuiltType(); + if (auto error = _gatherGenericParameters( + outerContext, allGenericArgs, BuiltType(), /* no parent */ + genericParamCounts, allGenericArgsVec, demangler)) + return *error; auto mangledName = descriptor->getUnderlyingTypeArgument(ordinal); SubstGenericParametersFromMetadata substitutions(descriptor, @@ -1210,7 +1279,7 @@ class DecodedMetadataBuilder { }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } BuiltTypeDecl createTypeDecl(NodePointer node, @@ -1245,8 +1314,9 @@ class DecodedMetadataBuilder { return ProtocolDescriptorRef(); #endif } - - BuiltType createObjCClassType(const std::string &mangledName) const { + + TypeLookupErrorOr + createObjCClassType(const std::string &mangledName) const { #if SWIFT_OBJC_INTEROP auto objcClass = objc_getClass(mangledName.c_str()); return swift_getObjCClassMetadata((const ClassMetadata *)objcClass); @@ -1255,7 +1325,7 @@ class DecodedMetadataBuilder { #endif } - BuiltType + TypeLookupErrorOr createBoundGenericObjCClassType(const std::string &mangledName, llvm::ArrayRef args) const { // Generic arguments of lightweight Objective-C generic classes are not @@ -1263,24 +1333,25 @@ class DecodedMetadataBuilder { return createObjCClassType(mangledName); } - BuiltType createNominalType(BuiltTypeDecl metadataOrTypeDecl, - BuiltType parent) const { + TypeLookupErrorOr + createNominalType(BuiltTypeDecl metadataOrTypeDecl, BuiltType parent) const { // Treat nominal type creation the same way as generic type creation, // but with no generic arguments at this level. return createBoundGenericType(metadataOrTypeDecl, { }, parent); } - BuiltType createTypeAliasType(BuiltTypeDecl typeAliasDecl, - BuiltType parent) const { + TypeLookupErrorOr createTypeAliasType(BuiltTypeDecl typeAliasDecl, + BuiltType parent) const { // We can't support sugared types here since we have no way to // resolve the underlying type of the type alias. However, some // CF types are mangled as type aliases. return createNominalType(typeAliasDecl, parent); } - BuiltType createBoundGenericType(BuiltTypeDecl anyTypeDecl, - llvm::ArrayRef genericArgs, - BuiltType parent) const { + TypeLookupErrorOr + createBoundGenericType(BuiltTypeDecl anyTypeDecl, + llvm::ArrayRef genericArgs, + BuiltType parent) const { auto typeDecl = dyn_cast(anyTypeDecl); if (!typeDecl) { if (auto protocol = dyn_cast(anyTypeDecl)) @@ -1294,13 +1365,11 @@ class DecodedMetadataBuilder { llvm::SmallVector genericParamCounts; llvm::SmallVector allGenericArgsVec; - if (!_gatherGenericParameters(typeDecl, - genericArgs, - parent, - genericParamCounts, allGenericArgsVec, - demangler)) - return BuiltType(); - + if (auto error = _gatherGenericParameters(typeDecl, genericArgs, parent, + genericParamCounts, + allGenericArgsVec, demangler)) + return *error; + // Call the access function. auto accessFunction = typeDecl->getAccessFunction(); if (!accessFunction) return BuiltType(); @@ -1308,8 +1377,8 @@ class DecodedMetadataBuilder { return accessFunction(MetadataState::Abstract, allGenericArgsVec).Value; } - BuiltType createBuiltinType(StringRef builtinName, - StringRef mangledName) const { + TypeLookupErrorOr createBuiltinType(StringRef builtinName, + StringRef mangledName) const { #define BUILTIN_TYPE(Symbol, _) \ if (mangledName.equals(#Symbol)) \ return &METADATA_SYM(Symbol).base; @@ -1317,19 +1386,19 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType createMetatypeType( + TypeLookupErrorOr createMetatypeType( BuiltType instance, llvm::Optional repr = None) const { return swift_getMetatypeMetadata(instance); } - BuiltType createExistentialMetatypeType( + TypeLookupErrorOr createExistentialMetatypeType( BuiltType instance, llvm::Optional repr = None) const { return swift_getExistentialMetatypeMetadata(instance); } - BuiltType + TypeLookupErrorOr createProtocolCompositionType(llvm::ArrayRef protocols, BuiltType superclass, bool isClassBound) const { // Determine whether we have a class bound. @@ -1349,13 +1418,13 @@ class DecodedMetadataBuilder { protocols.size(), protocols.data()); } - BuiltType createDynamicSelfType(BuiltType selfType) const { + TypeLookupErrorOr createDynamicSelfType(BuiltType selfType) const { // Free-standing mangled type strings should not contain DynamicSelfType. return BuiltType(); } - BuiltType createGenericTypeParameterType(unsigned depth, - unsigned index) const { + TypeLookupErrorOr + createGenericTypeParameterType(unsigned depth, unsigned index) const { // Use the callback, when provided. if (substGenericParameter) return substGenericParameter(depth, index); @@ -1363,7 +1432,7 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType + TypeLookupErrorOr createFunctionType(llvm::ArrayRef> params, BuiltType result, FunctionTypeFlags flags) const { llvm::SmallVector paramTypes; @@ -1386,7 +1455,7 @@ class DecodedMetadataBuilder { result); } - BuiltType createImplFunctionType( + TypeLookupErrorOr createImplFunctionType( Demangle::ImplParameterConvention calleeConvention, llvm::ArrayRef> params, llvm::ArrayRef> results, @@ -1396,8 +1465,9 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType createTupleType(llvm::ArrayRef elements, - std::string labels) const { + TypeLookupErrorOr + createTupleType(llvm::ArrayRef elements, + std::string labels) const { auto flags = TupleTypeFlags().withNumElements(elements.size()); if (!labels.empty()) flags = flags.withNonConstantLabels(true); @@ -1408,13 +1478,15 @@ class DecodedMetadataBuilder { .Value; } - BuiltType createDependentMemberType(StringRef name, BuiltType base) const { + TypeLookupErrorOr createDependentMemberType(StringRef name, + BuiltType base) const { // Should not have unresolved dependent member types here. return BuiltType(); } - BuiltType createDependentMemberType(StringRef name, BuiltType base, - BuiltProtocolDecl protocol) const { + TypeLookupErrorOr + createDependentMemberType(StringRef name, BuiltType base, + BuiltProtocolDecl protocol) const { #if SWIFT_OBJC_INTEROP if (protocol.isObjC()) return BuiltType(); @@ -1438,14 +1510,14 @@ class DecodedMetadataBuilder { *assocType).Value; } -#define REF_STORAGE(Name, ...) \ - BuiltType create##Name##StorageType(BuiltType base) { \ - ReferenceOwnership.set##Name(); \ - return base; \ +#define REF_STORAGE(Name, ...) \ + TypeLookupErrorOr create##Name##StorageType(BuiltType base) { \ + ReferenceOwnership.set##Name(); \ + return base; \ } #include "swift/AST/ReferenceStorage.def" - BuiltType createSILBoxType(BuiltType base) const { + TypeLookupErrorOr createSILBoxType(BuiltType base) const { // FIXME: Implement. return BuiltType(); } @@ -1454,22 +1526,23 @@ class DecodedMetadataBuilder { return ReferenceOwnership; } - BuiltType createOptionalType(BuiltType base) { + TypeLookupErrorOr createOptionalType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createArrayType(BuiltType base) { + TypeLookupErrorOr createArrayType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createDictionaryType(BuiltType key, BuiltType value) { + TypeLookupErrorOr createDictionaryType(BuiltType key, + BuiltType value) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createParenType(BuiltType base) { + TypeLookupErrorOr createParenType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } @@ -1478,13 +1551,12 @@ class DecodedMetadataBuilder { } SWIFT_CC(swift) -static TypeInfo swift_getTypeByMangledNodeImpl( - MetadataRequest request, - Demangler &demangler, - Demangle::NodePointer node, - const void * const *origArgumentVector, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +static TypeLookupErrorOr +swift_getTypeByMangledNodeImpl(MetadataRequest request, Demangler &demangler, + Demangle::NodePointer node, + const void *const *origArgumentVector, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { // Simply call an accessor function if that's all we got. if (node->getKind() == Node::Kind::AccessorFunctionReference) { // The accessor function is passed the pointer to the original argument @@ -1504,22 +1576,23 @@ static TypeInfo swift_getTypeByMangledNodeImpl( DecodedMetadataBuilder builder(demangler, substGenericParam, substWitnessTable); auto type = Demangle::decodeMangledType(builder, node); - if (!type) { - return {MetadataResponse{nullptr, MetadataState::Complete}, - TypeReferenceOwnership()}; + if (type.isError()) { + return *type.getError(); + } + if (!type.getType()) { + return TypeLookupError("NULL type but no error provided"); } - return {swift_checkMetadataState(request, type), - builder.getReferenceOwnership()}; + return TypeInfo{swift_checkMetadataState(request, type.getType()), + builder.getReferenceOwnership()}; } SWIFT_CC(swift) -static TypeInfo swift_getTypeByMangledNameImpl( - MetadataRequest request, - StringRef typeName, - const void * const *origArgumentVector, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +static TypeLookupErrorOr +swift_getTypeByMangledNameImpl(MetadataRequest request, StringRef typeName, + const void *const *origArgumentVector, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { DemanglerForRuntimeTypeResolution> demangler; NodePointer node; @@ -1587,7 +1660,7 @@ swift_getTypeByMangledNameInEnvironment( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1607,7 +1680,7 @@ swift_getTypeByMangledNameInEnvironmentInMetadataState( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1626,7 +1699,7 @@ swift_getTypeByMangledNameInContext( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1646,7 +1719,7 @@ swift_getTypeByMangledNameInContextInMetadataState( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } /// Demangle a mangled name, but don't allow symbolic references. @@ -1661,7 +1734,7 @@ swift_stdlib_getTypeByMangledNameUntrusted(const char *typeNameStart, } return swift_getTypeByMangledName(MetadataState::Complete, typeName, nullptr, - {}, {}).getMetadata(); + {}, {}).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1680,7 +1753,7 @@ swift_getOpaqueTypeMetadata(MetadataRequest request, }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getResponse(); + }).getType().getResponse(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1735,7 +1808,7 @@ getObjCClassByMangledName(const char * _Nonnull typeName, }, [&](const Metadata *type, unsigned index) { return nullptr; - }).getMetadata(); + }).getType().getMetadata(); } else { metadata = swift_stdlib_getTypeByMangledNameUntrusted(typeStr.data(), typeStr.size()); @@ -2068,7 +2141,7 @@ void swift::gatherWrittenGenericArgs( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); continue; } diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index c46e8ff9a9b38..2f5e7232cc55d 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -17,7 +17,10 @@ #ifndef SWIFT_RUNTIME_PRIVATE_H #define SWIFT_RUNTIME_PRIVATE_H +#include + #include "swift/Demangling/Demangler.h" +#include "swift/Demangling/TypeLookupError.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Metadata.h" @@ -77,6 +80,8 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } + operator bool() const { return getMetadata(); } + #define REF_STORAGE(Name, ...) \ bool is##Name() const { return ReferenceOwnership.is##Name(); } #include "swift/AST/ReferenceStorage.def" @@ -369,7 +374,7 @@ class TypeInfo { /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. SWIFT_CC(swift) - TypeInfo swift_getTypeByMangledNode( + TypeLookupErrorOr swift_getTypeByMangledNode( MetadataRequest request, Demangler &demangler, Demangle::NodePointer node, @@ -384,7 +389,7 @@ class TypeInfo { /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. SWIFT_CC(swift) - TypeInfo swift_getTypeByMangledName( + TypeLookupErrorOr swift_getTypeByMangledName( MetadataRequest request, StringRef typeName, const void * const *arguments, @@ -447,12 +452,12 @@ class TypeInfo { /// generic requirements (e.g., those that need to be /// passed to an instantiation function) will be added to this vector. /// - /// \returns true if an error occurred, false otherwise. - bool _checkGenericRequirements( - llvm::ArrayRef requirements, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable); + /// \returns the error if an error occurred, None otherwise. + llvm::Optional _checkGenericRequirements( + llvm::ArrayRef requirements, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable); /// A helper function which avoids performing a store if the destination /// address already contains the source value. This is useful when diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index e373d2a50848e..c434011faea85 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -165,15 +165,16 @@ ProtocolConformanceDescriptor::getWitnessTable(const Metadata *type) const { llvm::SmallVector conditionalArgs; if (hasConditionalRequirements()) { SubstGenericParametersFromMetadata substitutions(type); - bool failed = - _checkGenericRequirements(getConditionalRequirements(), conditionalArgs, + auto error = _checkGenericRequirements( + getConditionalRequirements(), conditionalArgs, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); - if (failed) return nullptr; + if (error) + return nullptr; } return swift_getWitnessTable(this, type, conditionalArgs.data()); @@ -642,31 +643,36 @@ static bool isSubclass(const Metadata *subclass, const Metadata *superclass) { }); } -bool swift::_checkGenericRequirements( - llvm::ArrayRef requirements, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +llvm::Optional swift::_checkGenericRequirements( + llvm::ArrayRef requirements, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { for (const auto &req : requirements) { // Make sure we understand the requirement we're dealing with. - if (!req.hasKnownKind()) return true; + if (!req.hasKnownKind()) + return TypeLookupError("unknown kind"); // Resolve the subject generic parameter. - const Metadata *subjectType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getParam(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!subjectType) - return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getParam(), extraArguments.data(), + substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + const Metadata *subjectType = result.getType().getMetadata(); // Check the requirement. switch (req.getKind()) { case GenericRequirementKind::Protocol: { const WitnessTable *witnessTable = nullptr; if (!_conformsToProtocol(nullptr, subjectType, req.getProtocol(), - &witnessTable)) - return true; + &witnessTable)) { + const char *protoName = + req.getProtocol() ? req.getProtocol().getName() : ""; + return TypeLookupError( + "subject type %s does not conform to protocol %s", req.getParam(), + protoName); + } // If we need a witness table, add it. if (req.getProtocol().needsWitnessTable()) { @@ -679,17 +685,19 @@ bool swift::_checkGenericRequirements( case GenericRequirementKind::SameType: { // Demangle the second type under the given substitutions. - auto otherType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getMangledTypeName(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!otherType) return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto otherType = result.getType().getMetadata(); assert(!req.getFlags().hasExtraArgument()); // Check that the types are equivalent. - if (subjectType != otherType) return true; + if (subjectType != otherType) + return TypeLookupError("subject type %s does not match %s", + req.getParam(), req.getMangledTypeName()); continue; } @@ -698,22 +706,24 @@ bool swift::_checkGenericRequirements( switch (req.getLayout()) { case GenericRequirementLayoutKind::Class: if (!subjectType->satisfiesClassConstraint()) - return true; + return TypeLookupError( + "subject type %s does not satisfy class constraint", + req.getParam()); continue; } // Unknown layout. - return true; + return TypeLookupError("unknown layout kind %u", req.getLayout()); } case GenericRequirementKind::BaseClass: { // Demangle the base type under the given substitutions. - auto baseType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getMangledTypeName(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!baseType) return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto baseType = result.getType().getMetadata(); // If the type which is constrained to a base class is an existential // type, and if that existential type includes a superclass constraint, @@ -725,7 +735,8 @@ bool swift::_checkGenericRequirements( } if (!isSubclass(subjectType, baseType)) - return true; + return TypeLookupError("%s is not subclass of %s", req.getParam(), + req.getMangledTypeName()); continue; } @@ -737,11 +748,12 @@ bool swift::_checkGenericRequirements( } // Unknown generic requirement kind. - return true; + return TypeLookupError("unknown generic requirement kind %u", + req.getKind()); } // Success! - return false; + return llvm::None; } const Metadata *swift::findConformingSuperclass( diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 821fecfb7fb0b..0206237d2b44b 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -400,27 +400,33 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { auto typeName = field.getMangledTypeName(); SubstGenericParametersFromMetadata substitutions(base); - auto typeInfo = swift_getTypeByMangledName(MetadataState::Complete, - typeName, - substitutions.getGenericArgs(), - [&substitutions](unsigned depth, unsigned index) { - return substitutions.getMetadata(depth, index); - }, - [&substitutions](const Metadata *type, unsigned index) { - return substitutions.getWitnessTable(type, index); - }); + auto result = swift_getTypeByMangledName( + MetadataState::Complete, typeName, substitutions.getGenericArgs(), + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }); // If demangling the type failed, pretend it's an empty type instead with // a log message. - if (!typeInfo.getMetadata()) { + TypeInfo typeInfo; + if (result.isError()) { typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING), MetadataState::Complete}, {}); + + auto *error = result.getError(); + char *str = error->copyErrorString(); missing_reflection_metadata_warning( - "warning: the Swift runtime was unable to demangle the type " - "of field '%*s'. the mangled type name is '%*s'. this field will " - "show up as an empty tuple in Mirrors\n", - (int)name.size(), name.data(), - (int)typeName.size(), typeName.data()); + "warning: the Swift runtime was unable to demangle the type " + "of field '%*s'. the mangled type name is '%*s': %s. this field will " + "show up as an empty tuple in Mirrors\n", + (int)name.size(), name.data(), (int)typeName.size(), typeName.data(), + str); + error->freeErrorString(str); + } else { + typeInfo = result.getType(); } auto fieldType = FieldType(typeInfo.getMetadata()); diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index f377a77d6f106..ba33dd19abd69 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -26,11 +26,13 @@ #include "llvm/ADT/StringRef.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Casting.h" +#include "swift/Runtime/Debug.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/ObjCBridge.h" +#include "swift/Runtime/Portability.h" #include "swift/Strings.h" #include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/AssertionReporting.h" @@ -39,7 +41,6 @@ #include "Private.h" #include "SwiftObject.h" #include "WeakReference.h" -#include "swift/Runtime/Debug.h" #if SWIFT_OBJC_INTEROP #include #endif diff --git a/stdlib/public/stubs/Assert.cpp b/stdlib/public/stubs/Assert.cpp index 62135b2312b1c..fa0169a5ad6b8 100644 --- a/stdlib/public/stubs/Assert.cpp +++ b/stdlib/public/stubs/Assert.cpp @@ -12,6 +12,7 @@ #include "swift/Runtime/Config.h" #include "swift/Runtime/Debug.h" +#include "swift/Runtime/Portability.h" #include "../SwiftShims/AssertionReporting.h" #include #include diff --git a/tools/swift-reflection-dump/swift-reflection-dump.cpp b/tools/swift-reflection-dump/swift-reflection-dump.cpp index c06ec35de5f81..8846f4452e855 100644 --- a/tools/swift-reflection-dump/swift-reflection-dump.cpp +++ b/tools/swift-reflection-dump/swift-reflection-dump.cpp @@ -668,12 +668,15 @@ static int doDumpReflectionSections(ArrayRef BinaryFilenames, Demangle::Demangler Dem; auto Demangled = Dem.demangleType(Line); - auto *TypeRef = - swift::Demangle::decodeMangledType(builder, Demangled); - if (TypeRef == nullptr) { - fprintf(file, "Invalid typeref:%s\n", Line.c_str()); + auto Result = swift::Demangle::decodeMangledType(builder, Demangled); + if (Result.isError()) { + auto *error = Result.getError(); + char *str = error->copyErrorString(); + fprintf(file, "Invalid typeref:%s - %s\n", Line.c_str(), str); + error->freeErrorString(str); continue; } + auto TypeRef = Result.getType(); TypeRef->dump(file); auto *TypeInfo = builder.getTypeConverter().getTypeInfo(TypeRef); diff --git a/unittests/runtime/CompatibilityOverride.cpp b/unittests/runtime/CompatibilityOverride.cpp index 80a9b086980b9..6056f26117930 100644 --- a/unittests/runtime/CompatibilityOverride.cpp +++ b/unittests/runtime/CompatibilityOverride.cpp @@ -35,8 +35,8 @@ namespace { return MetadataResponse{nullptr, MetadataState::Complete}; } - template<> - TypeInfo getEmptyValue() { + template <> + TypeLookupErrorOr getEmptyValue>() { return TypeInfo(); } } @@ -172,13 +172,13 @@ TEST_F(CompatibilityOverrideTest, test_swift_getTypeByMangledNode) { Demangler demangler; auto Result = swift_getTypeByMangledNode(MetadataState::Abstract, demangler, nullptr, nullptr, nullptr,nullptr); - ASSERT_EQ(Result.getMetadata(), nullptr); + ASSERT_EQ(Result.getType().getMetadata(), nullptr); } TEST_F(CompatibilityOverrideTest, test_swift_getTypeByMangledName) { auto Result = swift_getTypeByMangledName(MetadataState::Abstract, "", nullptr, nullptr, nullptr); - ASSERT_EQ(Result.getMetadata(), nullptr); + ASSERT_EQ(Result.getType().getMetadata(), nullptr); } TEST_F(CompatibilityOverrideTest, test_swift_getAssociatedTypeWitnessSlow) { From 6ad2757bef212510785eef8a9755d09140b54978 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 28 Aug 2020 13:58:02 -0700 Subject: [PATCH 22/47] [Concurrency] Use completion/completionHandler parameter names for async import Extend the check for completion handler parameters to also consider the name of the parameter (not its argument label). If it's `completion` or `completionHandler`, we have a completion handler. This extends our API coverage for importing Objective-C methods with completion handlers as 'async'. --- lib/ClangImporter/ImportName.cpp | 33 ++++++++++++------- test/ClangImporter/objc_async.swift | 2 ++ test/IDE/print_clang_objc_async.swift | 1 + .../usr/include/ObjCConcurrency.h | 2 ++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 791c695ff1b91..fc0313bbc91df 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1213,10 +1213,13 @@ NameImporter::considerAsyncImport( // handler. Optional newBaseName; if (isCompletionHandlerParamName(paramNames[completionHandlerParamNameIndex])) { - // The parameter itself has an appropriate name. + // The argument label itself has an appropriate name. } else if (!hasCustomName && completionHandlerParamIndex == 0 && (newBaseName = isCompletionHandlerInBaseName(baseName))) { // The base name implies that the first parameter is a completion handler. + } else if (isCompletionHandlerParamName( + params[completionHandlerParamIndex]->getName())) { + // The parameter has an appropriate name. } else { return None; } @@ -1238,6 +1241,10 @@ NameImporter::considerAsyncImport( if (isInitializer) return notAsync("initializers cannot be async"); + // Accessors are never imported as async. + if (clangDecl->isPropertyAccessor()) + return notAsync("method is a property accessor"); + // Check whether we method has a suitable return type. if (clangDecl->getReturnType()->isVoidType()) { // 'void' is the common case; the method produces no synchronous result. @@ -1515,7 +1522,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, else if (parsedName.IsSetter) result.info.accessorKind = ImportedAccessorKind::PropertySetter; - if (method && parsedName.IsFunctionName) { + if (method && parsedName.IsFunctionName && + result.info.accessorKind == ImportedAccessorKind::None) { // Get the parameters. ArrayRef params{method->param_begin(), method->param_end()}; @@ -1787,16 +1795,6 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.errorInfo = *errorInfo; } - if (version.supportsConcurrency()) { - if (auto asyncInfo = considerAsyncImport( - objcMethod, baseName, argumentNames, params, isInitializer, - /*hasCustomName=*/false, - result.getErrorInfo())) { - result.info.hasAsyncInfo = true; - result.info.asyncInfo = *asyncInfo; - } - } - isFunction = true; // Is this one of the accessors for subscripts? @@ -1814,6 +1812,17 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.accessorKind = ImportedAccessorKind::SubscriptSetter; } + if (version.supportsConcurrency() && + result.info.accessorKind == ImportedAccessorKind::None) { + if (auto asyncInfo = considerAsyncImport( + objcMethod, baseName, argumentNames, params, isInitializer, + /*hasCustomName=*/false, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + } + } + break; } } diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 8ec33ae643905..15b149a89c5dd 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -11,6 +11,8 @@ func testSlowServer(slowServer: SlowServer) async { let _: String = await slowServer.findAnswerFailingly() ?? "nope" // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} + let _: Void = await slowServer.doSomethingFun("jump") + let _: (Int) -> Void = slowServer.completionHandler } func testSlowServerOldSchool(slowServer: SlowServer) { diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index 6e4539de8825c..7d3029fff8be6 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -16,4 +16,5 @@ // CHECK-DAG: func findAnswer() async throws -> String? // CHECK-DAG: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws // CHECK-DAG: func findAnswerFailingly() async throws -> String? +// CHECK-DAG: func doSomethingFun(_ operation: String) async // CHECK: {{^[}]$}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index 9903bd0a91a64..b03e3530a4968 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -9,6 +9,8 @@ -(void)checkAvailabilityWithCompletionHandler:(void (^)(BOOL isAvailable))completionHandler; -(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); +-(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; +@property(readwrite) void (^completionHandler)(NSInteger); @end #pragma clang assume_nonnull end From c4f05052da564c5f47552af02cb6c185af8b5149 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Fri, 14 Aug 2020 14:58:42 -0700 Subject: [PATCH 23/47] [Parse] Don't drop throws containing a code completion expression. --- lib/Parse/ParseStmt.cpp | 7 ++++--- test/IDE/complete_exception.swift | 11 +++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index e39f0daf1846b..1a95fc1585f46 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -898,9 +898,7 @@ ParserResult Parser::parseStmtThrow(SourceLoc tryLoc) { exprLoc = Tok.getLoc(); ParserResult Result = parseExpr(diag::expected_expr_throw); - - if (Result.hasCodeCompletion()) - return makeParserCodeCompletionResult(); + bool hasCodeCompletion = Result.hasCodeCompletion(); if (Result.isNull()) Result = makeParserErrorResult(new (Context) ErrorExpr(throwLoc)); @@ -916,6 +914,9 @@ ParserResult Parser::parseStmtThrow(SourceLoc tryLoc) { Result = makeParserResult(new (Context) TryExpr(exprLoc, Result.get())); } + if (hasCodeCompletion) + Result.setHasCodeCompletion(); + return makeParserResult(Result, new (Context) ThrowStmt(throwLoc, Result.get())); } diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index 48d222ced7565..0a52f94e2928d 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -5,11 +5,13 @@ // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH2 | %FileCheck %s -check-prefix=CATCH2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW2 | %FileCheck %s -check-prefix=THROW2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH3 | %FileCheck %s -check-prefix=CATCH3 +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW3 | %FileCheck %s -check-prefix=THROW3 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH1 | %FileCheck %s -check-prefix=CATCH1 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW1 | %FileCheck %s -check-prefix=THROW1 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH2 | %FileCheck %s -check-prefix=CATCH2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW2 | %FileCheck %s -check-prefix=THROW2 +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW3 | %FileCheck %s -check-prefix=THROW3 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH1 > %t.inside_catch1 // RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch1 @@ -126,11 +128,20 @@ func test005() { // CATCH3: End completions } +func testInvalid() { + try throw Error4.#^THROW3^# +// THROW3: Begin completions +// THROW3: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}} +// THROW3: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}} +// THROW3: End completions +} + //===--- Top-level throw/catch do {} catch #^TOP_LEVEL_CATCH1^# {} throw #^TOP_LEVEL_THROW1^# do {} catch Error4.#^TOP_LEVEL_CATCH2^# {} throw Error4.#^TOP_LEVEL_THROW2^# +try throw Error4.#^TOP_LEVEL_THROW3^# //===--- Inside catch body From 00994f114768917a8037920e99352d872f2aa2b1 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Thu, 27 Aug 2020 14:34:56 -0700 Subject: [PATCH 24/47] [Parse] Stop early exiting when parsing catch statements and ObjCSelector expressions that contain code completions. --- lib/Parse/ParseExpr.cpp | 4 +--- lib/Parse/ParseStmt.cpp | 3 --- test/IDE/complete_exception.swift | 8 ++++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1d3a63a826d11..474cfb2cade44 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -802,8 +802,6 @@ ParserResult Parser::parseExprSelector() { parseExpr(selectorKind == ObjCSelectorExpr::Method ? diag::expr_selector_expected_method_expr : diag::expr_selector_expected_property_expr); - if (subExpr.hasCodeCompletion()) - return makeParserCodeCompletionResult(); // Parse the closing ')'. SourceLoc rParenLoc; @@ -819,7 +817,7 @@ ParserResult Parser::parseExprSelector() { } // If the subexpression was in error, just propagate the error. - if (subExpr.isParseError()) + if (subExpr.isParseError() && !subExpr.hasCodeCompletion()) return makeParserResult( new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc))); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 1a95fc1585f46..c4a64ca54973e 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2013,9 +2013,6 @@ ParserResult Parser::parseStmtCatch() { GuardedPattern PatternResult; parseGuardedPattern(*this, PatternResult, status, boundDecls, GuardedPatternContext::Catch, isFirst); - if (status.hasCodeCompletion()) { - return makeParserCodeCompletionResult(); - } caseLabelItems.emplace_back(PatternResult.ThePattern, PatternResult.WhereLoc, PatternResult.Guard); isFirst = false; diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index 0a52f94e2928d..a5cef6bb5175b 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -72,10 +72,10 @@ func test001() { do {} catch #^CATCH1^# // CATCH1: Begin completions -// CATCH1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}} +// CATCH1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}} // CATCH1-DAG: Keyword[let]/None: let{{; name=.+$}} // CATCH1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}} // CATCH1-DAG: Decl[Class]/OtherModule[Foundation]/IsSystem: NSError[#NSError#]{{; name=.+$}} From 3e0500d73c1f1efbbc166e22228fd589da19695d Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Mon, 24 Aug 2020 11:39:47 -0700 Subject: [PATCH 25/47] [Parse][IDE] Don't drop default argument init exprs containing code completion exprs and type check them for code completion. Fixes up some tests marked as non-ideal to give the ideal result now too. --- lib/IDE/ExprContextAnalysis.cpp | 5 +++++ lib/Parse/ParsePattern.cpp | 4 +++- test/IDE/complete_unresolved_members.swift | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 787606e321891..ad3a32fb7ada2 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -85,6 +85,11 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { typeCheckPatternBinding(PBD, i); } } + } else if (auto *defaultArg = dyn_cast(DC)) { + if (auto *AFD = dyn_cast(defaultArg->getParent())) { + auto *Param = AFD->getParameters()->get(defaultArg->getIndex()); + (void*)Param->getTypeCheckedDefaultExpr(); + } } break; diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index e26a0dd1cd238..c98ea0c6f04da 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -130,8 +130,10 @@ static ParserStatus parseDefaultArgument( defaultArgs->HasDefaultArgument = true; - if (initR.hasCodeCompletion()) + if (initR.hasCodeCompletion()) { + init = initR.get(); return makeParserCodeCompletionStatus(); + } if (initR.isNull()) return makeParserError(); diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 7a895c550c665..804d9cd5bcba9 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -109,9 +109,9 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_21 | %FileCheck %s -check-prefix=GENERICPARAM_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DECL_MEMBER_INIT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPEPARAM_IN_CONTEXTTYPE_1 | %FileCheck %s -check-prefix=TYPEPARAM_IN_CONTEXTTYPE_1 From 89803560f9a1d11d438b06392a9938648aa3be62 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Tue, 25 Aug 2020 21:28:38 -0700 Subject: [PATCH 26/47] [Parse] Perform the single expression function body transform for delayed parsing as well We were previously only doing it when parsing up front. --- include/swift/Parse/Parser.h | 3 +- lib/Parse/ParseDecl.cpp | 129 ++++++++++++++++++----------------- lib/Parse/Parser.cpp | 2 +- 3 files changed, 68 insertions(+), 66 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 723fdcd956a22..7f1b3ddba9249 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1133,8 +1133,7 @@ class Parser { ParseDeclOptions Flags, DeclAttributes &Attributes, bool HasFuncKeyword = true); - ParserResult - parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); + BraceStmt *parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); void parseAbstractFunctionBody(AbstractFunctionDecl *AFD); BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD); ParserResult parseDeclProtocol(ParseDeclOptions Flags, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index aa3cf8ce5cad1..ac374bee7e53b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6442,10 +6442,9 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, return DCC.fixupParserResult(FD); } -/// Parse a function body for \p AFD and returns it without setting the body -/// to \p AFD . -ParserResult -Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { +/// Parse a function body for \p AFD, setting the body to \p AFD before +/// returning it. +BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { assert(Tok.is(tok::l_brace)); // Enter the arguments for the function into a new function-body scope. We @@ -6473,13 +6472,70 @@ Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { CodeCompletion->completeAccessorBeginning(CCE); RBraceLoc = Tok.getLoc(); consumeToken(tok::code_complete); - return makeParserCodeCompletionResult( - BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc, - /*implicit*/ true)); + auto *BS = BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc, + /*implicit*/ true); + AFD->setBodyParsed(BS); + return BS; } } - return parseBraceItemList(diag::invalid_diagnostic); + ParserResult Body = parseBraceItemList(diag::invalid_diagnostic); + if (Body.isNull()) + return nullptr; + + BraceStmt *BS = Body.get(); + AFD->setBodyParsed(BS); + + // If the body consists of a single expression, turn it into a return + // statement. + // + // But don't do this transformation during code completion, as the source + // may be incomplete and the type mismatch in return statement will just + // confuse the type checker. + if (BS->getNumElements() != 1 || Body.hasCodeCompletion()) + return BS; + + auto Element = BS->getFirstElement(); + if (auto *stmt = Element.dyn_cast()) { + if (isa(AFD)) { + if (auto *returnStmt = dyn_cast(stmt)) { + if (!returnStmt->hasResult()) { + auto returnExpr = TupleExpr::createEmpty(Context, + SourceLoc(), + SourceLoc(), + /*implicit*/true); + returnStmt->setResult(returnExpr); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(returnExpr); + } + } + } + } else if (auto *E = Element.dyn_cast()) { + if (auto SE = dyn_cast(E->getSemanticsProvidingExpr())) { + if (SE->getNumElements() > 1 && isa(SE->getElement(1))) { + // This is an assignment. We don't want to implicitly return + // it. + return BS; + } + } + if (isa(AFD)) { + auto RS = new (Context) ReturnStmt(SourceLoc(), E); + BS->setFirstElement(RS); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(E); + } else if (auto *F = dyn_cast(AFD)) { + if (F->isFailable() && isa(E)) { + // If it's a nil literal, just insert return. This is the only + // legal thing to return. + auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); + BS->setFirstElement(RS); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(E); + } + } + } + + return BS; } /// Parse function body into \p AFD or skip it for delayed parsing. @@ -6504,60 +6560,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { } Scope S(this, ScopeKind::FunctionBody); - - ParserResult Body = parseAbstractFunctionBodyImpl(AFD); - if (!Body.isNull()) { - BraceStmt * BS = Body.get(); - AFD->setBodyParsed(BS); - - // If the body consists of a single expression, turn it into a return - // statement. - // - // But don't do this transformation during code completion, as the source - // may be incomplete and the type mismatch in return statement will just - // confuse the type checker. - if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) { - auto Element = BS->getFirstElement(); - if (auto *stmt = Element.dyn_cast()) { - if (isa(AFD)) { - if (auto *returnStmt = dyn_cast(stmt)) { - if (!returnStmt->hasResult()) { - auto returnExpr = TupleExpr::createEmpty(Context, - SourceLoc(), - SourceLoc(), - /*implicit*/true); - returnStmt->setResult(returnExpr); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(returnExpr); - } - } - } - } else if (auto *E = Element.dyn_cast()) { - if (auto SE = dyn_cast(E->getSemanticsProvidingExpr())) { - if (SE->getNumElements() > 1 && isa(SE->getElement(1))) { - // This is an assignment. We don't want to implicitly return - // it. - return; - } - } - if (isa(AFD)) { - auto RS = new (Context) ReturnStmt(SourceLoc(), E); - BS->setFirstElement(RS); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(E); - } else if (auto *F = dyn_cast(AFD)) { - if (F->isFailable() && isa(E)) { - // If it's a nil literal, just insert return. This is the only - // legal thing to return. - auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); - BS->setFirstElement(RS); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(E); - } - } - } - } - } + (void)parseAbstractFunctionBodyImpl(AFD); } BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { @@ -6589,7 +6592,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { Scope TopLevelScope(this, ScopeKind::TopLevel); Scope S(this, ScopeKind::FunctionBody); - return parseAbstractFunctionBodyImpl(AFD).getPtrOrNull(); + return parseAbstractFunctionBodyImpl(AFD); } /// Parse a 'enum' declaration, returning true (and doing no token diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 7b56a1f629368..25161aabaa9ee 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -203,7 +203,7 @@ void Parser::performCodeCompletionSecondPassImpl( case CodeCompletionDelayedDeclKind::FunctionBody: { auto *AFD = cast(DC); - AFD->setBodyParsed(parseAbstractFunctionBodyImpl(AFD).getPtrOrNull()); + (void)parseAbstractFunctionBodyImpl(AFD); break; } } From c03d76291cfa03676286b01807944f793e9d8a0c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 28 Aug 2020 17:26:52 -0700 Subject: [PATCH 27/47] [ConstraintSystem] NFC: Clarify why upward propagation of literal conformances is ncessary at the moment This is NFC because only literal protocols are tracked at the moment. Forward propagate (subtype -> supertype) only literal conformance requirements since that helps solver to infer more types at parameter positions. ```swift func foo(_: String, _: T) -> T { fatalError() } func bar(_: Any?) {} func test() { bar(foo("", "")) } ``` If one of the literal arguments doesn't propagate its `ExpressibleByStringLiteral` conformance, we'd end up picking `T` with only one type `Any?` which is incorrect. This is not going to be necessary once bindings are filtered based of requirements placed on a type variable. --- lib/Sema/CSBindings.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a82b389991788..90344be7b8154 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -56,8 +56,33 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings( auto &bindings = relatedBindings->getSecond(); - // Infer transitive protocol requirements. - llvm::copy(bindings.Protocols, std::back_inserter(Protocols)); + // FIXME: This is a workaround necessary because solver doesn't filter + // bindings based on protocol requirements placed on a type variable. + // + // Forward propagate (subtype -> supertype) only literal conformance + // requirements since that helps solver to infer more types at + // parameter positions. + // + // \code + // func foo(_: String, _: T) -> T { + // fatalError() + // } + // + // func bar(_: Any?) {} + // + // func test() { + // bar(foo("", "")) + // } + // \endcode + // + // If one of the literal arguments doesn't propagate its + // `ExpressibleByStringLiteral` conformance, we'd end up picking + // `T` with only one type `Any?` which is incorrect. + llvm::copy_if(bindings.Protocols, std::back_inserter(Protocols), + [](const Constraint *protocol) { + return protocol->getKind() == + ConstraintKind::LiteralConformsTo; + }); // Infer transitive defaults. llvm::copy(bindings.Defaults, std::back_inserter(Defaults)); From ebc03a1805eb9e2c8c6b6315dae10872095c49f3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 28 Aug 2020 17:39:21 -0700 Subject: [PATCH 28/47] [CSBindings] NFC: Remove dead condition leftover from Swift version 3 support --- lib/Sema/CSBindings.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 90344be7b8154..5f324d7579e64 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -931,15 +931,7 @@ bool ConstraintSystem::PotentialBindings::infer( case ConstraintKind::ConformsTo: case ConstraintKind::SelfObjectOfProtocol: - // Swift 3 allowed the use of default types for normal conformances - // to expressible-by-literal protocols. - if (cs.getASTContext().LangOpts.EffectiveLanguageVersion[0] >= 4) - return false; - - if (!constraint->getSecondType()->is()) - return false; - - LLVM_FALLTHROUGH; + return false; case ConstraintKind::LiteralConformsTo: { // Record constraint where protocol requirement originated From 172c4be02d3c582235cf256103bbb3d7af914a79 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Sat, 29 Aug 2020 02:38:42 +0100 Subject: [PATCH 29/47] [Sema] Diagnose use of ambiguous property wrappers (#33688) --- include/swift/AST/DiagnosticsCommon.def | 14 ++++++ include/swift/AST/DiagnosticsParse.def | 2 - lib/AST/NameLookup.cpp | 47 ++++++++++++++++++++ lib/Sema/TypeCheckAttr.cpp | 11 ----- test/NameLookup/Inputs/custom_attrs_A.swift | 13 ++++++ test/NameLookup/Inputs/custom_attrs_B.swift | 13 ++++++ test/NameLookup/custom_attrs_ambiguous.swift | 21 +++++++++ 7 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 test/NameLookup/Inputs/custom_attrs_A.swift create mode 100644 test/NameLookup/Inputs/custom_attrs_B.swift create mode 100644 test/NameLookup/custom_attrs_ambiguous.swift diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index f4c39ce036d06..72d8d64bbecb7 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -188,5 +188,19 @@ ERROR(scanner_find_cycle, none, ERROR(scanner_arguments_invalid, none, "dependencies scanner cannot be configured with arguments: '%0'", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: custom attribute diagnostics +//------------------------------------------------------------------------------ + +ERROR(ambiguous_custom_attribute_ref,none, + "ambiguous use of attribute %0", (Identifier)) +NOTE(ambiguous_custom_attribute_ref_fix,none, + "use '%0.' to reference the attribute %1 in module %2", + (StringRef, Identifier, Identifier)) +NOTE(found_attribute_candidate,none, + "found this attribute", ()) +ERROR(unknown_attribute,none, + "unknown attribute '%0'", (StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6c7f4e6533b04..3a278eb7dea14 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1324,8 +1324,6 @@ ERROR(replace_equal_with_colon_for_value,none, "'=' has been replaced with ':' in attribute arguments", ()) ERROR(expected_attribute_name,none, "expected an attribute name", ()) -ERROR(unknown_attribute,none, - "unknown attribute '%0'", (StringRef)) ERROR(unexpected_lparen_in_attribute,none, "unexpected '(' in attribute '%0'", (StringRef)) ERROR(duplicate_attribute,none, diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index b581137926415..7ef21cadcb205 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2434,6 +2434,53 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, } } + // If we have more than one attribute declaration, we have an ambiguity. + // So, emit an ambiguity diagnostic. + if (auto typeRepr = attr->getTypeRepr()) { + if (nominals.size() > 1) { + SmallVector ambiguousCandidates; + // Filter out declarations that cannot be attributes. + for (auto decl : nominals) { + if (isa(decl)) { + continue; + } + ambiguousCandidates.push_back(decl); + } + if (ambiguousCandidates.size() > 1) { + auto attrName = nominals.front()->getName(); + ctx.Diags.diagnose(typeRepr->getLoc(), + diag::ambiguous_custom_attribute_ref, attrName); + for (auto candidate : ambiguousCandidates) { + ctx.Diags.diagnose(candidate->getLoc(), + diag::found_attribute_candidate); + // If the candidate is a top-level attribute, let's suggest + // adding module name to resolve the ambiguity. + if (candidate->getDeclContext()->isModuleScopeContext()) { + auto moduleName = candidate->getParentModule()->getName(); + ctx.Diags + .diagnose(typeRepr->getLoc(), + diag::ambiguous_custom_attribute_ref_fix, + moduleName.str(), attrName, moduleName) + .fixItInsert(typeRepr->getLoc(), moduleName.str().str() + "."); + } + } + return nullptr; + } + } + } + + // There is no nominal type with this name, so complain about this being + // an unknown attribute. + std::string typeName; + if (auto typeRepr = attr->getTypeRepr()) { + llvm::raw_string_ostream out(typeName); + typeRepr->print(out); + } else { + typeName = attr->getType().getString(); + } + + ctx.Diags.diagnose(attr->getLocation(), diag::unknown_attribute, typeName); + return nullptr; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 94478abf4f50f..df4080e85a1ba 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2901,18 +2901,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { auto nominal = evaluateOrDefault( Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); - // If there is no nominal type with this name, complain about this being - // an unknown attribute. if (!nominal) { - std::string typeName; - if (auto typeRepr = attr->getTypeRepr()) { - llvm::raw_string_ostream out(typeName); - typeRepr->print(out); - } else { - typeName = attr->getType().getString(); - } - - diagnose(attr->getLocation(), diag::unknown_attribute, typeName); attr->setInvalid(); return; } diff --git a/test/NameLookup/Inputs/custom_attrs_A.swift b/test/NameLookup/Inputs/custom_attrs_A.swift new file mode 100644 index 0000000000000..c8035b4fa167e --- /dev/null +++ b/test/NameLookup/Inputs/custom_attrs_A.swift @@ -0,0 +1,13 @@ +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +@_functionBuilder +public struct Builder { + static func buildBlock(_ component: T) -> T { component } +} diff --git a/test/NameLookup/Inputs/custom_attrs_B.swift b/test/NameLookup/Inputs/custom_attrs_B.swift new file mode 100644 index 0000000000000..c8035b4fa167e --- /dev/null +++ b/test/NameLookup/Inputs/custom_attrs_B.swift @@ -0,0 +1,13 @@ +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +@_functionBuilder +public struct Builder { + static func buildBlock(_ component: T) -> T { component } +} diff --git a/test/NameLookup/custom_attrs_ambiguous.swift b/test/NameLookup/custom_attrs_ambiguous.swift new file mode 100644 index 0000000000000..b549e34e273ba --- /dev/null +++ b/test/NameLookup/custom_attrs_ambiguous.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_A.swift +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_B.swift +// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t %s +import custom_attrs_A +import custom_attrs_B + +// SR-13470 + +struct Test { + @Wrapper var x: Int = 17 + // expected-error@-1 {{ambiguous use of attribute 'Wrapper'}} + // expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Wrapper' in module 'custom_attrs_A'}} {{4-4=custom_attrs_A.}} + // expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Wrapper' in module 'custom_attrs_B'}} {{4-4=custom_attrs_B.}} + + init(@Builder closure: () -> Int) {} + // expected-error@-1 {{ambiguous use of attribute 'Builder'}} + // expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Builder' in module 'custom_attrs_A'}} {{9-9=custom_attrs_A.}} + // expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Builder' in module 'custom_attrs_B'}} {{9-9=custom_attrs_B.}} +} + From 7a6f84dc644607114237d85571d5a747c13bca71 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Fri, 28 Aug 2020 20:50:20 -0700 Subject: [PATCH 30/47] When building standalone stdlib, explicitly use C/C++ compilers from NATIVE_CLANG_TOOLS_PATH (#33675) --- utils/build-script-impl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/build-script-impl b/utils/build-script-impl index 1e80510fa7ad2..4c4d499371b9a 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1407,6 +1407,13 @@ for host in "${ALL_HOSTS[@]}"; do fi fi + if [[ "${NATIVE_CLANG_TOOLS_PATH}" ]] ; then + common_cmake_options_host+=( + -DCMAKE_C_COMPILER="${NATIVE_CLANG_TOOLS_PATH}/clang" + -DCMAKE_CXX_COMPILER="${NATIVE_CLANG_TOOLS_PATH}/clang++" + ) + fi + llvm_cmake_options=( "${llvm_cmake_options[@]}" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" From 73f427369fc7ec1c7e13ae91aabf0a0829e3d22e Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Fri, 28 Aug 2020 20:50:47 -0700 Subject: [PATCH 31/47] Mark Reflection_jit.swift as UNSUPPORTED for standalone stdlib builds (#33682) --- test/stdlib/Reflection_jit.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/stdlib/Reflection_jit.swift b/test/stdlib/Reflection_jit.swift index c36b6b6e8cc9f..8704ba6069ceb 100644 --- a/test/stdlib/Reflection_jit.swift +++ b/test/stdlib/Reflection_jit.swift @@ -2,3 +2,8 @@ // RUN: %target-jit-run -parse-stdlib %S/Reflection.swift -- %S/Inputs/shuffle.jpg | %FileCheck %S/Reflection.swift // REQUIRES: swift_interpreter + +// Only run this test when we build host tools (e.g. bin/swift-frontend), avoid running it for standalone stdlib builds. +// Standalone stdlib builds use downloadable toolchains from swift.org, which are codesigned in a way that doesn't let +// the interpreter process (swift-frontend) dynamically load locally-built modules (libswiftCore). +// REQUIRES: swift_tools_extra From dbd16d2e05108bc922ab87fc40f2a0b7891ea5e9 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 28 Aug 2020 18:56:17 -0700 Subject: [PATCH 32/47] swift_build_sdk_interfaces.py: adjust output directory path to an SDK-versioned directory When clients specify an output directory without SDK version, the script will change the input so we emit all prebuilt module caches into SDK-versioned directories without modifying the client side. rdar://67951012 --- utils/swift_build_sdk_interfaces.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/utils/swift_build_sdk_interfaces.py b/utils/swift_build_sdk_interfaces.py index 900bae30064e0..c8381193a9bd4 100755 --- a/utils/swift_build_sdk_interfaces.py +++ b/utils/swift_build_sdk_interfaces.py @@ -354,6 +354,14 @@ def process_module_files(pool, module_files): return overall_exit_status +def getSDKVersion(sdkroot): + settingPath = os.path.join(sdkroot, 'SDKSettings.json') + with open(settingPath) as json_file: + data = json.load(json_file) + return data['Version'] + fatal("Failed to get SDK version from: " + settingPath) + + def main(): global args, shared_output_lock parser = create_parser() @@ -373,6 +381,12 @@ def main(): if not os.path.isdir(args.sdk): fatal("invalid SDK: " + args.sdk) + # if the given output dir ends with 'prebuilt-modules', we should + # append the SDK version number so all modules will built into + # the SDK-versioned sub-directory. + if os.path.basename(args.output_dir) == 'prebuilt-modules': + args.output_dir = os.path.join(args.output_dir, getSDKVersion(args.sdk)) + xfails = () if args.ignore_non_stdlib_failures: if args.xfails: From 198687018cbeb52e225c5acac7c72661c12dee80 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Sat, 29 Aug 2020 08:06:09 -0700 Subject: [PATCH 33/47] [IDE] Fix typo to address unused warning (NFC) --- lib/IDE/ExprContextAnalysis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 96f0b2126ce09..904f42a949f24 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -104,7 +104,7 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { } else if (auto *defaultArg = dyn_cast(DC)) { if (auto *AFD = dyn_cast(defaultArg->getParent())) { auto *Param = AFD->getParameters()->get(defaultArg->getIndex()); - (void*)Param->getTypeCheckedDefaultExpr(); + (void)Param->getTypeCheckedDefaultExpr(); } } break; From c34212aa20019141f4bccd0f2cb7fc0ffe217303 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 28 Aug 2020 20:00:21 -0700 Subject: [PATCH 34/47] Turn on Verbose Output For Test --- validation-test/BuildSystem/skip-local-build.test-sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation-test/BuildSystem/skip-local-build.test-sh b/validation-test/BuildSystem/skip-local-build.test-sh index b658957c70a85..36657e3d6fa37 100644 --- a/validation-test/BuildSystem/skip-local-build.test-sh +++ b/validation-test/BuildSystem/skip-local-build.test-sh @@ -1,8 +1,8 @@ # REQUIRES: standalone_build # -# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG +# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG --dump-input=always # CONFIG: "skip_local_build": true -# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY +# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY --dump-input=always # DRY: build-script-impl # DRY-SAME: --skip-local-build From a412e48065dece9681451127504d20ccdc2065d7 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 29 Aug 2020 11:09:54 -0700 Subject: [PATCH 35/47] Revert "Add A Reproducer For rdar://48443680" This reverts commit 57f2944aad1d08d9bbd530a8534e52895c783eb1. --- .../ClangImporter/ClangImporterTests.cpp | 107 +----------------- 1 file changed, 1 insertion(+), 106 deletions(-) diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index 4068678fd8433..e353fba57745f 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -6,10 +6,6 @@ #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterOptions.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/FileSystemStatCache.h" -#include "clang/Frontend/CompilerInstance.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -58,6 +54,7 @@ TEST(ClangImporterTest, emitPCHInMemory) { // Create the includes. std::string include = createFilename(temp, "include"); ASSERT_FALSE(llvm::sys::fs::create_directory(include)); + options.ExtraArgs.emplace_back("-nosysteminc"); options.ExtraArgs.emplace_back((llvm::Twine("-I") + include).str()); ASSERT_FALSE(emitFileWithContents(include, "module.modulemap", "module A {\n" @@ -91,105 +88,3 @@ TEST(ClangImporterTest, emitPCHInMemory) { ASSERT_FALSE(emitFileWithContents(PCH, "garbage")); ASSERT_TRUE(importer->canReadPCH(PCH)); } - -class ForgetfulStatCache : public clang::FileSystemStatCache { -private: - std::unique_ptr RealStatCache; - -public: - ForgetfulStatCache() { - RealStatCache = std::make_unique(); - } - ~ForgetfulStatCache() = default; - - std::error_code getStat(StringRef Path, llvm::vfs::Status &Status, - bool isFile, - std::unique_ptr *F, - llvm::vfs::FileSystem &FS) override { - if (llvm::sys::path::extension(Path) == ".pcm") { - const auto UID = llvm::sys::fs::UniqueID(1, std::numeric_limits::max()); - Status = llvm::vfs::Status(Path, UID, - /*MTime*/{}, /*User*/0, /*Group*/0, - /*Size*/0, - llvm::sys::fs::file_type::regular_file, - llvm::sys::fs::perms::all_all); - return std::error_code(); - } - - return clang::FileSystemStatCache::get(Path, Status, isFile, F, - RealStatCache.get(), FS); - } -}; - -TEST(ClangImporterTest, missingSubmodule) { - // Create a temporary cache on disk and clean it up at the end. - ClangImporterOptions options; - SmallString<256> temp; - ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory( - "ClangImporterTest.missingSubmodule", temp)); - SWIFT_DEFER { llvm::sys::fs::remove_directories(temp); }; - - // Create a cache subdirectory for the modules and PCH. - std::string cache = createFilename(temp, "cache"); - ASSERT_FALSE(llvm::sys::fs::create_directory(cache)); - options.ModuleCachePath = cache; - options.PrecompiledHeaderOutputDir = cache; - - // Create the includes. - std::string include1 = createFilename(temp, "include1"); - ASSERT_FALSE(llvm::sys::fs::create_directory(include1)); - - std::string include2 = createFilename(temp, "include2"); - ASSERT_FALSE(llvm::sys::fs::create_directory(include2)); - - options.ExtraArgs.emplace_back((llvm::Twine("-I") + include1).str()); - options.ExtraArgs.emplace_back((llvm::Twine("-I") + include2).str()); - options.ExtraArgs.emplace_back("-DEXTRA_C_DEFINE=2"); - - ASSERT_FALSE(emitFileWithContents(include1, "module.modulemap", - "module CLib1 {\n" - " umbrella header \"" + include1 + "/Clib1.h\"\n" - " export * \n" - "}\n" - "module CLib2 {\n" - " umbrella header \"" + include2 + "/Clib2.h\"\n" - " export * \n" - "}\n")); - ASSERT_FALSE(emitFileWithContents(include1, "CLib1.h", - "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" - "#error \"unexpected compiler flags\"\n" - "#endif\n" - "void foo(void);\n")); - ASSERT_FALSE(emitFileWithContents(include2, "CLib2.h", - "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" - "#error \"unexpected compiler flags\"\n" - "#endif\n" - "void foo(void);\n")); - - // Set up the importer. - swift::LangOptions langOpts; - langOpts.Target = llvm::Triple("x86_64", "apple", "darwin"); - swift::TypeCheckerOptions typeckOpts; - INITIALIZE_LLVM(); - swift::SearchPathOptions searchPathOpts; - swift::SourceManager sourceMgr; - swift::DiagnosticEngine diags(sourceMgr); - std::unique_ptr context( - ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags)); - auto importer = ClangImporter::create(*context, options); - - // Install a stats cache that conveniently "forgets" that PCMs have different - // underlying FileEntry values. - importer->getClangInstance() - .getFileManager() - .setStatCache(std::make_unique()); - - context->addModuleLoader(std::move(importer)); - - auto CLib1 = context->getIdentifier("CLib1"); - auto CLib2 = context->getIdentifier("CLib2"); - // The first load succeeds and primes the ModuleManager with PCM data. - (void)context->getModule({ Located{ CLib1, {} } }); - // The second load fails because we collide in the ModuleManager. - ASSERT_TRUE(context->getModule({ Located{ CLib2, {} } }) == nullptr); -} From 0869b519cdb15c8a1dfad09271c9e1ad19defffd Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 29 Aug 2020 17:19:49 -0700 Subject: [PATCH 36/47] Revert "Fallback to building Cmake from source on all platforms, not just Linux (#33543)" This reverts commit 539b241bf2b36b7d7e9362c30d7a39d1978bad38. --- utils/swift_build_support/swift_build_support/cmake.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/swift_build_support/swift_build_support/cmake.py b/utils/swift_build_support/swift_build_support/cmake.py index 5ef74e0cf5ee4..85a97dfa747e2 100644 --- a/utils/swift_build_support/swift_build_support/cmake.py +++ b/utils/swift_build_support/swift_build_support/cmake.py @@ -18,6 +18,7 @@ from __future__ import absolute_import, unicode_literals import os +import platform import re from numbers import Number @@ -255,6 +256,9 @@ def build_cmake(self, source_root, build_root): # the source and build the source if necessary. Returns the path to the # cmake binary. def check_cmake_version(self, source_root, build_root): + if platform.system() != 'Linux': + return + cmake_source_dir = os.path.join(source_root, 'cmake') # If the source is not checked out then don't attempt to build cmake. if not os.path.isdir(cmake_source_dir): From 161fa16cf821a23bab2b1ad57dc1a05fde404e61 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 29 Aug 2020 17:20:49 -0700 Subject: [PATCH 37/47] Revert "Turn on Verbose Output For Test" This reverts commit c34212aa20019141f4bccd0f2cb7fc0ffe217303. --- validation-test/BuildSystem/skip-local-build.test-sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation-test/BuildSystem/skip-local-build.test-sh b/validation-test/BuildSystem/skip-local-build.test-sh index 36657e3d6fa37..b658957c70a85 100644 --- a/validation-test/BuildSystem/skip-local-build.test-sh +++ b/validation-test/BuildSystem/skip-local-build.test-sh @@ -1,8 +1,8 @@ # REQUIRES: standalone_build # -# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG --dump-input=always +# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG # CONFIG: "skip_local_build": true -# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY --dump-input=always +# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY # DRY: build-script-impl # DRY-SAME: --skip-local-build From fcf398a17b4e53deef12019800dd95fdc582e2aa Mon Sep 17 00:00:00 2001 From: David Ungar Date: Sat, 29 Aug 2020 19:36:04 -0700 Subject: [PATCH 38/47] Don't crash on a bad -j input --- test/Driver/badJ.swift | 2 ++ tools/driver/driver.cpp | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 test/Driver/badJ.swift diff --git a/test/Driver/badJ.swift b/test/Driver/badJ.swift new file mode 100644 index 0000000000000..9f27b6735d52f --- /dev/null +++ b/test/Driver/badJ.swift @@ -0,0 +1,2 @@ +// RUN: not %swiftc_driver %S/../Inputs/empty.swift -jsnort 2>&1 | %FileCheck %s +// CHECK-NOT: Stack dump diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index ff189f773ab28..4ad79a461cdff 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -191,6 +191,8 @@ static int run_driver(StringRef ExecName, if (C) { std::unique_ptr TQ = TheDriver.buildTaskQueue(*C); + if (!TQ) + return 1; return C->performJobs(std::move(TQ)); } From 364b7c742ca4cd61b839f552647c2f59705603e7 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 27 Aug 2020 23:23:19 -0700 Subject: [PATCH 39/47] [ownership] Refactor OwnershipLiveRange and OwnershipPhiOperand into separate files from SemanticARCOpts.cpp --- lib/SILOptimizer/SemanticARC/CMakeLists.txt | 3 +- .../SemanticARC/OwnershipLiveRange.cpp | 354 ++++++++++ .../SemanticARC/OwnershipLiveRange.h | 210 ++++++ .../SemanticARC/OwnershipPhiOperand.h | 122 ++++ .../SemanticARC/SemanticARCOpts.cpp | 652 +----------------- 5 files changed, 695 insertions(+), 646 deletions(-) create mode 100644 lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp create mode 100644 lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h create mode 100644 lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h diff --git a/lib/SILOptimizer/SemanticARC/CMakeLists.txt b/lib/SILOptimizer/SemanticARC/CMakeLists.txt index 7ae8f36a2bbde..91b9e107c5dc8 100644 --- a/lib/SILOptimizer/SemanticARC/CMakeLists.txt +++ b/lib/SILOptimizer/SemanticARC/CMakeLists.txt @@ -1,2 +1,3 @@ target_sources(swiftSILOptimizer PRIVATE - SemanticARCOpts.cpp) + SemanticARCOpts.cpp + OwnershipLiveRange.cpp) diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp new file mode 100644 index 0000000000000..187b28e68c27b --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp @@ -0,0 +1,354 @@ +//===--- OwnershipLiveRange.cpp -------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// + +#include "OwnershipLiveRange.h" +#include "OwnershipPhiOperand.h" + +using namespace swift; +using namespace swift::semanticarc; + +OwnershipLiveRange::OwnershipLiveRange(SILValue value) + : introducer(*OwnedValueIntroducer::get(value)), destroyingUses(), + ownershipForwardingUses(), unknownConsumingUses() { + assert(introducer.value.getOwnershipKind() == ValueOwnershipKind::Owned); + + SmallVector tmpDestroyingUses; + SmallVector tmpForwardingConsumingUses; + SmallVector tmpUnknownConsumingUses; + + // We know that our silvalue produces an @owned value. Look through all of our + // uses and classify them as either consuming or not. + SmallVector worklist(introducer.value->getUses()); + while (!worklist.empty()) { + auto *op = worklist.pop_back_val(); + + // Skip type dependent operands. + if (op->isTypeDependent()) + continue; + + // Do a quick check that we did not add ValueOwnershipKind that are not + // owned to the worklist. + assert(op->get().getOwnershipKind() == ValueOwnershipKind::Owned && + "Added non-owned value to worklist?!"); + + auto *user = op->getUser(); + + // Ok, this constraint can take something owned as live. Assert that it + // can also accept something that is guaranteed. Any non-consuming use of + // an owned value should be able to take a guaranteed parameter as well + // (modulo bugs). We assert to catch these. + if (!op->isConsumingUse()) { + continue; + } + + // Ok, we know now that we have a consuming use. See if we have a destroy + // value, quickly up front. If we do have one, stash it and continue. + if (isa(user)) { + tmpDestroyingUses.push_back(op); + continue; + } + + // Otherwise, see if we have a forwarding value that has a single + // non-trivial operand that can accept a guaranteed value. If not, we can + // not recursively process it, so be conservative and assume that we /may + // consume/ the value, so the live range must not be eliminated. + // + // DISCUSSION: For now we do not support forwarding instructions with + // multiple non-trivial arguments since we would need to optimize all of + // the non-trivial arguments at the same time. + // + // NOTE: Today we do not support TermInsts for simplicity... we /could/ + // support it though if we need to. + auto *ti = dyn_cast(user); + if ((ti && !ti->isTransformationTerminator()) || + !isGuaranteedForwardingInst(user) || + 1 != count_if(user->getOperandValues( + true /*ignore type dependent operands*/), + [&](SILValue v) { + return v.getOwnershipKind() == + ValueOwnershipKind::Owned; + })) { + tmpUnknownConsumingUses.push_back(op); + continue; + } + + // Ok, this is a forwarding instruction whose ownership we can flip from + // owned -> guaranteed. + tmpForwardingConsumingUses.push_back(op); + + // If we have a non-terminator, just visit its users recursively to see if + // the the users force the live range to be alive. + if (!ti) { + for (SILValue v : user->getResults()) { + if (v.getOwnershipKind() != ValueOwnershipKind::Owned) + continue; + llvm::copy(v->getUses(), std::back_inserter(worklist)); + } + continue; + } + + // Otherwise, we know that we have no only a terminator, but a + // transformation terminator, so we should add the users of its results to + // the worklist. + for (auto &succ : ti->getSuccessors()) { + auto *succBlock = succ.getBB(); + + // If we do not have any arguments, then continue. + if (succBlock->args_empty()) + continue; + + for (auto *succArg : succBlock->getSILPhiArguments()) { + // If we have an any value, just continue. + if (succArg->getOwnershipKind() == ValueOwnershipKind::None) + continue; + + // Otherwise add all users of this BBArg to the worklist to visit + // recursively. + llvm::copy(succArg->getUses(), std::back_inserter(worklist)); + } + } + } + + // The order in which we append these to consumingUses matters since we assume + // their order as an invariant. This is done to ensure that we can pass off + // all of our uses or individual sub-arrays of our users without needing to + // move around memory. + llvm::copy(tmpDestroyingUses, std::back_inserter(consumingUses)); + llvm::copy(tmpForwardingConsumingUses, std::back_inserter(consumingUses)); + llvm::copy(tmpUnknownConsumingUses, std::back_inserter(consumingUses)); + + auto cUseArrayRef = llvm::makeArrayRef(consumingUses); + destroyingUses = cUseArrayRef.take_front(tmpDestroyingUses.size()); + ownershipForwardingUses = cUseArrayRef.slice( + tmpDestroyingUses.size(), tmpForwardingConsumingUses.size()); + unknownConsumingUses = cUseArrayRef.take_back(tmpUnknownConsumingUses.size()); +} + +void OwnershipLiveRange::insertEndBorrowsAtDestroys( + SILValue newGuaranteedValue, DeadEndBlocks &deadEndBlocks, + ValueLifetimeAnalysis::Frontier &scratch) { + assert(scratch.empty() && "Expected scratch to be initially empty?!"); + + // Since we are looking through forwarding uses that can accept guaranteed + // parameters, we can have multiple destroy_value along the same path. We need + // to find the post-dominating block set of these destroy value to ensure that + // we do not insert multiple end_borrow. + // + // TODO: Hoist this out? + SILInstruction *inst = introducer.value->getDefiningInstruction(); + Optional analysis; + if (!inst) { + analysis.emplace(cast(introducer.value), + getAllConsumingInsts()); + } else { + analysis.emplace(inst, getAllConsumingInsts()); + } + + // Use all consuming uses in our value lifetime analysis to ensure correctness + // in the face of unreachable code. + bool foundCriticalEdges = !analysis->computeFrontier( + scratch, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + auto loc = RegularLocation::getAutoGeneratedLocation(); + while (!scratch.empty()) { + auto *insertPoint = scratch.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createEndBorrow(loc, newGuaranteedValue); + } +} + +static void convertInstructionOwnership(SILInstruction *i, + ValueOwnershipKind oldOwnership, + ValueOwnershipKind newOwnership) { + // If this is a term inst, just convert all of its incoming values that are + // owned to be guaranteed. + if (auto *ti = dyn_cast(i)) { + for (auto &succ : ti->getSuccessors()) { + auto *succBlock = succ.getBB(); + + // If we do not have any arguments, then continue. + if (succBlock->args_empty()) + continue; + + for (auto *succArg : succBlock->getSILPhiArguments()) { + // If we have an any value, just continue. + if (succArg->getOwnershipKind() == oldOwnership) { + succArg->setOwnershipKind(newOwnership); + } + } + } + return; + } + + assert(i->hasResults()); + for (SILValue result : i->getResults()) { + if (auto *svi = dyn_cast(result)) { + if (svi->getOwnershipKind() == oldOwnership) { + svi->setOwnershipKind(newOwnership); + } + continue; + } + + if (auto *ofci = dyn_cast(result)) { + if (ofci->getOwnershipKind() == oldOwnership) { + ofci->setOwnershipKind(newOwnership); + } + continue; + } + + if (auto *sei = dyn_cast(result)) { + if (sei->getOwnershipKind() == oldOwnership) { + sei->setOwnershipKind(newOwnership); + } + continue; + } + + if (auto *mvir = dyn_cast(result)) { + if (mvir->getOwnershipKind() == oldOwnership) { + mvir->setOwnershipKind(newOwnership); + } + continue; + } + + llvm_unreachable("unhandled forwarding instruction?!"); + } +} + +void OwnershipLiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() && { + while (!ownershipForwardingUses.empty()) { + auto *i = ownershipForwardingUses.back()->getUser(); + ownershipForwardingUses = ownershipForwardingUses.drop_back(); + convertInstructionOwnership(i, ValueOwnershipKind::Owned, + ValueOwnershipKind::Guaranteed); + } +} + +void OwnershipLiveRange::convertToGuaranteedAndRAUW( + SILValue newGuaranteedValue, InstModCallbacks callbacks) && { + auto *value = cast(introducer.value); + while (!destroyingUses.empty()) { + auto *d = destroyingUses.back(); + destroyingUses = destroyingUses.drop_back(); + callbacks.deleteInst(d->getUser()); + } + + callbacks.eraseAndRAUWSingleValueInst(value, newGuaranteedValue); + + // Then change all of our guaranteed forwarding insts to have guaranteed + // ownership kind instead of what ever they previously had (ignoring trivial + // results); + std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); +} + +// TODO: If this is useful, move onto OwnedValueIntroducer itself? +static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) { + switch (introducer.kind) { + case OwnedValueIntroducerKind::Phi: { + auto *phiArg = cast(introducer.value); + phiArg->setOwnershipKind(ValueOwnershipKind::Guaranteed); + return phiArg; + } + case OwnedValueIntroducerKind::Struct: { + auto *si = cast(introducer.value); + si->setOwnershipKind(ValueOwnershipKind::Guaranteed); + return si; + } + case OwnedValueIntroducerKind::Tuple: { + auto *ti = cast(introducer.value); + ti->setOwnershipKind(ValueOwnershipKind::Guaranteed); + return ti; + } + case OwnedValueIntroducerKind::Copy: + case OwnedValueIntroducerKind::LoadCopy: + case OwnedValueIntroducerKind::Apply: + case OwnedValueIntroducerKind::BeginApply: + case OwnedValueIntroducerKind::TryApply: + case OwnedValueIntroducerKind::LoadTake: + case OwnedValueIntroducerKind::FunctionArgument: + case OwnedValueIntroducerKind::PartialApplyInit: + case OwnedValueIntroducerKind::AllocBoxInit: + case OwnedValueIntroducerKind::AllocRefInit: + return SILValue(); + } +} + +void OwnershipLiveRange::convertJoinedLiveRangePhiToGuaranteed( + DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, + InstModCallbacks callbacks) && { + + // First convert the phi value itself to be guaranteed. + SILValue phiValue = convertIntroducerToGuaranteed(introducer); + + // Then insert end_borrows at each of our destroys if we are consuming. We + // have to convert the phi to guaranteed first since otherwise, the ownership + // check when we create the end_borrows will trigger. + if (introducer.hasConsumingGuaranteedOperands()) { + insertEndBorrowsAtDestroys(phiValue, deadEndBlocks, scratch); + } + + // Then eliminate all of the destroys... + while (!destroyingUses.empty()) { + auto *d = destroyingUses.back(); + destroyingUses = destroyingUses.drop_back(); + callbacks.deleteInst(d->getUser()); + } + + // and change all of our guaranteed forwarding insts to have guaranteed + // ownership kind instead of what ever they previously had (ignoring trivial + // results); + std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); +} + +OwnershipLiveRange::HasConsumingUse_t +OwnershipLiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const { + // First do a quick check if we have /any/ unknown consuming + // uses. If we do not have any, return false early. + if (unknownConsumingUses.empty()) { + return HasConsumingUse_t::No; + } + + // Ok, we do have some unknown consuming uses. If we aren't assuming we are at + // the fixed point yet, just bail. + if (!assumingAtFixPoint) { + return HasConsumingUse_t::Yes; + } + + // We do not know how to handle yet cases where an owned value is used by + // multiple phi nodes. So we bail early if unknown consuming uses is > 1. + // + // TODO: Build up phi node web. + auto *op = getSingleUnknownConsumingUse(); + if (!op) { + return HasConsumingUse_t::Yes; + } + + // Make sure our single unknown consuming use is a branch inst. If not, bail, + // this is a /real/ unknown consuming use. + if (!OwnershipPhiOperand::get(op)) { + return HasConsumingUse_t::Yes; + } + + // Otherwise, setup the phi to incoming value map mapping the block arguments + // to our introducer. + return HasConsumingUse_t::YesButAllPhiArgs; +} + +OwnershipLiveRange::DestroyingInstsRange +OwnershipLiveRange::getDestroyingInsts() const { + return DestroyingInstsRange(getDestroyingUses(), OperandToUser()); +} + +OwnershipLiveRange::ConsumingInstsRange +OwnershipLiveRange::getAllConsumingInsts() const { + return ConsumingInstsRange(consumingUses, OperandToUser()); +} diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h new file mode 100644 index 0000000000000..c9be71b479a3b --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h @@ -0,0 +1,210 @@ +//===--- OwnershipLiveRange.h ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPLIVERANGE_H +#define SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPLIVERANGE_H + +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" + +namespace swift { +namespace semanticarc { + +/// This class represents an "extended live range" of an owned value. Such a +/// representation includes: +/// +/// 1. The owned introducing value. +/// 2. Any forwarding instructions that consume the introduced value +/// (transitively) and then propagate a new owned value. +/// 3. Transitive destroys on the forwarding instructions/destroys on the owned +/// introducing value. +/// 4. Any unknown consuming uses that are not understood by this code. +/// +/// This allows for this to be used to convert such a set of uses from using +/// owned ownership to using guaranteed ownership by converting the +/// destroy_value -> end_borrow and "flipping" the ownership of individual +/// forwarding instructions. +/// +/// NOTE: We do not look through "phi nodes" in the ownership graph (e.x.: real +/// phi arguments, struct, tuple). Instead we represent those as nodes in a +/// larger phi ownership web, connected via individual OwnershipLiveRange. +class LLVM_LIBRARY_VISIBILITY OwnershipLiveRange { + /// The value that we are computing the LiveRange for. Expected to be an owned + /// introducer and not to be forwarding. + OwnedValueIntroducer introducer; + + /// A vector that we store all of our uses into. + /// + /// Some properties of this array are: + /// + /// 1. It is only mutated in the constructor of LiveRange. + /// + /// 2. destroyingUses, ownershipForwardingUses, and unknownConsumingUses are + /// views into this array. We store the respective uses in the aforementioned + /// order. This is why it is important not to mutate consumingUses after we + /// construct the LiveRange since going from small -> large could invalidate + /// the uses. + SmallVector consumingUses; + + /// A list of destroy_values of the live range. + /// + /// This is just a view into consuming uses. + ArrayRef destroyingUses; + + /// A list of forwarding instructions that forward owned ownership, but that + /// are also able to be converted to guaranteed ownership. + /// + /// If we are able to eliminate this LiveRange due to it being from a + /// guaranteed value, we must flip the ownership of all of these instructions + /// to guaranteed from owned. + /// + /// NOTE: Normally only destroying or consuming uses end the live range. We + /// copy these transitive uses as well into the consumingUses array since + /// transitive uses can extend a live range up to an unreachable block without + /// ultimately being consuming. In such a situation if we did not also store + /// this into consuming uses, we would not be able to ascertain just using the + /// "consumingUses" array the true lifetime of the OwnershipLiveRange. + /// + /// Corresponds to isOwnershipForwardingInst(...). + ArrayRef ownershipForwardingUses; + + /// Consuming uses that we were not able to understand as a forwarding + /// instruction or a destroy_value. These must be passed a strongly control + /// equivalent +1 value. + ArrayRef unknownConsumingUses; + +public: + OwnershipLiveRange(SILValue value); + OwnershipLiveRange(const OwnershipLiveRange &) = delete; + OwnershipLiveRange &operator=(const OwnershipLiveRange &) = delete; + + enum class HasConsumingUse_t { + No = 0, + YesButAllPhiArgs = 1, + Yes = 2, + }; + + /// Return true if v only has invalidating uses that are destroy_value. Such + /// an owned value is said to represent a dead "live range". + /// + /// Semantically this implies that a value is never passed off as +1 to memory + /// or another function implying it can be used everywhere at +0. + HasConsumingUse_t + hasUnknownConsumingUse(bool assumingFixedPoint = false) const; + + /// Return an array ref to /all/ consuming uses. Will include all 3 sorts of + /// consuming uses: destroying uses, forwarding consuming uses, and unknown + /// forwarding instruction. + ArrayRef getAllConsumingUses() const { return consumingUses; } + + ArrayRef getDestroyingUses() const { return destroyingUses; } + +private: + struct OperandToUser; + +public: + using DestroyingInstsRange = + TransformRange, OperandToUser>; + DestroyingInstsRange getDestroyingInsts() const; + + using ConsumingInstsRange = + TransformRange, OperandToUser>; + ConsumingInstsRange getAllConsumingInsts() const; + + /// If this LiveRange has a single destroying use, return that use. Otherwise, + /// return nullptr. + Operand *getSingleDestroyingUse() const { + if (destroyingUses.size() != 1) { + return nullptr; + } + return destroyingUses.front(); + } + + /// If this LiveRange has a single unknown destroying use, return that + /// use. Otherwise, return nullptr. + Operand *getSingleUnknownConsumingUse() const { + if (unknownConsumingUses.size() != 1) { + return nullptr; + } + return unknownConsumingUses.front(); + } + + OwnedValueIntroducer getIntroducer() const { return introducer; } + + ArrayRef getOwnershipForwardingUses() const { + return ownershipForwardingUses; + } + + void convertOwnedGeneralForwardingUsesToGuaranteed() &&; + + /// A consuming operation that: + /// + /// 1. If \p insertEndBorrows is true inserts end borrows at all + /// destroying insts locations. + /// + /// 2. Deletes all destroy_values. + /// + /// 3. RAUW value with newGuaranteedValue. + /// + /// 4. Convert all of the general forwarding instructions from + /// @owned -> @guaranteed. "Like Dominoes". + /// + /// 5. Leaves all of the unknown consuming users alone. It is up to + /// the caller to handle converting their ownership. + void convertToGuaranteedAndRAUW(SILValue newGuaranteedValue, + InstModCallbacks callbacks) &&; + + /// A consuming operation that in order: + /// + /// 1. Converts the phi argument to be guaranteed via setOwnership. + /// + /// 2. If this consumes a borrow, insert end_borrows at the relevant + /// destroy_values. + /// + /// 3. Deletes all destroy_values. + /// + /// 4. Converts all of the general forwarding instructions from @owned -> + /// @guaranteed. "Like Dominoes". + /// + /// NOTE: This leaves all of the unknown consuming users alone. It is up to + /// the caller to handle converting their ownership. + /// + /// NOTE: This routine leaves inserting begin_borrows for the incoming values + /// to the caller since those are not part of the LiveRange itself. + void convertJoinedLiveRangePhiToGuaranteed( + DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, + InstModCallbacks callbacks) &&; + + /// Given a new guaranteed value, insert end_borrow for the newGuaranteedValue + /// at all of our destroy_values in prepration for converting from owned to + /// guaranteed. + /// + /// This is used when converting load [copy] -> load_borrow. + void insertEndBorrowsAtDestroys(SILValue newGuaranteedValue, + DeadEndBlocks &deadEndBlocks, + ValueLifetimeAnalysis::Frontier &scratch); +}; + +struct OwnershipLiveRange::OperandToUser { + OperandToUser() {} + + SILInstruction *operator()(const Operand *use) const { + auto *nonConstUse = const_cast(use); + return nonConstUse->getUser(); + } +}; + +} // namespace semanticarc +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPLIVERANGE_H diff --git a/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h new file mode 100644 index 0000000000000..169eb9859bea8 --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h @@ -0,0 +1,122 @@ +//===--- OwnershipPhiOperand.h --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPPHIOPERAND_H +#define SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPPHIOPERAND_H + +#include "swift/Basic/STLExtras.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILUndef.h" +#include "swift/SIL/SILValue.h" + +namespace swift { +namespace semanticarc { + +/// The operand of a "phi" in the induced ownership graph of a def-use graph. +/// +/// Some examples: br, struct, tuple. +class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand { +public: + enum Kind { + Branch, + Struct, + Tuple, + }; + +private: + Operand *op; + + OwnershipPhiOperand(Operand *op) : op(op) {} + +public: + static Optional get(const Operand *op) { + switch (op->getUser()->getKind()) { + case SILInstructionKind::BranchInst: + case SILInstructionKind::StructInst: + case SILInstructionKind::TupleInst: + return {{const_cast(op)}}; + default: + return None; + } + } + + Kind getKind() const { + switch (op->getUser()->getKind()) { + case SILInstructionKind::BranchInst: + return Kind::Branch; + case SILInstructionKind::StructInst: + return Kind::Struct; + case SILInstructionKind::TupleInst: + return Kind::Tuple; + default: + llvm_unreachable("unhandled case?!"); + } + } + + Operand *getOperand() const { return op; } + SILValue getValue() const { return op->get(); } + SILType getType() const { return op->get()->getType(); } + + unsigned getOperandNumber() const { return op->getOperandNumber(); } + + void markUndef() & { + op->set(SILUndef::get(getType(), *op->getUser()->getFunction())); + } + + SILInstruction *getInst() const { return op->getUser(); } + + /// Return true if this phi consumes a borrow. + /// + /// If so, we may need to insert an extra begin_borrow to balance the +1 when + /// converting owned ownership phis to guaranteed ownership phis. + bool isGuaranteedConsuming() const { + switch (getKind()) { + case Kind::Branch: + return true; + case Kind::Tuple: + case Kind::Struct: + return false; + } + } + + bool operator<(const OwnershipPhiOperand &other) const { + return op < other.op; + } + + bool operator==(const OwnershipPhiOperand &other) const { + return op == other.op; + } + + bool visitResults(function_ref visitor) const { + switch (getKind()) { + case Kind::Struct: + return visitor(cast(getInst())); + case Kind::Tuple: + return visitor(cast(getInst())); + case Kind::Branch: { + auto *br = cast(getInst()); + unsigned opNum = getOperandNumber(); + return llvm::all_of( + br->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) { + return visitor(succBlock->getSILPhiArguments()[opNum]); + }); + } + } + } +}; + +} // namespace semanticarc +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPPHIOPERAND_H diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 180f3882a7807..383ec145a6455 100644 --- a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -11,6 +11,12 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-semantic-arc-opts" + +#include "swift/Basic/LLVM.h" + +#include "OwnershipPhiOperand.h" +#include "OwnershipLiveRange.h" + #include "swift/Basic/BlotSetVector.h" #include "swift/Basic/FrozenMultiMap.h" #include "swift/Basic/MultiMapCache.h" @@ -36,642 +42,7 @@ #include "llvm/Support/CommandLine.h" using namespace swift; - -STATISTIC(NumEliminatedInsts, "number of removed instructions"); -STATISTIC(NumLoadCopyConvertedToLoadBorrow, - "number of load_copy converted to load_borrow"); - -//===----------------------------------------------------------------------===// -// Ownership Phi Operand -//===----------------------------------------------------------------------===// - -namespace { - -/// The operand of a "phi" in the induced ownership graph of a def-use graph. -/// -/// Some examples: br, struct, tuple. -class OwnershipPhiOperand { -public: - enum Kind { - Branch, - Struct, - Tuple, - }; - -private: - Operand *op; - - OwnershipPhiOperand(Operand *op) : op(op) {} - -public: - static Optional get(const Operand *op) { - switch (op->getUser()->getKind()) { - case SILInstructionKind::BranchInst: - case SILInstructionKind::StructInst: - case SILInstructionKind::TupleInst: - return {{const_cast(op)}}; - default: - return None; - } - } - - Kind getKind() const { - switch (op->getUser()->getKind()) { - case SILInstructionKind::BranchInst: - return Kind::Branch; - case SILInstructionKind::StructInst: - return Kind::Struct; - case SILInstructionKind::TupleInst: - return Kind::Tuple; - default: - llvm_unreachable("unhandled case?!"); - } - } - - Operand *getOperand() const { return op; } - SILValue getValue() const { return op->get(); } - SILType getType() const { return op->get()->getType(); } - - unsigned getOperandNumber() const { return op->getOperandNumber(); } - - void markUndef() & { - op->set(SILUndef::get(getType(), *op->getUser()->getFunction())); - } - - SILInstruction *getInst() const { return op->getUser(); } - - /// Return true if this phi consumes a borrow. - /// - /// If so, we may need to insert an extra begin_borrow to balance the +1 when - /// converting owned ownership phis to guaranteed ownership phis. - bool isGuaranteedConsuming() const { - switch (getKind()) { - case Kind::Branch: - return true; - case Kind::Tuple: - case Kind::Struct: - return false; - } - } - - bool operator<(const OwnershipPhiOperand &other) const { - return op < other.op; - } - - bool operator==(const OwnershipPhiOperand &other) const { - return op == other.op; - } - - bool visitResults(function_ref visitor) const { - switch (getKind()) { - case Kind::Struct: - return visitor(cast(getInst())); - case Kind::Tuple: - return visitor(cast(getInst())); - case Kind::Branch: { - auto *br = cast(getInst()); - unsigned opNum = getOperandNumber(); - return llvm::all_of( - br->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) { - return visitor(succBlock->getSILPhiArguments()[opNum]); - }); - } - } - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Live Range Modeling -//===----------------------------------------------------------------------===// - -namespace { - -/// This class represents an "extended live range" of an owned value. Such a -/// representation includes: -/// -/// 1. The owned introducing value. -/// 2. Any forwarding instructions that consume the introduced value -/// (transitively) and then propagate a new owned value. -/// 3. Transitive destroys on the forwarding instructions/destroys on the owned -/// introducing value. -/// 4. Any unknown consuming uses that are not understood by this code. -/// -/// This allows for this to be used to convert such a set of uses from using -/// owned ownership to using guaranteed ownership by converting the -/// destroy_value -> end_borrow and "flipping" the ownership of individual -/// forwarding instructions. -/// -/// NOTE: We do not look through "phi nodes" in the ownership graph (e.x.: real -/// phi arguments, struct, tuple). Instead we represent those as nodes in a -/// larger phi ownership web, connected via individual OwnershipLiveRange. -class OwnershipLiveRange { - /// The value that we are computing the LiveRange for. Expected to be an owned - /// introducer and not to be forwarding. - OwnedValueIntroducer introducer; - - /// A vector that we store all of our uses into. - /// - /// Some properties of this array are: - /// - /// 1. It is only mutated in the constructor of LiveRange. - /// - /// 2. destroyingUses, ownershipForwardingUses, and unknownConsumingUses are - /// views into this array. We store the respective uses in the aforementioned - /// order. This is why it is important not to mutate consumingUses after we - /// construct the LiveRange since going from small -> large could invalidate - /// the uses. - SmallVector consumingUses; - - /// A list of destroy_values of the live range. - /// - /// This is just a view into consuming uses. - ArrayRef destroyingUses; - - /// A list of forwarding instructions that forward owned ownership, but that - /// are also able to be converted to guaranteed ownership. - /// - /// If we are able to eliminate this LiveRange due to it being from a - /// guaranteed value, we must flip the ownership of all of these instructions - /// to guaranteed from owned. - /// - /// NOTE: Normally only destroying or consuming uses end the live range. We - /// copy these transitive uses as well into the consumingUses array since - /// transitive uses can extend a live range up to an unreachable block without - /// ultimately being consuming. In such a situation if we did not also store - /// this into consuming uses, we would not be able to ascertain just using the - /// "consumingUses" array the true lifetime of the OwnershipLiveRange. - /// - /// Corresponds to isOwnershipForwardingInst(...). - ArrayRef ownershipForwardingUses; - - /// Consuming uses that we were not able to understand as a forwarding - /// instruction or a destroy_value. These must be passed a strongly control - /// equivalent +1 value. - ArrayRef unknownConsumingUses; - -public: - OwnershipLiveRange(SILValue value); - OwnershipLiveRange(const OwnershipLiveRange &) = delete; - OwnershipLiveRange &operator=(const OwnershipLiveRange &) = delete; - - enum class HasConsumingUse_t { - No = 0, - YesButAllPhiArgs = 1, - Yes = 2, - }; - - /// Return true if v only has invalidating uses that are destroy_value. Such - /// an owned value is said to represent a dead "live range". - /// - /// Semantically this implies that a value is never passed off as +1 to memory - /// or another function implying it can be used everywhere at +0. - HasConsumingUse_t - hasUnknownConsumingUse(bool assumingFixedPoint = false) const; - - /// Return an array ref to /all/ consuming uses. Will include all 3 sorts of - /// consuming uses: destroying uses, forwarding consuming uses, and unknown - /// forwarding instruction. - ArrayRef getAllConsumingUses() const { return consumingUses; } - - ArrayRef getDestroyingUses() const { return destroyingUses; } - -private: - struct OperandToUser; - -public: - using DestroyingInstsRange = - TransformRange, OperandToUser>; - DestroyingInstsRange getDestroyingInsts() const; - - using ConsumingInstsRange = - TransformRange, OperandToUser>; - ConsumingInstsRange getAllConsumingInsts() const; - - /// If this LiveRange has a single destroying use, return that use. Otherwise, - /// return nullptr. - Operand *getSingleDestroyingUse() const { - if (destroyingUses.size() != 1) { - return nullptr; - } - return destroyingUses.front(); - } - - /// If this LiveRange has a single unknown destroying use, return that - /// use. Otherwise, return nullptr. - Operand *getSingleUnknownConsumingUse() const { - if (unknownConsumingUses.size() != 1) { - return nullptr; - } - return unknownConsumingUses.front(); - } - - OwnedValueIntroducer getIntroducer() const { return introducer; } - - ArrayRef getOwnershipForwardingUses() const { - return ownershipForwardingUses; - } - - void convertOwnedGeneralForwardingUsesToGuaranteed() &&; - - /// A consuming operation that: - /// - /// 1. If \p insertEndBorrows is true inserts end borrows at all - /// destroying insts locations. - /// - /// 2. Deletes all destroy_values. - /// - /// 3. RAUW value with newGuaranteedValue. - /// - /// 4. Convert all of the general forwarding instructions from - /// @owned -> @guaranteed. "Like Dominoes". - /// - /// 5. Leaves all of the unknown consuming users alone. It is up to - /// the caller to handle converting their ownership. - void convertToGuaranteedAndRAUW(SILValue newGuaranteedValue, - InstModCallbacks callbacks) &&; - - /// A consuming operation that in order: - /// - /// 1. Converts the phi argument to be guaranteed via setOwnership. - /// - /// 2. If this consumes a borrow, insert end_borrows at the relevant - /// destroy_values. - /// - /// 3. Deletes all destroy_values. - /// - /// 4. Converts all of the general forwarding instructions from @owned -> - /// @guaranteed. "Like Dominoes". - /// - /// NOTE: This leaves all of the unknown consuming users alone. It is up to - /// the caller to handle converting their ownership. - /// - /// NOTE: This routine leaves inserting begin_borrows for the incoming values - /// to the caller since those are not part of the LiveRange itself. - void convertJoinedLiveRangePhiToGuaranteed( - DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, - InstModCallbacks callbacks) &&; - - /// Given a new guaranteed value, insert end_borrow for the newGuaranteedValue - /// at all of our destroy_values in prepration for converting from owned to - /// guaranteed. - /// - /// This is used when converting load [copy] -> load_borrow. - void insertEndBorrowsAtDestroys(SILValue newGuaranteedValue, - DeadEndBlocks &deadEndBlocks, - ValueLifetimeAnalysis::Frontier &scratch); -}; - -} // end anonymous namespace - -struct OwnershipLiveRange::OperandToUser { - OperandToUser() {} - - SILInstruction *operator()(const Operand *use) const { - auto *nonConstUse = const_cast(use); - return nonConstUse->getUser(); - } -}; - -OwnershipLiveRange::DestroyingInstsRange -OwnershipLiveRange::getDestroyingInsts() const { - return DestroyingInstsRange(getDestroyingUses(), OperandToUser()); -} - -OwnershipLiveRange::ConsumingInstsRange -OwnershipLiveRange::getAllConsumingInsts() const { - return ConsumingInstsRange(consumingUses, OperandToUser()); -} - -OwnershipLiveRange::OwnershipLiveRange(SILValue value) - : introducer(*OwnedValueIntroducer::get(value)), destroyingUses(), - ownershipForwardingUses(), unknownConsumingUses() { - assert(introducer.value.getOwnershipKind() == ValueOwnershipKind::Owned); - - SmallVector tmpDestroyingUses; - SmallVector tmpForwardingConsumingUses; - SmallVector tmpUnknownConsumingUses; - - // We know that our silvalue produces an @owned value. Look through all of our - // uses and classify them as either consuming or not. - SmallVector worklist(introducer.value->getUses()); - while (!worklist.empty()) { - auto *op = worklist.pop_back_val(); - - // Skip type dependent operands. - if (op->isTypeDependent()) - continue; - - // Do a quick check that we did not add ValueOwnershipKind that are not - // owned to the worklist. - assert(op->get().getOwnershipKind() == ValueOwnershipKind::Owned && - "Added non-owned value to worklist?!"); - - auto *user = op->getUser(); - - // Ok, this constraint can take something owned as live. Assert that it - // can also accept something that is guaranteed. Any non-consuming use of - // an owned value should be able to take a guaranteed parameter as well - // (modulo bugs). We assert to catch these. - if (!op->isConsumingUse()) { - continue; - } - - // Ok, we know now that we have a consuming use. See if we have a destroy - // value, quickly up front. If we do have one, stash it and continue. - if (isa(user)) { - tmpDestroyingUses.push_back(op); - continue; - } - - // Otherwise, see if we have a forwarding value that has a single - // non-trivial operand that can accept a guaranteed value. If not, we can - // not recursively process it, so be conservative and assume that we /may - // consume/ the value, so the live range must not be eliminated. - // - // DISCUSSION: For now we do not support forwarding instructions with - // multiple non-trivial arguments since we would need to optimize all of - // the non-trivial arguments at the same time. - // - // NOTE: Today we do not support TermInsts for simplicity... we /could/ - // support it though if we need to. - auto *ti = dyn_cast(user); - if ((ti && !ti->isTransformationTerminator()) || - !isGuaranteedForwardingInst(user) || - 1 != count_if(user->getOperandValues( - true /*ignore type dependent operands*/), - [&](SILValue v) { - return v.getOwnershipKind() == - ValueOwnershipKind::Owned; - })) { - tmpUnknownConsumingUses.push_back(op); - continue; - } - - // Ok, this is a forwarding instruction whose ownership we can flip from - // owned -> guaranteed. - tmpForwardingConsumingUses.push_back(op); - - // If we have a non-terminator, just visit its users recursively to see if - // the the users force the live range to be alive. - if (!ti) { - for (SILValue v : user->getResults()) { - if (v.getOwnershipKind() != ValueOwnershipKind::Owned) - continue; - llvm::copy(v->getUses(), std::back_inserter(worklist)); - } - continue; - } - - // Otherwise, we know that we have no only a terminator, but a - // transformation terminator, so we should add the users of its results to - // the worklist. - for (auto &succ : ti->getSuccessors()) { - auto *succBlock = succ.getBB(); - - // If we do not have any arguments, then continue. - if (succBlock->args_empty()) - continue; - - for (auto *succArg : succBlock->getSILPhiArguments()) { - // If we have an any value, just continue. - if (succArg->getOwnershipKind() == ValueOwnershipKind::None) - continue; - - // Otherwise add all users of this BBArg to the worklist to visit - // recursively. - llvm::copy(succArg->getUses(), std::back_inserter(worklist)); - } - } - } - - // The order in which we append these to consumingUses matters since we assume - // their order as an invariant. This is done to ensure that we can pass off - // all of our uses or individual sub-arrays of our users without needing to - // move around memory. - llvm::copy(tmpDestroyingUses, std::back_inserter(consumingUses)); - llvm::copy(tmpForwardingConsumingUses, std::back_inserter(consumingUses)); - llvm::copy(tmpUnknownConsumingUses, std::back_inserter(consumingUses)); - - auto cUseArrayRef = llvm::makeArrayRef(consumingUses); - destroyingUses = cUseArrayRef.take_front(tmpDestroyingUses.size()); - ownershipForwardingUses = cUseArrayRef.slice( - tmpDestroyingUses.size(), tmpForwardingConsumingUses.size()); - unknownConsumingUses = cUseArrayRef.take_back(tmpUnknownConsumingUses.size()); -} - -void OwnershipLiveRange::insertEndBorrowsAtDestroys( - SILValue newGuaranteedValue, DeadEndBlocks &deadEndBlocks, - ValueLifetimeAnalysis::Frontier &scratch) { - assert(scratch.empty() && "Expected scratch to be initially empty?!"); - - // Since we are looking through forwarding uses that can accept guaranteed - // parameters, we can have multiple destroy_value along the same path. We need - // to find the post-dominating block set of these destroy value to ensure that - // we do not insert multiple end_borrow. - // - // TODO: Hoist this out? - SILInstruction *inst = introducer.value->getDefiningInstruction(); - Optional analysis; - if (!inst) { - analysis.emplace(cast(introducer.value), - getAllConsumingInsts()); - } else { - analysis.emplace(inst, getAllConsumingInsts()); - } - - // Use all consuming uses in our value lifetime analysis to ensure correctness - // in the face of unreachable code. - bool foundCriticalEdges = !analysis->computeFrontier( - scratch, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - auto loc = RegularLocation::getAutoGeneratedLocation(); - while (!scratch.empty()) { - auto *insertPoint = scratch.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createEndBorrow(loc, newGuaranteedValue); - } -} - -static void convertInstructionOwnership(SILInstruction *i, - ValueOwnershipKind oldOwnership, - ValueOwnershipKind newOwnership) { - // If this is a term inst, just convert all of its incoming values that are - // owned to be guaranteed. - if (auto *ti = dyn_cast(i)) { - for (auto &succ : ti->getSuccessors()) { - auto *succBlock = succ.getBB(); - - // If we do not have any arguments, then continue. - if (succBlock->args_empty()) - continue; - - for (auto *succArg : succBlock->getSILPhiArguments()) { - // If we have an any value, just continue. - if (succArg->getOwnershipKind() == oldOwnership) { - succArg->setOwnershipKind(newOwnership); - } - } - } - return; - } - - assert(i->hasResults()); - for (SILValue result : i->getResults()) { - if (auto *svi = dyn_cast(result)) { - if (svi->getOwnershipKind() == oldOwnership) { - svi->setOwnershipKind(newOwnership); - } - continue; - } - - if (auto *ofci = dyn_cast(result)) { - if (ofci->getOwnershipKind() == oldOwnership) { - ofci->setOwnershipKind(newOwnership); - } - continue; - } - - if (auto *sei = dyn_cast(result)) { - if (sei->getOwnershipKind() == oldOwnership) { - sei->setOwnershipKind(newOwnership); - } - continue; - } - - if (auto *mvir = dyn_cast(result)) { - if (mvir->getOwnershipKind() == oldOwnership) { - mvir->setOwnershipKind(newOwnership); - } - continue; - } - - llvm_unreachable("unhandled forwarding instruction?!"); - } -} - -void OwnershipLiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() && { - while (!ownershipForwardingUses.empty()) { - auto *i = ownershipForwardingUses.back()->getUser(); - ownershipForwardingUses = ownershipForwardingUses.drop_back(); - convertInstructionOwnership(i, ValueOwnershipKind::Owned, - ValueOwnershipKind::Guaranteed); - } -} - -void OwnershipLiveRange::convertToGuaranteedAndRAUW( - SILValue newGuaranteedValue, InstModCallbacks callbacks) && { - auto *value = cast(introducer.value); - while (!destroyingUses.empty()) { - auto *d = destroyingUses.back(); - destroyingUses = destroyingUses.drop_back(); - callbacks.deleteInst(d->getUser()); - ++NumEliminatedInsts; - } - - callbacks.eraseAndRAUWSingleValueInst(value, newGuaranteedValue); - - // Then change all of our guaranteed forwarding insts to have guaranteed - // ownership kind instead of what ever they previously had (ignoring trivial - // results); - std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); -} - -// TODO: If this is useful, move onto OwnedValueIntroducer itself? -static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) { - switch (introducer.kind) { - case OwnedValueIntroducerKind::Phi: { - auto *phiArg = cast(introducer.value); - phiArg->setOwnershipKind(ValueOwnershipKind::Guaranteed); - return phiArg; - } - case OwnedValueIntroducerKind::Struct: { - auto *si = cast(introducer.value); - si->setOwnershipKind(ValueOwnershipKind::Guaranteed); - return si; - } - case OwnedValueIntroducerKind::Tuple: { - auto *ti = cast(introducer.value); - ti->setOwnershipKind(ValueOwnershipKind::Guaranteed); - return ti; - } - case OwnedValueIntroducerKind::Copy: - case OwnedValueIntroducerKind::LoadCopy: - case OwnedValueIntroducerKind::Apply: - case OwnedValueIntroducerKind::BeginApply: - case OwnedValueIntroducerKind::TryApply: - case OwnedValueIntroducerKind::LoadTake: - case OwnedValueIntroducerKind::FunctionArgument: - case OwnedValueIntroducerKind::PartialApplyInit: - case OwnedValueIntroducerKind::AllocBoxInit: - case OwnedValueIntroducerKind::AllocRefInit: - return SILValue(); - } -} - -void OwnershipLiveRange::convertJoinedLiveRangePhiToGuaranteed( - DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, - InstModCallbacks callbacks) && { - - // First convert the phi value itself to be guaranteed. - SILValue phiValue = convertIntroducerToGuaranteed(introducer); - - // Then insert end_borrows at each of our destroys if we are consuming. We - // have to convert the phi to guaranteed first since otherwise, the ownership - // check when we create the end_borrows will trigger. - if (introducer.hasConsumingGuaranteedOperands()) { - insertEndBorrowsAtDestroys(phiValue, deadEndBlocks, scratch); - } - - // Then eliminate all of the destroys... - while (!destroyingUses.empty()) { - auto *d = destroyingUses.back(); - destroyingUses = destroyingUses.drop_back(); - callbacks.deleteInst(d->getUser()); - ++NumEliminatedInsts; - } - - // and change all of our guaranteed forwarding insts to have guaranteed - // ownership kind instead of what ever they previously had (ignoring trivial - // results); - std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); -} - -OwnershipLiveRange::HasConsumingUse_t -OwnershipLiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const { - // First do a quick check if we have /any/ unknown consuming - // uses. If we do not have any, return false early. - if (unknownConsumingUses.empty()) { - return HasConsumingUse_t::No; - } - - // Ok, we do have some unknown consuming uses. If we aren't assuming we are at - // the fixed point yet, just bail. - if (!assumingAtFixPoint) { - return HasConsumingUse_t::Yes; - } - - // We do not know how to handle yet cases where an owned value is used by - // multiple phi nodes. So we bail early if unknown consuming uses is > 1. - // - // TODO: Build up phi node web. - auto *op = getSingleUnknownConsumingUse(); - if (!op) { - return HasConsumingUse_t::Yes; - } - - // Make sure our single unknown consuming use is a branch inst. If not, bail, - // this is a /real/ unknown consuming use. - if (!OwnershipPhiOperand::get(op)) { - return HasConsumingUse_t::Yes; - } - - // Otherwise, setup the phi to incoming value map mapping the block arguments - // to our introducer. - return HasConsumingUse_t::YesButAllPhiArgs; -} +using namespace swift::semanticarc; //===----------------------------------------------------------------------===// // Address Written To Analysis @@ -1392,11 +763,9 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { while (!endBorrows.empty()) { auto *ebi = endBorrows.pop_back_val(); eraseInstruction(ebi); - ++NumEliminatedInsts; } eraseAndRAUWSingleValueInstruction(bbi, bbi->getOperand()); - ++NumEliminatedInsts; return true; } @@ -1612,7 +981,6 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization( // Otherwise, our copy must truly not be needed, o RAUW and convert to // guaranteed! std::move(lr).convertToGuaranteedAndRAUW(cvi->getOperand(), getCallbacks()); - ++NumEliminatedInsts; return true; } @@ -1627,7 +995,6 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue( if (auto *dvi = dyn_cast(op->getUser())) { eraseInstruction(dvi); eraseInstructionAndAddOperandsToWorklist(cvi); - NumEliminatedInsts += 2; return true; } } @@ -1651,10 +1018,8 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue( // Now that we have a truly dead live range copy value, eliminate it! while (!destroys.empty()) { eraseInstruction(destroys.pop_back_val()); - ++NumEliminatedInsts; } eraseInstructionAndAddOperandsToWorklist(cvi); - ++NumEliminatedInsts; return true; } @@ -1814,7 +1179,6 @@ bool SemanticARCOptVisitor::tryJoiningCopyValueLiveRangeWithOperand( if (canSafelyJoinSimpleRange(operand, dvi, cvi)) { eraseInstruction(dvi); eraseAndRAUWSingleValueInstruction(cvi, operand); - NumEliminatedInsts += 2; return true; } } @@ -2161,8 +1525,6 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { lr.insertEndBorrowsAtDestroys(lbi, getDeadEndBlocks(), lifetimeFrontier); std::move(lr).convertToGuaranteedAndRAUW(lbi, getCallbacks()); - ++NumEliminatedInsts; - ++NumLoadCopyConvertedToLoadBorrow; return true; } From 76d0648e60e79153b2fe79699397f1788b3bb5eb Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Sat, 29 Aug 2020 21:46:58 -0700 Subject: [PATCH 40/47] [AutoDiff] [Sema] Include certain 'let' properties in 'Differentiable' derived conformances. (#33700) In `Differentiable` derived conformances, `let` properties are currently treated as if they had `@noDerivative` and excluded from the derived `Differentiable` conformance implementation. This is limiting to properties that have a non-mutating `move(along:)` (e.g. class properties), which can be mathematically treated as differentiable variables. This patch changes the derived conformances behavior such that `let` properties will be included as differentiable variables if they have a non-mutating `move(along:)`. This unblocks the following code: ```swift final class Foo: Differentiable { let x: ClassStuff // Class type with a non-mutating 'move(along:)' // Synthesized code: // struct TangentVector { // var x: ClassStuff.TangentVector // } // ... // func move(along direction: TangentVector) { // x.move(along: direction.x) // } } ``` Resolves SR-13474 (rdar://67982207). --- include/swift/AST/DiagnosticsSema.def | 7 ++- lib/Sema/DerivedConformanceDifferentiable.cpp | 56 ++++++++++++++----- .../class_differentiable.swift | 48 ++++++++++++++-- .../struct_differentiable.swift | 49 ++++++++++++++-- .../class_differentiation.swift | 15 +++++ 5 files changed, 147 insertions(+), 28 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9374d59db3e3f..9d36c35bc7c80 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2823,15 +2823,16 @@ WARNING(differentiable_nondiff_type_implicit_noderivative_fixit,none, /*nominalCanDeriveAdditiveArithmetic*/ bool)) WARNING(differentiable_immutable_wrapper_implicit_noderivative_fixit,none, "synthesis of the 'Differentiable.move(along:)' requirement for %1 " - "requires 'wrappedValue' in property wrapper %0 to be mutable; " - "add an explicit '@noDerivative' attribute" + "requires 'wrappedValue' in property wrapper %0 to be mutable or have a " + "non-mutating 'move(along:)'; add an explicit '@noDerivative' attribute" "%select{|, or conform %1 to 'AdditiveArithmetic'}2", (/*wrapperType*/ Identifier, /*nominalName*/ Identifier, /*nominalCanDeriveAdditiveArithmetic*/ bool)) WARNING(differentiable_let_property_implicit_noderivative_fixit,none, "synthesis of the 'Differentiable.move(along:)' requirement for %0 " "requires all stored properties not marked with `@noDerivative` to be " - "mutable; use 'var' instead, or add an explicit '@noDerivative' attribute" + "mutable or have a non-mutating 'move(along:)'; use 'var' instead, or " + "add an explicit '@noDerivative' attribute " "%select{|, or conform %0 to 'AdditiveArithmetic'}1", (/*nominalName*/ Identifier, /*nominalCanDeriveAdditiveArithmetic*/ bool)) diff --git a/lib/Sema/DerivedConformanceDifferentiable.cpp b/lib/Sema/DerivedConformanceDifferentiable.cpp index 451dc2c65efad..5ab7ffc81c1ef 100644 --- a/lib/Sema/DerivedConformanceDifferentiable.cpp +++ b/lib/Sema/DerivedConformanceDifferentiable.cpp @@ -32,12 +32,37 @@ using namespace swift; +/// Return true if `move(along:)` can be invoked on the given `Differentiable`- +/// conforming property. +/// +/// If the given property is a `var`, return true because `move(along:)` can be +/// invoked regardless. Otherwise, return true if and only if the property's +/// type's 'Differentiable.move(along:)' witness is non-mutating. +static bool canInvokeMoveAlongOnProperty( + VarDecl *vd, ProtocolConformanceRef diffableConformance) { + assert(diffableConformance && "Property must conform to 'Differentiable'"); + // `var` always supports `move(along:)` since it is mutable. + if (vd->getIntroducer() == VarDecl::Introducer::Var) + return true; + // When the property is a `let`, the only case that would be supported is when + // it has a `move(along:)` protocol requirement witness that is non-mutating. + auto interfaceType = vd->getInterfaceType(); + auto &C = vd->getASTContext(); + auto witness = diffableConformance.getWitnessByName( + interfaceType, DeclName(C, C.Id_move, {C.Id_along})); + if (!witness) + return false; + auto *decl = cast(witness.getDecl()); + return decl->isNonMutating(); +} + /// Get the stored properties of a nominal type that are relevant for /// differentiation, except the ones tagged `@noDerivative`. static void -getStoredPropertiesForDifferentiation(NominalTypeDecl *nominal, DeclContext *DC, - SmallVectorImpl &result, - bool includeLetProperties = false) { +getStoredPropertiesForDifferentiation( + NominalTypeDecl *nominal, DeclContext *DC, + SmallVectorImpl &result, + bool includeLetPropertiesWithNonmutatingMoveAlong = false) { auto &C = nominal->getASTContext(); auto *diffableProto = C.getProtocol(KnownProtocolKind::Differentiable); for (auto *vd : nominal->getStoredProperties()) { @@ -53,15 +78,18 @@ getStoredPropertiesForDifferentiation(NominalTypeDecl *nominal, DeclContext *DC, // Skip stored properties with `@noDerivative` attribute. if (vd->getAttrs().hasAttribute()) continue; - // Skip `let` stored properties if requested. - // `mutating func move(along:)` cannot be synthesized to update `let` - // properties. - if (!includeLetProperties && vd->isLet()) - continue; if (vd->getInterfaceType()->hasError()) continue; auto varType = DC->mapTypeIntoContext(vd->getValueInterfaceType()); - if (!TypeChecker::conformsToProtocol(varType, diffableProto, nominal)) + auto conformance = TypeChecker::conformsToProtocol( + varType, diffableProto, nominal); + if (!conformance) + continue; + // Skip `let` stored properties with a mutating `move(along:)` if requested. + // `mutating func move(along:)` cannot be synthesized to update `let` + // properties. + if (!includeLetPropertiesWithNonmutatingMoveAlong && + !canInvokeMoveAlongOnProperty(vd, conformance)) continue; result.push_back(vd); } @@ -782,18 +810,18 @@ static void checkAndDiagnoseImplicitNoDerivative(ASTContext &Context, continue; // Check whether to diagnose stored property. auto varType = DC->mapTypeIntoContext(vd->getValueInterfaceType()); - bool conformsToDifferentiable = - !TypeChecker::conformsToProtocol(varType, diffableProto, nominal) - .isInvalid(); + auto diffableConformance = + TypeChecker::conformsToProtocol(varType, diffableProto, nominal); // If stored property should not be diagnosed, continue. - if (conformsToDifferentiable && !vd->isLet()) + if (diffableConformance && + canInvokeMoveAlongOnProperty(vd, diffableConformance)) continue; // Otherwise, add an implicit `@noDerivative` attribute. vd->getAttrs().add(new (Context) NoDerivativeAttr(/*Implicit*/ true)); auto loc = vd->getAttributeInsertionLoc(/*forModifier*/ false); assert(loc.isValid() && "Expected valid source location"); // Diagnose properties that do not conform to `Differentiable`. - if (!conformsToDifferentiable) { + if (!diffableConformance) { Context.Diags .diagnose( loc, diff --git a/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift b/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift index f4b50fb19fa21..228c782375eba 100644 --- a/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift +++ b/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift @@ -29,26 +29,62 @@ func testEmpty() { assertConformsToAdditiveArithmetic(Empty.TangentVector.self) } +protocol DifferentiableWithNonmutatingMoveAlong: Differentiable {} +extension DifferentiableWithNonmutatingMoveAlong { + func move(along _: TangentVector) {} +} + +class EmptyWithInheritedNonmutatingMoveAlong: DifferentiableWithNonmutatingMoveAlong { + typealias TangentVector = Empty.TangentVector + var zeroTangentVectorInitializer: () -> TangentVector { { .init() } } + static func proof_that_i_have_nonmutating_move_along() { + let empty = EmptyWithInheritedNonmutatingMoveAlong() + empty.move(along: .init()) + } +} + +class EmptyWrapper: Differentiable {} +func testEmptyWrapper() { + assertConformsToAdditiveArithmetic(Empty.TangentVector.self) + assertConformsToAdditiveArithmetic(EmptyWrapper.TangentVector.self) +} + // Test structs with `let` stored properties. // Derived conformances fail because `mutating func move` requires all stored // properties to be mutable. -class ImmutableStoredProperties: Differentiable { +class ImmutableStoredProperties: Differentiable { var okay: Float // expected-warning @+1 {{stored property 'nondiff' has no derivative because 'Int' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let nondiff: Int - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let diff: Float - init() { + let letClass: Empty // No error on class-bound differentiable `let` with a non-mutating 'move(along:)'. + + let letClassWithInheritedNonmutatingMoveAlong: EmptyWithInheritedNonmutatingMoveAlong + + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + let letClassGeneric: T // Error due to lack of non-mutating 'move(along:)'. + + let letClassWrappingGeneric: EmptyWrapper // No error on class-bound differentiable `let` with a non-mutating 'move(along:)'. + + init(letClassGeneric: T) { okay = 0 nondiff = 0 diff = 0 + letClass = Empty() + self.letClassGeneric = letClassGeneric + self.letClassWrappingGeneric = EmptyWrapper() } } func testImmutableStoredProperties() { - _ = ImmutableStoredProperties.TangentVector(okay: 1) + _ = ImmutableStoredProperties.TangentVector( + okay: 1, + letClass: Empty.TangentVector(), + letClassWithInheritedNonmutatingMoveAlong: Empty.TangentVector(), + letClassWrappingGeneric: EmptyWrapper.TangentVector()) } class MutableStoredPropertiesWithInitialValue: Differentiable { var x = Float(1) @@ -56,7 +92,7 @@ class MutableStoredPropertiesWithInitialValue: Differentiable { } // Test class with both an empty constructor and memberwise initializer. class AllMixedStoredPropertiesHaveInitialValue: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let x = Float(1) var y = Float(1) // Memberwise initializer should be `init(y:)` since `x` is immutable. @@ -550,7 +586,7 @@ struct Generic {} extension Generic: Differentiable where T: Differentiable {} class WrappedProperties: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable; add an explicit '@noDerivative' attribute}} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable or have a non-mutating 'move(along:)'; add an explicit '@noDerivative' attribute}} @ImmutableWrapper var immutableInt: Generic = Generic() // expected-warning @+1 {{stored property 'mutableInt' has no derivative because 'Generic' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} diff --git a/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift b/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift index e75b2730232f5..d8ec6e56c588f 100644 --- a/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift +++ b/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift @@ -11,6 +11,35 @@ func testEmpty() { assertConformsToAdditiveArithmetic(Empty.TangentVector.self) } +struct EmptyWithConcreteNonmutatingMoveAlong: Differentiable { + typealias TangentVector = Empty.TangentVector + var zeroTangentVectorInitializer: () -> TangentVector { { .init() } } + func move(along _: TangentVector) {} + static func proof_that_i_have_nonmutating_move_along() { + let empty = Self() + empty.move(along: .init()) + } +} + +protocol DifferentiableWithNonmutatingMoveAlong: Differentiable {} +extension DifferentiableWithNonmutatingMoveAlong { + func move(along _: TangentVector) {} +} + +struct EmptyWithInheritedNonmutatingMoveAlong: DifferentiableWithNonmutatingMoveAlong { + typealias TangentVector = Empty.TangentVector + var zeroTangentVectorInitializer: () -> TangentVector { { .init() } } + static func proof_that_i_have_nonmutating_move_along() { + let empty = Self() + empty.move(along: .init()) + } +} + +class EmptyClass: Differentiable {} +func testEmptyClass() { + assertConformsToAdditiveArithmetic(EmptyClass.TangentVector.self) +} + // Test interaction with `AdditiveArithmetic` derived conformances. // Previously, this crashed due to duplicate memberwise initializer synthesis. struct EmptyAdditiveArithmetic: AdditiveArithmetic, Differentiable {} @@ -21,14 +50,24 @@ struct EmptyAdditiveArithmetic: AdditiveArithmetic, Differentiable {} struct ImmutableStoredProperties: Differentiable { var okay: Float - // expected-warning @+1 {{stored property 'nondiff' has no derivative because 'Int' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute, or conform 'ImmutableStoredProperties' to 'AdditiveArithmetic'}} {{3-3=@noDerivative }} + // expected-warning @+1 {{stored property 'nondiff' has no derivative because 'Int' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let nondiff: Int - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute, or conform 'ImmutableStoredProperties' to 'AdditiveArithmetic}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let diff: Float + + let nonmutatingMoveAlongStruct: EmptyWithConcreteNonmutatingMoveAlong + + let inheritedNonmutatingMoveAlongStruct: EmptyWithInheritedNonmutatingMoveAlong + + let diffClass: EmptyClass // No error on class-bound `let` with a non-mutating `move(along:)`. } func testImmutableStoredProperties() { - _ = ImmutableStoredProperties.TangentVector(okay: 1) + _ = ImmutableStoredProperties.TangentVector( + okay: 1, + nonmutatingMoveAlongStruct: Empty.TangentVector(), + inheritedNonmutatingMoveAlongStruct: Empty.TangentVector(), + diffClass: EmptyClass.TangentVector()) } struct MutableStoredPropertiesWithInitialValue: Differentiable { var x = Float(1) @@ -36,7 +75,7 @@ struct MutableStoredPropertiesWithInitialValue: Differentiable { } // Test struct with both an empty constructor and memberwise initializer. struct AllMixedStoredPropertiesHaveInitialValue: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let x = Float(1) var y = Float(1) // Memberwise initializer should be `init(y:)` since `x` is immutable. @@ -363,7 +402,7 @@ struct Generic {} extension Generic: Differentiable where T: Differentiable {} struct WrappedProperties: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable; add an explicit '@noDerivative' attribute}} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable or have a non-mutating 'move(along:)'; add an explicit '@noDerivative' attribute}} @ImmutableWrapper var immutableInt: Generic // expected-warning @+1 {{stored property 'mutableInt' has no derivative because 'Generic' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} diff --git a/test/AutoDiff/validation-test/class_differentiation.swift b/test/AutoDiff/validation-test/class_differentiation.swift index 2ae7619486214..ee76c21ba1d87 100644 --- a/test/AutoDiff/validation-test/class_differentiation.swift +++ b/test/AutoDiff/validation-test/class_differentiation.swift @@ -524,4 +524,19 @@ ClassTests.test("ClassProperties") { gradient(at: Super(base: 2)) { foo in foo.squared }) } +ClassTests.test("LetProperties") { + final class Foo: Differentiable { + var x: Tracked + init(x: Tracked) { self.x = x } + } + final class Bar: Differentiable { + let x = Foo(x: 2) + } + let bar = Bar() + let grad = gradient(at: bar) { bar in (bar.x.x * bar.x.x).value } + expectEqual(Bar.TangentVector(x: .init(x: 6.0)), grad) + bar.move(along: grad) + expectEqual(8.0, bar.x.x) +} + runAllTests() From 76a81efac97550ea19968ab7ae4e1237da581f21 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 30 Aug 2020 01:25:54 -0400 Subject: [PATCH 41/47] ASTDemangler: Fix reconstruction of opaque result type defined in a constrained extension The mangling includes all generic parameters, even non-canonical ones. Fixes . --- lib/AST/ASTDemangler.cpp | 15 +++++---------- test/TypeDecoder/opaque_return_type.swift | 9 +++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index bf72c01eae702..d5fbfa63f56ae 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -200,21 +200,16 @@ createSubstitutionMapFromGenericArgs(GenericSignature genericSig, if (!genericSig) return SubstitutionMap(); - SmallVector genericParams; - genericSig->forEachParam([&](GenericTypeParamType *gp, bool canonical) { - if (canonical) - genericParams.push_back(gp); - }); - if (genericParams.size() != args.size()) + if (genericSig->getGenericParams().size() != args.size()) return SubstitutionMap(); return SubstitutionMap::get( genericSig, [&](SubstitutableType *t) -> Type { - for (unsigned i = 0, e = genericParams.size(); i < e; ++i) { - if (t->isEqual(genericParams[i])) - return args[i]; - } + auto *gp = cast(t); + unsigned ordinal = genericSig->getGenericParamOrdinal(gp); + if (ordinal < args.size()) + return args[ordinal]; return Type(); }, LookUpConformanceInModule(moduleDecl)); diff --git a/test/TypeDecoder/opaque_return_type.swift b/test/TypeDecoder/opaque_return_type.swift index 94b139ad48d1e..327e4860570d8 100644 --- a/test/TypeDecoder/opaque_return_type.swift +++ b/test/TypeDecoder/opaque_return_type.swift @@ -12,6 +12,12 @@ var prop: some P { return 0 } func bar() -> some Sequence { return [] } +struct G {} + +extension G where T == Int { + var baz: some P { return 0 } +} + // DEMANGLE: $s18opaque_return_type3fooQryFQOyQo_ // CHECK: some P @@ -21,5 +27,8 @@ func bar() -> some Sequence { return [] } // DEMANGLE: $s18opaque_return_type3barQryFQOyQo_ // CHECK: some Sequence +// DEMANGLE: $s18opaque_return_type1GVAASiRszlE3bazQrvpQOySi_Qo_ +// CHECK: some P + // DEMANGLE: $s18opaque_return_type3barQryFQOyQo_7ElementSTQxD // CHECK: (some Sequence).Element From 96feb2dedda55fd54c8b1c724ae43e2002f62436 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 27 Aug 2020 23:54:33 -0700 Subject: [PATCH 42/47] [ownership] Extract out the main visitor SemanticARCOptVisitor from SemanticARCOpts.h into its own header. This is so I can move individual sub-optimizations on SemanticARCOptVisitor into their own files. So for instance, there will be one file containing the load [copy] optimization and another containing the phi optimization, etc. --- .../SemanticARC/SemanticARCOptVisitor.h | 252 ++++++++++++++++++ .../SemanticARC/SemanticARCOpts.cpp | 225 +--------------- 2 files changed, 257 insertions(+), 220 deletions(-) create mode 100644 lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h b/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h new file mode 100644 index 0000000000000..fce6908ae6252 --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h @@ -0,0 +1,252 @@ +//===--- SemanticARCOptVisitor.h ------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H +#define SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H + +#include "swift/Basic/BlotSetVector.h" +#include "swift/Basic/FrozenMultiMap.h" +#include "swift/Basic/MultiMapCache.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/SILVisitor.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" + +namespace swift { +namespace semanticarc { + +bool constructCacheValue( + SILValue initialValue, + SmallVectorImpl &wellBehavedWriteAccumulator); + +/// A visitor that optimizes ownership instructions and eliminates any trivially +/// dead code that results after optimization. It uses an internal worklist that +/// is initialized on construction with targets to avoid iterator invalidation +/// issues. Rather than revisit the entire CFG like SILCombine and other +/// visitors do, we maintain a visitedSinceLastMutation list to ensure that we +/// revisit all interesting instructions in between mutations. +struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor + : SILInstructionVisitor { + /// Our main worklist. We use this after an initial run through. + SmallBlotSetVector worklist; + + /// A set of values that we have visited since the last mutation. We use this + /// to ensure that we do not visit values twice without mutating. + /// + /// This is specifically to ensure that we do not go into an infinite loop + /// when visiting phi nodes. + SmallBlotSetVector visitedSinceLastMutation; + + SILFunction &F; + Optional TheDeadEndBlocks; + ValueLifetimeAnalysis::Frontier lifetimeFrontier; + SmallMultiMapCache addressToExhaustiveWriteListCache; + + /// Are we assuming that we reached a fix point and are re-processing to + /// prepare to use the phiToIncomingValueMultiMap. + bool assumingAtFixedPoint = false; + + /// A map from a value that acts as a "joined owned introducer" in the def-use + /// graph. + /// + /// A "joined owned introducer" is a value with owned ownership whose + /// ownership is derived from multiple non-trivial owned operands of a related + /// instruction. Some examples are phi arguments, tuples, structs. Naturally, + /// all of these instructions must be non-unary instructions and only have + /// this property if they have multiple operands that are non-trivial. + /// + /// In such a case, we can not just treat them like normal forwarding concepts + /// since we can only eliminate optimize such a value if we are able to reason + /// about all of its operands together jointly. This is not amenable to a + /// small peephole analysis. + /// + /// Instead, as we perform the peephole analysis, using the multimap, we map + /// each joined owned value introducer to the set of its @owned operands that + /// we thought we could convert to guaranteed only if we could do the same to + /// the joined owned value introducer. Then once we finish performing + /// peepholes, we iterate through the map and see if any of our joined phi + /// ranges had all of their operand's marked with this property by iterating + /// over the multimap. Since we are dealing with owned values and we know that + /// our LiveRange can not see through joined live ranges, we know that we + /// should only be able to have a single owned value introducer for each + /// consumed operand. + FrozenMultiMap joinedOwnedIntroducerToConsumedOperands; + + /// If set to true, then we should only run cheap optimizations that do not + /// build up data structures or analyze code in depth. + /// + /// As an example, we do not do load [copy] optimizations here since they + /// generally involve more complex analysis, but simple peepholes of + /// copy_values we /do/ allow. + bool onlyGuaranteedOpts; + + using FrozenMultiMapRange = + decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange; + + explicit SemanticARCOptVisitor(SILFunction &F, bool onlyGuaranteedOpts) + : F(F), addressToExhaustiveWriteListCache(constructCacheValue), + onlyGuaranteedOpts(onlyGuaranteedOpts) {} + + DeadEndBlocks &getDeadEndBlocks() { + if (!TheDeadEndBlocks) + TheDeadEndBlocks.emplace(&F); + return *TheDeadEndBlocks; + } + + /// Given a single value instruction, RAUW it with newValue, add newValue to + /// the worklist, and then call eraseInstruction on i. + void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i, + SILValue newValue) { + worklist.insert(newValue); + for (auto *use : i->getUses()) { + for (SILValue result : use->getUser()->getResults()) { + worklist.insert(result); + } + } + i->replaceAllUsesWith(newValue); + eraseInstructionAndAddOperandsToWorklist(i); + } + + /// Add all operands of i to the worklist and then call eraseInstruction on + /// i. Assumes that the instruction doesnt have users. + void eraseInstructionAndAddOperandsToWorklist(SILInstruction *i) { + // Then copy all operands into the worklist for future processing. + for (SILValue v : i->getOperandValues()) { + worklist.insert(v); + } + eraseInstruction(i); + } + + /// Pop values off of visitedSinceLastMutation, adding .some values to the + /// worklist. + void drainVisitedSinceLastMutationIntoWorklist() { + while (!visitedSinceLastMutation.empty()) { + Optional nextValue = visitedSinceLastMutation.pop_back_val(); + if (!nextValue.hasValue()) { + continue; + } + worklist.insert(*nextValue); + } + } + + /// Remove all results of the given instruction from the worklist and then + /// erase the instruction. Assumes that the instruction does not have any + /// users left. + void eraseInstruction(SILInstruction *i) { + // Remove all SILValues of the instruction from the worklist and then erase + // the instruction. + for (SILValue result : i->getResults()) { + worklist.erase(result); + visitedSinceLastMutation.erase(result); + } + i->eraseFromParent(); + + // Add everything else from visitedSinceLastMutation to the worklist. + drainVisitedSinceLastMutationIntoWorklist(); + } + + InstModCallbacks getCallbacks() { + return InstModCallbacks( + [this](SILInstruction *inst) { eraseInstruction(inst); }, + [](SILInstruction *) {}, [](SILValue, SILValue) {}, + [this](SingleValueInstruction *i, SILValue value) { + eraseAndRAUWSingleValueInstruction(i, value); + }); + } + + /// The default visitor. + bool visitSILInstruction(SILInstruction *i) { + assert(!isGuaranteedForwardingInst(i) && + "Should have forwarding visitor for all ownership forwarding " + "instructions"); + return false; + } + + bool visitCopyValueInst(CopyValueInst *cvi); + bool visitBeginBorrowInst(BeginBorrowInst *bbi); + bool visitLoadInst(LoadInst *li); + static bool shouldVisitInst(SILInstruction *i) { + switch (i->getKind()) { + default: + return false; + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::LoadInst: + return true; + } + } + +#define FORWARDING_INST(NAME) \ + bool visit##NAME##Inst(NAME##Inst *cls) { \ + for (SILValue v : cls->getResults()) { \ + worklist.insert(v); \ + } \ + return false; \ + } + FORWARDING_INST(Tuple) + FORWARDING_INST(Struct) + FORWARDING_INST(Enum) + FORWARDING_INST(OpenExistentialRef) + FORWARDING_INST(Upcast) + FORWARDING_INST(UncheckedRefCast) + FORWARDING_INST(ConvertFunction) + FORWARDING_INST(RefToBridgeObject) + FORWARDING_INST(BridgeObjectToRef) + FORWARDING_INST(UnconditionalCheckedCast) + FORWARDING_INST(UncheckedEnumData) + FORWARDING_INST(MarkUninitialized) + FORWARDING_INST(SelectEnum) + FORWARDING_INST(DestructureStruct) + FORWARDING_INST(DestructureTuple) + FORWARDING_INST(TupleExtract) + FORWARDING_INST(StructExtract) + FORWARDING_INST(OpenExistentialValue) + FORWARDING_INST(OpenExistentialBoxValue) + FORWARDING_INST(MarkDependence) + FORWARDING_INST(InitExistentialRef) + FORWARDING_INST(DifferentiableFunction) + FORWARDING_INST(LinearFunction) + FORWARDING_INST(DifferentiableFunctionExtract) + FORWARDING_INST(LinearFunctionExtract) +#undef FORWARDING_INST + +#define FORWARDING_TERM(NAME) \ + bool visit##NAME##Inst(NAME##Inst *cls) { \ + for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ + for (SILValue v : succValues) { \ + worklist.insert(v); \ + } \ + } \ + return false; \ + } + + FORWARDING_TERM(SwitchEnum) + FORWARDING_TERM(CheckedCastBranch) + FORWARDING_TERM(Branch) +#undef FORWARDING_TERM + + bool isWrittenTo(LoadInst *li, const OwnershipLiveRange &lr); + + bool processWorklist(); + bool optimize(); + + bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi); + bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi); + bool tryJoiningCopyValueLiveRangeWithOperand(CopyValueInst *cvi); + bool performPostPeepholeOwnedArgElimination(); +}; + +} // namespace semanticarc +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 383ec145a6455..b501ed7e64cff 100644 --- a/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -14,8 +14,9 @@ #include "swift/Basic/LLVM.h" -#include "OwnershipPhiOperand.h" #include "OwnershipLiveRange.h" +#include "OwnershipPhiOperand.h" +#include "SemanticARCOptVisitor.h" #include "swift/Basic/BlotSetVector.h" #include "swift/Basic/FrozenMultiMap.h" @@ -50,9 +51,9 @@ using namespace swift::semanticarc; /// Returns true if we were able to ascertain that either the initialValue has /// no write uses or all of the write uses were writes that we could understand. -static bool -constructCacheValue(SILValue initialValue, - SmallVectorImpl &wellBehavedWriteAccumulator) { +bool swift::semanticarc::constructCacheValue( + SILValue initialValue, + SmallVectorImpl &wellBehavedWriteAccumulator) { SmallVector worklist(initialValue->getUses()); while (!worklist.empty()) { @@ -186,222 +187,6 @@ constructCacheValue(SILValue initialValue, namespace { -/// A visitor that optimizes ownership instructions and eliminates any trivially -/// dead code that results after optimization. It uses an internal worklist that -/// is initialized on construction with targets to avoid iterator invalidation -/// issues. Rather than revisit the entire CFG like SILCombine and other -/// visitors do, we maintain a visitedSinceLastMutation list to ensure that we -/// revisit all interesting instructions in between mutations. -struct SemanticARCOptVisitor - : SILInstructionVisitor { - /// Our main worklist. We use this after an initial run through. - SmallBlotSetVector worklist; - - /// A set of values that we have visited since the last mutation. We use this - /// to ensure that we do not visit values twice without mutating. - /// - /// This is specifically to ensure that we do not go into an infinite loop - /// when visiting phi nodes. - SmallBlotSetVector visitedSinceLastMutation; - - SILFunction &F; - Optional TheDeadEndBlocks; - ValueLifetimeAnalysis::Frontier lifetimeFrontier; - SmallMultiMapCache addressToExhaustiveWriteListCache; - - /// Are we assuming that we reached a fix point and are re-processing to - /// prepare to use the phiToIncomingValueMultiMap. - bool assumingAtFixedPoint = false; - - /// A map from a value that acts as a "joined owned introducer" in the def-use - /// graph. - /// - /// A "joined owned introducer" is a value with owned ownership whose - /// ownership is derived from multiple non-trivial owned operands of a related - /// instruction. Some examples are phi arguments, tuples, structs. Naturally, - /// all of these instructions must be non-unary instructions and only have - /// this property if they have multiple operands that are non-trivial. - /// - /// In such a case, we can not just treat them like normal forwarding concepts - /// since we can only eliminate optimize such a value if we are able to reason - /// about all of its operands together jointly. This is not amenable to a - /// small peephole analysis. - /// - /// Instead, as we perform the peephole analysis, using the multimap, we map - /// each joined owned value introducer to the set of its @owned operands that - /// we thought we could convert to guaranteed only if we could do the same to - /// the joined owned value introducer. Then once we finish performing - /// peepholes, we iterate through the map and see if any of our joined phi - /// ranges had all of their operand's marked with this property by iterating - /// over the multimap. Since we are dealing with owned values and we know that - /// our LiveRange can not see through joined live ranges, we know that we - /// should only be able to have a single owned value introducer for each - /// consumed operand. - FrozenMultiMap joinedOwnedIntroducerToConsumedOperands; - - /// If set to true, then we should only run cheap optimizations that do not - /// build up data structures or analyze code in depth. - /// - /// As an example, we do not do load [copy] optimizations here since they - /// generally involve more complex analysis, but simple peepholes of - /// copy_values we /do/ allow. - bool onlyGuaranteedOpts; - - using FrozenMultiMapRange = - decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange; - - explicit SemanticARCOptVisitor(SILFunction &F, bool onlyGuaranteedOpts) - : F(F), addressToExhaustiveWriteListCache(constructCacheValue), - onlyGuaranteedOpts(onlyGuaranteedOpts) {} - - DeadEndBlocks &getDeadEndBlocks() { - if (!TheDeadEndBlocks) - TheDeadEndBlocks.emplace(&F); - return *TheDeadEndBlocks; - } - - /// Given a single value instruction, RAUW it with newValue, add newValue to - /// the worklist, and then call eraseInstruction on i. - void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i, SILValue newValue) { - worklist.insert(newValue); - for (auto *use : i->getUses()) { - for (SILValue result : use->getUser()->getResults()) { - worklist.insert(result); - } - } - i->replaceAllUsesWith(newValue); - eraseInstructionAndAddOperandsToWorklist(i); - } - - /// Add all operands of i to the worklist and then call eraseInstruction on - /// i. Assumes that the instruction doesnt have users. - void eraseInstructionAndAddOperandsToWorklist(SILInstruction *i) { - // Then copy all operands into the worklist for future processing. - for (SILValue v : i->getOperandValues()) { - worklist.insert(v); - } - eraseInstruction(i); - } - - /// Pop values off of visitedSinceLastMutation, adding .some values to the - /// worklist. - void drainVisitedSinceLastMutationIntoWorklist() { - while (!visitedSinceLastMutation.empty()) { - Optional nextValue = visitedSinceLastMutation.pop_back_val(); - if (!nextValue.hasValue()) { - continue; - } - worklist.insert(*nextValue); - } - } - - /// Remove all results of the given instruction from the worklist and then - /// erase the instruction. Assumes that the instruction does not have any - /// users left. - void eraseInstruction(SILInstruction *i) { - // Remove all SILValues of the instruction from the worklist and then erase - // the instruction. - for (SILValue result : i->getResults()) { - worklist.erase(result); - visitedSinceLastMutation.erase(result); - } - i->eraseFromParent(); - - // Add everything else from visitedSinceLastMutation to the worklist. - drainVisitedSinceLastMutationIntoWorklist(); - } - - InstModCallbacks getCallbacks() { - return InstModCallbacks( - [this](SILInstruction *inst) { eraseInstruction(inst); }, - [](SILInstruction *) {}, [](SILValue, SILValue) {}, - [this](SingleValueInstruction *i, SILValue value) { - eraseAndRAUWSingleValueInstruction(i, value); - }); - } - - /// The default visitor. - bool visitSILInstruction(SILInstruction *i) { - assert(!isGuaranteedForwardingInst(i) && - "Should have forwarding visitor for all ownership forwarding " - "instructions"); - return false; - } - - bool visitCopyValueInst(CopyValueInst *cvi); - bool visitBeginBorrowInst(BeginBorrowInst *bbi); - bool visitLoadInst(LoadInst *li); - static bool shouldVisitInst(SILInstruction *i) { - switch (i->getKind()) { - default: - return false; - case SILInstructionKind::CopyValueInst: - case SILInstructionKind::BeginBorrowInst: - case SILInstructionKind::LoadInst: - return true; - } - } - -#define FORWARDING_INST(NAME) \ - bool visit##NAME##Inst(NAME##Inst *cls) { \ - for (SILValue v : cls->getResults()) { \ - worklist.insert(v); \ - } \ - return false; \ - } - FORWARDING_INST(Tuple) - FORWARDING_INST(Struct) - FORWARDING_INST(Enum) - FORWARDING_INST(OpenExistentialRef) - FORWARDING_INST(Upcast) - FORWARDING_INST(UncheckedRefCast) - FORWARDING_INST(ConvertFunction) - FORWARDING_INST(RefToBridgeObject) - FORWARDING_INST(BridgeObjectToRef) - FORWARDING_INST(UnconditionalCheckedCast) - FORWARDING_INST(UncheckedEnumData) - FORWARDING_INST(MarkUninitialized) - FORWARDING_INST(SelectEnum) - FORWARDING_INST(DestructureStruct) - FORWARDING_INST(DestructureTuple) - FORWARDING_INST(TupleExtract) - FORWARDING_INST(StructExtract) - FORWARDING_INST(OpenExistentialValue) - FORWARDING_INST(OpenExistentialBoxValue) - FORWARDING_INST(MarkDependence) - FORWARDING_INST(InitExistentialRef) - FORWARDING_INST(DifferentiableFunction) - FORWARDING_INST(LinearFunction) - FORWARDING_INST(DifferentiableFunctionExtract) - FORWARDING_INST(LinearFunctionExtract) -#undef FORWARDING_INST - -#define FORWARDING_TERM(NAME) \ - bool visit##NAME##Inst(NAME##Inst *cls) { \ - for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ - for (SILValue v : succValues) { \ - worklist.insert(v); \ - } \ - } \ - return false; \ - } - - FORWARDING_TERM(SwitchEnum) - FORWARDING_TERM(CheckedCastBranch) - FORWARDING_TERM(Branch) -#undef FORWARDING_TERM - - bool isWrittenTo(LoadInst *li, const OwnershipLiveRange &lr); - - bool processWorklist(); - bool optimize(); - - bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi); - bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi); - bool tryJoiningCopyValueLiveRangeWithOperand(CopyValueInst *cvi); - bool performPostPeepholeOwnedArgElimination(); -}; - } // end anonymous namespace static llvm::cl::opt From 421bce3de7d400348db3540d207a0b2ca03a19d3 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sun, 30 Aug 2020 03:06:14 -0700 Subject: [PATCH 43/47] Disable lldb tests in macOS nightly toolchain package (67923799) --- utils/build-presets.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 0892aee12e7c8..b1fe751b1ac4f 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1303,6 +1303,8 @@ mixin-preset= mixin_osx_package_test mixin_lightweight_assertions,no-stdlib-asserts +# SKIP LLDB TESTS (67923799) +skip-test-lldb [preset: buildbot_osx_package,no_assertions] mixin-preset= @@ -1313,8 +1315,6 @@ dash-dash no-assertions -# SKIP LLDB TESTS (67923799) -skip-test-lldb [preset: buildbot_osx_package,no_assertions,lto] mixin-preset=buildbot_osx_package,no_assertions From f326927f458bfadf46eb2a780ff71029c49ed91f Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 28 Aug 2020 15:58:11 +0300 Subject: [PATCH 44/47] CSGen: Infer generic arguments in typed placeholders --- lib/Sema/CSGen.cpp | 5 +++-- test/Sema/editor_placeholders.swift | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 496052c162432..e4fab384e8efa 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3273,9 +3273,10 @@ namespace { Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) { if (auto *placeholderRepr = E->getPlaceholderTypeRepr()) { // Just resolve the referenced type. - // FIXME: The type reference needs to be opened into context. return resolveTypeReferenceInExpression( - placeholderRepr, TypeResolverContext::InExpression, nullptr); + placeholderRepr, TypeResolverContext::InExpression, + // Introduce type variables for unbound generics. + OpenUnboundGenericType(CS, CS.getConstraintLocator(E))); } auto locator = CS.getConstraintLocator(E); diff --git a/test/Sema/editor_placeholders.swift b/test/Sema/editor_placeholders.swift index dedeb59a440db..cf14d23b74fd2 100644 --- a/test/Sema/editor_placeholders.swift +++ b/test/Sema/editor_placeholders.swift @@ -2,6 +2,7 @@ func foo(_ x: Int) -> Int {} func foo(_ x: Float) -> Float {} +func foo(_ t: T) -> T {} var v = foo(<#T##x: Float##Float#>) // expected-error {{editor placeholder}} v = "" // expected-error {{cannot assign value of type 'String' to type 'Float'}} @@ -36,3 +37,6 @@ func test_ambiguity_with_placeholders(pairs: [(rank: Int, count: Int)]) -> Bool // expected-error@-1 {{editor placeholder in source file}} // expected-error@-2 {{ambiguous use of 'subscript(_:)'}} } + +let unboundInPlaceholder1: Array = <#T##Array#> // expected-error{{editor placeholder in source file}} +let unboundInPlaceholder2: Array = foo(<#T##t: Array##Array#>) // expected-error{{editor placeholder in source file}} From 6b1b8377f8ad01380f2398b0dd109a13cd67d1ba Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 28 Aug 2020 17:18:33 +0300 Subject: [PATCH 45/47] [NFC] CSGen: Tighten up resolveTypeReferenceInExpression Now that all sites have been refactored to pass a dedicated OpenUnboundGenericType callable, we can consistently move the construction of the opener into resolveTypeReferenceInExpression and have it take a ConstraintLocatorBuilder parameter instead --- lib/Sema/CSGen.cpp | 83 ++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 51 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index e4fab384e8efa..c3450ad9f39fd 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1328,17 +1328,11 @@ namespace { Type resolveTypeReferenceInExpression(TypeRepr *repr, TypeResolverContext resCtx, - OpenUnboundGenericTypeFn unboundTyOpener) { - if (!unboundTyOpener) { - unboundTyOpener = [](auto unboundTy) { - // FIXME: Don't let unbound generic types escape type resolution. - // For now, just return the unbound generic type. - return unboundTy; - }; - } - const auto result = - TypeResolution::forContextual(CS.DC, resCtx, unboundTyOpener) - .resolveType(repr); + const ConstraintLocatorBuilder &locator) { + // Introduce type variables for unbound generics. + const auto opener = OpenUnboundGenericType(CS, locator); + const auto result = TypeResolution::forContextual(CS.DC, resCtx, opener) + .resolveType(repr); if (result->hasError()) { return Type(); } @@ -1364,8 +1358,7 @@ namespace { auto *repr = E->getTypeRepr(); assert(repr && "Explicit node has no type repr!"); type = resolveTypeReferenceInExpression( - repr, TypeResolverContext::InExpression, - OpenUnboundGenericType(CS, locator)); + repr, TypeResolverContext::InExpression, locator); } if (!type || type->hasError()) return Type(); @@ -2067,10 +2060,8 @@ namespace { const auto resolvedTy = resolveTypeReferenceInExpression( closure->getExplicitResultTypeRepr(), TypeResolverContext::InExpression, - // Introduce type variables for unbound generics. - OpenUnboundGenericType( - CS, CS.getConstraintLocator( - closure, ConstraintLocator::ClosureResult))); + CS.getConstraintLocator(closure, + ConstraintLocator::ClosureResult)); if (resolvedTy) return resolvedTy; } @@ -2354,9 +2345,7 @@ namespace { const Type castType = resolveTypeReferenceInExpression( isPattern->getCastTypeRepr(), TypeResolverContext::InExpression, - OpenUnboundGenericType(CS, - locator.withPathElement( - LocatorPathElt::PatternMatch(pattern)))); + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); if (!castType) return Type(); auto *subPattern = isPattern->getSubPattern(); @@ -2407,32 +2396,31 @@ namespace { FunctionRefKind functionRefKind = FunctionRefKind::Compound; if (enumPattern->getParentType() || enumPattern->getParentTypeRepr()) { // Resolve the parent type. - Type parentType = [&]() -> Type { - if (auto preTy = enumPattern->getParentType()) { - return preTy; + const auto parentType = [&] { + auto *const patternMatchLoc = CS.getConstraintLocator( + locator, {LocatorPathElt::PatternMatch(pattern), + ConstraintLocator::ParentType}); + + // FIXME: Sometimes the parent type is realized eagerly in + // ResolvePattern::visitUnresolvedDotExpr, so we have to open it + // ex post facto. Remove this once we learn how to resolve patterns + // while generating constraints to keep the opening of generic types + // contained within the type resolver. + if (const auto preresolvedTy = enumPattern->getParentType()) { + const auto openedTy = + CS.openUnboundGenericTypes(preresolvedTy, patternMatchLoc); + assert(openedTy); + return openedTy; } + return resolveTypeReferenceInExpression( enumPattern->getParentTypeRepr(), - TypeResolverContext::InExpression, [](auto unboundTy) { - // FIXME: We ought to pass an OpenUnboundGenericType object - // rather than calling CS.openUnboundGenericType below, but - // sometimes the parent type is resolved eagerly in - // ResolvePattern::visitUnresolvedDotExpr, letting unbound - // generics escape. - return unboundTy; - }); + TypeResolverContext::InExpression, patternMatchLoc); }(); if (!parentType) return Type(); - parentType = CS.openUnboundGenericTypes( - parentType, CS.getConstraintLocator( - locator, {LocatorPathElt::PatternMatch(pattern), - ConstraintLocator::ParentType})); - - assert(parentType); - // Perform member lookup into the parent's metatype. Type parentMetaType = MetatypeType::get(parentType); CS.addValueMemberConstraint( @@ -2976,8 +2964,7 @@ namespace { // Validate the resulting type. const auto toType = resolveTypeReferenceInExpression( repr, TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3003,8 +2990,7 @@ namespace { auto *const repr = expr->getCastTypeRepr(); const auto toType = resolveTypeReferenceInExpression( repr, TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3036,8 +3022,7 @@ namespace { auto *const repr = expr->getCastTypeRepr(); const auto toType = resolveTypeReferenceInExpression( repr, TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3064,8 +3049,7 @@ namespace { auto &ctx = CS.getASTContext(); const auto toType = resolveTypeReferenceInExpression( expr->getCastTypeRepr(), TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3275,8 +3259,7 @@ namespace { // Just resolve the referenced type. return resolveTypeReferenceInExpression( placeholderRepr, TypeResolverContext::InExpression, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(E))); + CS.getConstraintLocator(E)); } auto locator = CS.getConstraintLocator(E); @@ -3347,9 +3330,7 @@ namespace { // If a root type was explicitly given, then resolve it now. if (auto rootRepr = E->getRootType()) { const auto rootObjectTy = resolveTypeReferenceInExpression( - rootRepr, TypeResolverContext::InExpression, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, locator)); + rootRepr, TypeResolverContext::InExpression, locator); if (!rootObjectTy || rootObjectTy->hasError()) return Type(); From 88e26c019fbdb8b4f212dc4c001d60683a3e6b1d Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 30 Aug 2020 21:21:51 -0300 Subject: [PATCH 46/47] [NFC] Correcting minor typo on a CSDiagnostics comment --- lib/Sema/CSDiagnostics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ed980fef21cb6..2ccc3a88d63d5 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -643,7 +643,7 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( void GenericArgumentsMismatchFailure::emitNoteForMismatch(int position) { auto *locator = getLocator(); - // Since there could be implicit conversions assoicated with argument + // Since there could be implicit conversions associated with argument // to parameter conversions, let's use parameter type as a source of // generic parameter information. auto paramSourceTy = From 294bde951b2b735a45ccd311d867d094db05c318 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 31 Aug 2020 08:46:24 -0400 Subject: [PATCH 47/47] Documentation fix: remainder matches sign of lhs, not rhs. --- stdlib/public/core/Integers.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 7c637c6fe7bc9..1b7ba775989ff 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -1211,7 +1211,7 @@ public protocol BinaryInteger : /// /// - Parameter rhs: The value to divide this value by. /// - Returns: A tuple containing the quotient and remainder of this value - /// divided by `rhs`. The remainder has the same sign as `rhs`. + /// divided by `rhs`. The remainder has the same sign as `lhs`. func quotientAndRemainder(dividingBy rhs: Self) -> (quotient: Self, remainder: Self)