From c97a0cf2d639154fd21a426de82cf5adf25fd79a Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 22 Jan 2020 14:10:57 -0800 Subject: [PATCH 01/69] [sil.rst] Clarify documentation around partial_apply closure contexts/closed over parameters. --- docs/SIL.rst | 90 +++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/docs/SIL.rst b/docs/SIL.rst index 4358e2ec12f3a..256b14beb74f0 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -3495,46 +3495,8 @@ partial_apply // %r will be of the substituted thick function type $(Z'...) -> R' Creates a closure by partially applying the function ``%0`` to a partial -sequence of its arguments. In the instruction syntax, the type of the callee is -specified after the argument list; the types of the argument and of the defined -value are derived from the function type of the callee. If the ``partial_apply`` -has an escaping function type (not ``[on_stack]``) the closure context will be -allocated with retain count 1 and initialized to contain the values ``%1``, -``%2``, etc. The closed-over values will not be retained; that must be done -separately before the ``partial_apply``. The closure does however take ownership -of the partially applied arguments (except for ``@inout_aliasable`` parameters); -when the closure reference count reaches zero, the contained values will be -destroyed. If the ``partial_apply`` has a ``@noescape`` function type -(``partial_apply [on_stack]``) the closure context is allocated on the stack and -initialized to contain the closed-over values. The closed-over values are not -retained, lifetime of the closed-over values must be managed separately. The -lifetime of the stack context of a ``partial_apply [on_stack]`` must be -terminated with a ``dealloc_stack``. - -If the callee is generic, all of its generic parameters must be bound by the -given substitution list. The arguments are given with these generic -substitutions applied, and the resulting closure is of concrete function -type with the given substitutions applied. The generic parameters themselves -cannot be partially applied; all of them must be bound. The result is always -a concrete function. - -If an address argument has ``@inout_aliasable`` convention, the closure -obtained from ``partial_apply`` will not own its underlying value. -The ``@inout_aliasable`` parameter convention is used when a ``@noescape`` -closure captures an ``inout`` argument. - -TODO: The instruction, when applied to a generic function, -currently implicitly performs abstraction difference transformations enabled -by the given substitutions, such as promoting address-only arguments and returns -to register arguments. This should be fixed. - -By default, ``partial_apply`` creates a closure whose invocation takes ownership -of the context, meaning that a call implicitly releases the closure. The -``[callee_guaranteed]`` change this to a caller-guaranteed model, where the -caller promises not to release the closure while the function is being called. - -This instruction is used to implement both curry thunks and closures. A -curried function in Swift:: +sequence of its arguments. This instruction is used to implement both curry +thunks and closures. A curried function in Swift:: func foo(_ a:A)(b:B)(c:C)(d:D) -> E { /* body of foo */ } @@ -3607,6 +3569,54 @@ lowers to an uncurried entry point and is curried in the enclosing function:: return %ret : $Int } +**Ownership Semantics of Closure Context during Invocation**: By default, an +escaping ``partial_apply`` (``partial_apply`` without ``[on_stack]]`` creates a +closure whose invocation takes ownership of the context, meaning that a call +implicitly releases the closure. If the ``partial_apply`` is marked with the +flag ``[callee_guaranteed]`` the invocation instead uses a caller-guaranteed +model, where the caller promises not to release the closure while the function +is being called. + +**Captured Value Ownership Semantics**: In the instruction syntax, the type of +the callee is specified after the argument list; the types of the argument and +of the defined value are derived from the function type of the callee. Even so, +the ownership requirements of the partial apply are not the same as that of the +callee function (and thus said signature). Instead: + +1. If the ``partial_apply`` has a ``@noescape`` function type (``partial_apply + [on_stack]``) the closure context is allocated on the stack and is + initialized to contain the closed-over values without taking ownership of + those values. The closed-over values are not retained and the lifetime of the + closed-over values must be managed by other instruction independently of the + ``partial_apply``. The lifetime of the stack context of a ``partial_apply + [on_stack]`` must be terminated with a ``dealloc_stack``. + +2. If the ``partial_apply`` has an escaping function type (not ``[on_stack]``) + then the closure context will be heap allocated with a retain count of 1. Any + closed over parameters (except for ``@inout`` parameters) will be consumed by + the partial_apply. This ensures that no matter when the ``partial_apply`` is + called, the captured arguments are alive. When the closure context's + reference count reaches zero, the contained values are destroyed. If the + callee requires an owned parameter, then the implicit partial_apply forwarder + created by IRGen will copy the underlying argument and pass it to the callee. + +3. If an address argument has ``@inout_aliasable`` convention, the closure + obtained from ``partial_apply`` will not own its underlying value. The + ``@inout_aliasable`` parameter convention is used when a ``@noescape`` + closure captures an ``inout`` argument. + +**NOTE:** If the callee is generic, all of its generic parameters must be bound +by the given substitution list. The arguments are given with these generic +substitutions applied, and the resulting closure is of concrete function type +with the given substitutions applied. The generic parameters themselves cannot +be partially applied; all of them must be bound. The result is always a concrete +function. + +**TODO:** The instruction, when applied to a generic function, currently +implicitly performs abstraction difference transformations enabled by the given +substitutions, such as promoting address-only arguments and returns to register +arguments. This should be fixed. + builtin ``````` :: From 42345dabd342fe793e4eb3bf338ba9560fac0725 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Tue, 28 Jan 2020 12:56:59 -0800 Subject: [PATCH 02/69] SymbolGraph: Move pathComponents up and include interfaceLanguage `pathComponents` doesn't help with disambiguation, so it shouldn't be a part of the identifier, but can be moved up one level. Include an interface language in the identifier instead. rdar://problem/58853310 --- lib/SymbolGraphGen/Edge.cpp | 10 +++-- lib/SymbolGraphGen/Symbol.cpp | 24 ++++++++++-- lib/SymbolGraphGen/Symbol.h | 43 ++------------------- lib/SymbolGraphGen/SymbolGraphASTWalker.cpp | 29 +++----------- lib/SymbolGraphGen/SymbolGraphASTWalker.h | 8 +--- 5 files changed, 37 insertions(+), 77 deletions(-) diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp index 296cc0960fee2..22f43eacc5402 100644 --- a/lib/SymbolGraphGen/Edge.cpp +++ b/lib/SymbolGraphGen/Edge.cpp @@ -25,12 +25,14 @@ void Edge::serialize(llvm::json::OStream &OS) const { // In case a dependent module isn't available, serialize a fallback name. auto TargetModuleName = Target->getModuleContext()->getName().str(); + if (TargetModuleName != Walker->M.getName().str()) { - auto TargetSymbolIdentifier = Walker->getSymbolIdentifier(Target); - auto TargetComponents = TargetSymbolIdentifier.SimpleComponents; + SmallVector, 8> TargetPathComponents; + Walker->getPathComponents(Target, TargetPathComponents); + SmallString<128> Scratch(TargetModuleName); - for (auto it = TargetComponents.begin(); - it != TargetComponents.end(); ++it) { + for (auto it = TargetPathComponents.begin(); + it != TargetPathComponents.end(); ++it) { Scratch.push_back('.'); Scratch.append(*it); } diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index c30210d75a1e1..425b98217b921 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -76,15 +76,30 @@ void Symbol::serializeKind(llvm::json::OStream &OS) const { void Symbol::serializeIdentifier(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const { - AttributeRAII A("identifier", OS); - Walker.getSymbolIdentifier(VD).serialize(OS); + OS.attributeObject("identifier", [&](){ + OS.attribute("precise", Walker.getUSR(VD)); + OS.attribute("interfaceLanguage", "swift"); + }); +} + +void Symbol::serializePathComponents(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeArray("pathComponents", [&](){ + SmallVector, 8> PathComponents; + Walker.getPathComponents(VD, PathComponents); + for (auto Component : PathComponents) { + OS.value(Component); + } + }); } void Symbol::serializeNames(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const { OS.attributeObject("names", [&](){ - auto Identifier = Walker.getSymbolIdentifier(VD); - OS.attribute("title", Identifier.SimpleComponents.back()); + SmallVector, 8> PathComponents; + Walker.getPathComponents(VD, PathComponents); + + OS.attribute("title", PathComponents.back()); // "navigator": null Walker.serializeSubheadingDeclarationFragments("subheading", VD, OS); // "prose": null @@ -370,6 +385,7 @@ void Symbol::serialize(SymbolGraphASTWalker &Walker, OS.object([&](){ serializeKind(OS); serializeIdentifier(Walker, OS); + serializePathComponents(Walker, OS); serializeNames(Walker, OS); serializeDocComment(Walker, OS); diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h index 9cdbca5757164..6e22255596b50 100644 --- a/lib/SymbolGraphGen/Symbol.h +++ b/lib/SymbolGraphGen/Symbol.h @@ -24,46 +24,6 @@ namespace symbolgraphgen { struct AvailabilityDomain; struct SymbolGraphASTWalker; -/** - An identifier for a symbol that provides a globally unique identifier suitable for - internal lookups and a locally unique path for human use, such as a URL. - */ -struct SymbolIdentifier { - /** - A string that uniquely identifies a symbol within a module in the event of - ambiguities. A precise identifier need not be human readable. - */ - StringRef PreciseIdentifier; - - /** - The components for a "fully qualified" identifier. - */ - ArrayRef SimpleComponents; - - SymbolIdentifier(llvm::StringRef PreciseIdentifier, - ArrayRef SimpleComponents) - : PreciseIdentifier(PreciseIdentifier), - SimpleComponents(SimpleComponents) { - assert(!PreciseIdentifier.empty()); - } - - void serialize(llvm::json::OStream &OS) const { - OS.object([&](){ - OS.attribute("precise", PreciseIdentifier); - OS.attributeArray("simpleComponents", [&](){ - for (auto Component : SimpleComponents) { - OS.value(Component); - } - }); - }); - } - - bool operator==(const SymbolIdentifier &Other) const { - return PreciseIdentifier == Other.PreciseIdentifier && - SimpleComponents == Other.SimpleComponents; - } -}; - /// A symbol from a module: a node in a graph. struct Symbol { const ValueDecl *VD; @@ -76,6 +36,9 @@ struct Symbol { void serializeIdentifier(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const; + void serializePathComponents(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + void serializeNames(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const; diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp index 2ce1337d65468..cc370438be29b 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -149,24 +149,15 @@ StringRef SymbolGraphASTWalker::getUSR(const ValueDecl *VD) { return USR; } -SymbolIdentifier -SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { - // Look in the symbol identifier cache for this declartion. - auto Found = SymbolIdentifierCache.find(VD); - if (Found != SymbolIdentifierCache.end()) { - return Found->getSecond(); - } - - // Not found; need to build a symbol identifier and add it to the cache. - auto PreciseIdentifier = getUSR(VD); - llvm::SmallVector SimpleIdentifierChain; - +void +SymbolGraphASTWalker::getPathComponents(const ValueDecl *VD, + SmallVectorImpl> &Components) { // Collect the spellings of the fully qualified identifier components. auto Decl = VD; while (Decl && !isa(Decl)) { SmallString<32> Scratch; Decl->getFullName().getString(Scratch); - SimpleIdentifierChain.push_back(Ctx.allocateCopy(Scratch.str())); + Components.push_back(Scratch); if (const auto *DC = Decl->getDeclContext()) { if (const auto *Proto = DC->getExtendedProtocolDecl()) { Decl = Proto; @@ -179,17 +170,9 @@ SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { Decl = nullptr; } } - - // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. - std::reverse(SimpleIdentifierChain.begin(), SimpleIdentifierChain.end()); - SymbolIdentifier Identifier { - PreciseIdentifier, - Ctx.allocateCopy(llvm::makeArrayRef(SimpleIdentifierChain)) - }; - - SymbolIdentifierCache.insert({VD, Identifier}); - return Identifier; + // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. + std::reverse(Components.begin(), Components.end()); } PrintOptions SymbolGraphASTWalker::getDeclarationFragmentsPrintOptions() const { diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h index fb3e3dc9b6d0d..adf15ec5d46ca 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.h +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -27,7 +27,6 @@ class ValueDecl; namespace symbolgraphgen { -struct SymbolIdentifier; struct SymbolGraph; struct SymbolGraphOptions; @@ -49,9 +48,6 @@ struct SymbolGraphASTWalker : public SourceEntityWalker { /// A context for allocations. markup::MarkupContext Ctx; - /// A cache of identifiers for declarations that may be seen more than once. - llvm::DenseMap SymbolIdentifierCache; - /// A cache of USRs for declarations. llvm::DenseMap USRCache; @@ -72,8 +68,8 @@ struct SymbolGraphASTWalker : public SourceEntityWalker { /// Get the USR of a declaration and add it to the local allocator. StringRef getUSR(const ValueDecl *VD); - /// Returns a `SymbolIdentifier` for a given declaration. - SymbolIdentifier getSymbolIdentifier(const ValueDecl *VD); + /// Returns an array of path components for a declaration. + void getPathComponents(const ValueDecl *VD, SmallVectorImpl> &Components); // MARK: - Declaration Fragments From 37e403db78c78a39e046b810cd3bd702ec1f598d Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 28 Jan 2020 16:16:04 -0800 Subject: [PATCH 03/69] Compile libraries in testcases with -parse-as-library (NFC) This is in preparation to a change in serialization of global variables where this detail will matter. --- test/DebugInfo/inlinescopes.swift | 2 +- test/SILGen/expressions.swift | 2 +- test/SILGen/global_init_attribute.swift | 2 +- test/SILGen/global_resilience.swift | 2 +- test/Serialization/Recovery/typedefs.swift | 3 +++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/DebugInfo/inlinescopes.swift b/test/DebugInfo/inlinescopes.swift index 769b8a38ce8f0..6c4a3a4fefc3d 100644 --- a/test/DebugInfo/inlinescopes.swift +++ b/test/DebugInfo/inlinescopes.swift @@ -6,7 +6,7 @@ // RUN: %FileCheck %s -check-prefix=TRANSPARENT-CHECK < %t.ll // CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main{{.*}} -// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %4), !dbg ![[CALL:.*]] +// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %{{.*}}), !dbg ![[CALL:.*]] // CHECK-DAG: ![[TOPLEVEL:.*]] = !DIFile(filename: "{{.*}}inlinescopes.swift" import FooBar diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index f642702602900..54654a6ee7b29 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t - +// RUN: echo "public var x = Int()" | %target-swift-frontend -parse-as-library -module-name FooBar -emit-module -o %t - // RUN: %target-swift-emit-silgen -parse-stdlib -module-name expressions %s -I%t -disable-access-control | %FileCheck %s import Swift diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index f47bde0192a73..95167c7127078 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-module -o %t %S/Inputs/def_global.swift +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-module -o %t %S/Inputs/def_global.swift // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -I %t %s | %FileCheck %s // // Test that SILGen uses the "global_init" attribute for all global diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index c05720d0713eb..78b425a9ed684 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift +// RUN: %target-swift-frontend -emit-module -parse-as-library -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift // RUN: %target-swift-emit-silgen -I %t -enable-library-evolution -parse-as-library %s | %FileCheck %s // RUN: %target-swift-emit-sil -I %t -O -enable-library-evolution -parse-as-library %s | %FileCheck --check-prefix=CHECK-OPT %s diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index a9fab839e045b..4b33701fd00e8 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -1,4 +1,7 @@ // RUN: %empty-directory(%t) + +// Cannot use -parse-as-library here because that would compile also the +// #if VERIFY path, which contains top-level code. // RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s From f2c61d8ec58fd10535fc3eb2cab192a47de7e222 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Mon, 27 Jan 2020 21:48:19 -0800 Subject: [PATCH 04/69] Bug fixes for type fingerprints --- ...endenciesSourceFileDepGraphConstructor.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index 41df94b5fab71..a2cb94342861f 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -680,12 +680,10 @@ class SourceFileDepGraphConstructor { for (const auto &contextNameFingerprint : contextNameFingerprints) { auto p = g.findExistingNodePairOrCreateAndAddIfNew( kind, contextNameFingerprint); - // When we don't have a fingerprint yet, must rebuild every provider when - // interfaceHash changes. So when interface (i.e. interface hash) of - // sourceFile changes, every provides is dirty. And since we don't know - // what happened, dirtyness might affect the interface. - if (!p.getInterface()->getFingerprint().hasValue()) - g.addArc(g.getSourceFileNodePair().getInterface(), p.getInterface()); + // Since the current type fingerprints only include tokens in the body, + // when the interface hash changes, it is possible that the type in the + // file has changed. + g.addArc(g.getSourceFileNodePair().getInterface(), p.getInterface()); } } @@ -809,8 +807,15 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( // that may have been there. No error handling -- this is just a nicety, it // doesn't matter if it fails. llvm::sys::fs::rename(outputPath, outputPath + "~"); + // Since, when fingerprints are enabled, + // the parser diverts token hashing into per-body fingerprints + // before it can know if a difference is in a private type, + // in order to be able to test the changed fingerprints + // we force the inclusion of private declarations when fingerprints + // are enabled. const bool includeIntrafileDeps = - SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes; + SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || + SF->getASTContext().LangOpts.EnableTypeFingerprints; const bool hadCompilationError = SF->getASTContext().hadError(); auto gc = SourceFileDepGraphConstructor::forSourceFile( SF, depTracker, outputPath, includeIntrafileDeps, hadCompilationError); From bbffc0959b714a0300d92a2f8549309f841b1fae Mon Sep 17 00:00:00 2001 From: David Ungar Date: Mon, 27 Jan 2020 21:48:51 -0800 Subject: [PATCH 05/69] Tests for type fingerprints are enabled. --- ...ine_grained_swiftdeps_with_fingerprints.sh | 7 ++ .../added_method-type-fingerprints.swift | 55 ++++++++++++ ...s_private_property-type-fingerprints.swift | 53 +++++++++++ ...ate_class_property-type-fingerprints.swift | 53 +++++++++++ ...m_private_property-type-fingerprints.swift | 56 ++++++++++++ ...vate_enum_property-type-fingerprints.swift | 55 ++++++++++++ ...ded_private_method-type-fingerprints.swift | 55 ++++++++++++ ...method_value_types-type-fingerprints.swift | 88 +++++++++++++++++++ ...te_protocol_method-type-fingerprints.swift | 50 +++++++++++ ..._protocol_property-type-fingerprints.swift | 50 +++++++++++ ...t_private_property-type-fingerprints.swift | 56 ++++++++++++ ...te_struct_property-type-fingerprints.swift | 56 ++++++++++++ ...edited_method_body-type-fingerprints.swift | 31 +++++++ test/InterfaceHash/edited_method_body.swift | 4 +- ...ed_property_getter-type-fingerprints.swift | 33 +++++++ .../edited_property_getter.swift | 4 +- .../rdar23148987-type-fingerprints.swift | 61 +++++++++++++ .../Serialization/rdar40839486.swift | 4 +- 18 files changed, 765 insertions(+), 6 deletions(-) create mode 100755 test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh create mode 100644 test/InterfaceHash/added_method-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_class_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_enum_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_method-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_struct_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/edited_method_body-type-fingerprints.swift create mode 100644 test/InterfaceHash/edited_property_getter-type-fingerprints.swift create mode 100644 validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift diff --git a/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh b/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh new file mode 100755 index 0000000000000..80bb392c22cfb --- /dev/null +++ b/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +# Fine-grained swiftdeps files use multiple lines for each graph node. +# Compress such a file so that each entry is one line of the form: +# +# Also sort for consistency, since the node order can vary. + +awk '/kind:/ {k = $2; f = ""}; /aspect:/ {a = $2}; /context:/ {c = $2}; /name/ {n = $2}; /sequenceNumber/ {s = $2}; /fingerprint:/ {f = $2 }; /isProvides:/ {isP = $2; print k, a, c, n, isP, f}' | sort diff --git a/test/InterfaceHash/added_method-type-fingerprints.swift b/test/InterfaceHash/added_method-type-fingerprints.swift new file mode 100644 index 0000000000000..7b51e1f67b758 --- /dev/null +++ b/test/InterfaceHash/added_method-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..cf3a104cc71b1 --- /dev/null +++ b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift @@ -0,0 +1,53 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +private class C { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 +} + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift new file mode 100644 index 0000000000000..f4e01619c1b76 --- /dev/null +++ b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift @@ -0,0 +1,53 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 +} + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..9ad0b28d56449 --- /dev/null +++ b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + + +// BEGIN a.swift +private enum A { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +private enum A { + case x, y + func f2() -> Int { + return 0 + } + + var foo: Int { return 0 } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift new file mode 100644 index 0000000000000..c8220f6f9a54b --- /dev/null +++ b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +enum A { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +enum A { + case x, y + func f2() -> Int { + return 0 + } + + private var foo: Int { return 0 } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method-type-fingerprints.swift b/test/InterfaceHash/added_private_method-type-fingerprints.swift new file mode 100644 index 0000000000000..c0c60206d9c92 --- /dev/null +++ b/test/InterfaceHash/added_private_method-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift new file mode 100644 index 0000000000000..42fee00e31bf0 --- /dev/null +++ b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift @@ -0,0 +1,88 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +struct A { + func f2() -> Int { + return 0 + } +} + +enum B { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +struct A { + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +enum B { + case x, y + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift new file mode 100644 index 0000000000000..c0ccd54a11771 --- /dev/null +++ b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift @@ -0,0 +1,50 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private protocol P { + func f2() -> Int + var y: Int { get set } +} + +// BEGIN b.swift +private protocol P { + func f2() -> Int + func f3() -> Int + var y: Int { get set } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift new file mode 100644 index 0000000000000..c27cee3752b83 --- /dev/null +++ b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift @@ -0,0 +1,50 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private protocol P { + func f2() -> Int + var y: Int { get set } +} + +// BEGIN b.swift +private protocol P { + func f2() -> Int + var x: Int { get set } + var y: Int { get set } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..47e371aff0901 --- /dev/null +++ b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +struct S { + func f2() -> Int { + return 0 + } + + var y: Int = 0 +} + +// BEGIN b.swift +struct S { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 + var y: Int = 0 +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift new file mode 100644 index 0000000000000..bd92b5dfbe37f --- /dev/null +++ b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private struct S { + func f2() -> Int { + return 0 + } + + var y: Int = 0 +} + +// BEGIN b.swift +private struct S { + func f2() -> Int { + return 0 + } + + var x: Int = 0 + var y: Int = 0 +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/edited_method_body-type-fingerprints.swift b/test/InterfaceHash/edited_method_body-type-fingerprints.swift new file mode 100644 index 0000000000000..10ae3e000c09f --- /dev/null +++ b/test/InterfaceHash/edited_method_body-type-fingerprints.swift @@ -0,0 +1,31 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 1 + } +} diff --git a/test/InterfaceHash/edited_method_body.swift b/test/InterfaceHash/edited_method_body.swift index 2f87dd9f02fef..b0aebce44e500 100644 --- a/test/InterfaceHash/edited_method_body.swift +++ b/test/InterfaceHash/edited_method_body.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift new file mode 100644 index 0000000000000..0a0b5fdaaff54 --- /dev/null +++ b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift @@ -0,0 +1,33 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps + +// BEGIN a.swift +class C { + var p: Int { + return 0 + } +} + +// BEGIN b.swift +class C { + var p: Int { + let x = 1 + return x + } +} + diff --git a/test/InterfaceHash/edited_property_getter.swift b/test/InterfaceHash/edited_property_getter.swift index 42584e33f6467..deaeb8ccc08dc 100644 --- a/test/InterfaceHash/edited_property_getter.swift +++ b/test/InterfaceHash/edited_property_getter.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift new file mode 100644 index 0000000000000..3eefcdd073ba8 --- /dev/null +++ b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift @@ -0,0 +1,61 @@ +// RUN: %empty-directory(%t) + +// RUN: cp %s %t/main.swift +// RUN: cp %S/Inputs/rdar23148987/helper-1.swift %t/helper.swift +// RUN: touch -t 201401240005 %t/*.swift + +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s + +// CHECK-1-NOT: warning +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/main.swift" +// CHECK-1: {{^}$}} + +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/helper.swift" +// CHECK-1: {{^}$}} + +// RUN: ls %t/ | %FileCheck -check-prefix=CHECK-LS %s + +// CHECK-LS-DAG: main.o +// CHECK-LS-DAG: helper.o + +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s + +// CHECK-1-SKIPPED-NOT: warning +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/main.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/helper.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// RUN: cp %S/Inputs/rdar23148987/helper-2.swift %t/helper.swift +// RUN: touch -t 201401240006 %t/helper.swift +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 -driver-show-incremental -driver-show-job-lifecycle | %FileCheck -check-prefix=CHECK-2 %s + +// CHECK-2-NOT: warning +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/helper.swift" +// CHECK-2: {{^}$}} + +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/main.swift" +// CHECK-2: {{^}$}} + +func test(obj: Test) { + obj.foo() +} diff --git a/validation-test/Serialization/rdar40839486.swift b/validation-test/Serialization/rdar40839486.swift index 3704eec44bf09..8c4e1531b0db1 100644 --- a/validation-test/Serialization/rdar40839486.swift +++ b/validation-test/Serialization/rdar40839486.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -disable-type-fingerprints -emit-module-path %t/main4.swiftmodule -swift-version 4 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s -// RUN: %target-build-swift -disable-type-fingerprints -emit-module-path %t/main4_2.swiftmodule -swift-version 4.2 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s +// RUN: %target-build-swift -emit-module-path %t/main4.swiftmodule -swift-version 4 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s +// RUN: %target-build-swift -emit-module-path %t/main4_2.swiftmodule -swift-version 4.2 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s // REQUIRES: OS=macosx || OS=ios From b0786e7e06230a3d1f59271389b56222ea74aca3 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 28 Jan 2020 21:13:22 -0800 Subject: [PATCH 06/69] Fix propagation of guaranteed phi args during DiagnoseUnreachable. Fixes assertion failure - UNREACHABLE executed at swift/include/swift/SIL/OwnershipUtils.h:127! --- .../Mandatory/DiagnoseUnreachable.cpp | 7 ++--- test/SILOptimizer/diagnose_unreachable.sil | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index 6923725dc55b6..107829132d2a8 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -185,12 +185,9 @@ static void propagateBasicBlockArgs(SILBasicBlock &BB) { // this to CCP and trigger another round of copy propagation. SILArgument *Arg = *AI; - // If this argument is guaranteed and Args[Idx] is a SILFunctionArgument, - // delete the end_borrow. - if (Arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed && - isa(Args[Idx])) { + // If this argument is guaranteed and Args[Idx], delete the end_borrow. + if (Arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed) deleteEndBorrows(Arg); - } // We were able to fold, so all users should use the new folded value. Arg->replaceAllUsesWith(Args[Idx]); diff --git a/test/SILOptimizer/diagnose_unreachable.sil b/test/SILOptimizer/diagnose_unreachable.sil index bbf443dc223ec..7045ca9e368ab 100644 --- a/test/SILOptimizer/diagnose_unreachable.sil +++ b/test/SILOptimizer/diagnose_unreachable.sil @@ -782,3 +782,33 @@ bb1(%2 : @owned $Builtin.NativeObject): bb2: unreachable } + +// Test propagation of guaranteed phi arguments. The nested end_borrow +// must be removed, even with the outer borrow is *not* a function +// argument. + +enum EnumWithB { + case A(B) + func testit() -> Int +} + +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK: bb1([[PHI:%.*]] : @guaranteed $B): +// CHECK: br bb2 +// CHECK: bb2: +// CHECK: end_borrow [[PHI]] : $B +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' +sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +bb0(%0 : @guaranteed $EnumWithB): + switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 + +bb1(%2 : @guaranteed $B): + br bb3(%2 : $B) + +bb3(%4 : @guaranteed $B): + end_borrow %4 : $B + end_borrow %2 : $B + %99 = tuple () + return %99 : $() +} From 1af49ecb996e66aa0b7de7395ee073973a13ed7c Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 28 Jan 2020 22:54:08 -0800 Subject: [PATCH 07/69] Fix an EscapeAnalysis assert to handle recent changes. setPointsToEdge should assert that its target isn't already merged, but now that we batch up multiple merge requests, it's fine to allow the target to be scheduled-for-merge. Many assertions have been recently added and tightened in order to "discover" unexpected cases. There's nothing incorrect about how these cases were handled, but they lack unit tests. In this case I still haven't been able to reduce a test case. I'm continuing to work on it, but don't want to further delay the fix. --- include/swift/SILOptimizer/Analysis/EscapeAnalysis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index c1d405c4d7f42..2d56b97d85695 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -449,7 +449,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Sets the outgoing points-to edge. The \p To node must be a Content node. void setPointsToEdge(CGNode *To) { - assert(!To->mergeTo); + assert(!To->isMerged); assert(To->Type == NodeType::Content && "Wrong node type for points-to edge"); pointsToIsEdge = true; From b2083db45d88696eec1c34e5e79ce1f9849e290b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 29 Jan 2020 00:06:57 -0800 Subject: [PATCH 08/69] [ConstraintSystem] Add a fix to allow conversion between non-class type and AnyObject --- lib/Sema/CSFix.cpp | 37 +++++++++++++++++++++++++++++++++++++ lib/Sema/CSFix.h | 22 ++++++++++++++++++++-- lib/Sema/CSSimplify.cpp | 13 +++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index ffc4e502509a5..95e78c3a454be 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1180,3 +1180,40 @@ SpecifyObjectLiteralTypeImport::create(ConstraintSystem &cs, ConstraintLocator *locator) { return new (cs.getAllocator()) SpecifyObjectLiteralTypeImport(cs, locator); } + +AllowNonClassTypeToConvertToAnyObject::AllowNonClassTypeToConvertToAnyObject( + ConstraintSystem &cs, Type type, ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::AllowNonClassTypeToConvertToAnyObject, + type, cs.getASTContext().getAnyObjectType(), locator) { +} + +bool AllowNonClassTypeToConvertToAnyObject::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + + auto *locator = getLocator(); + if (locator->getPath().empty()) + return false; + + const auto &last = locator->getPath().back(); + switch (last.getKind()) { + case ConstraintLocator::ContextualType: { + ContextualFailure failure(cs, getFromType(), getToType(), locator); + return failure.diagnose(asNote); + } + + case ConstraintLocator::ApplyArgToParam: { + ArgumentMismatchFailure failure(cs, getFromType(), getToType(), locator); + return failure.diagnose(asNote); + } + + default: + return false; + } +} + +AllowNonClassTypeToConvertToAnyObject * +AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + AllowNonClassTypeToConvertToAnyObject(cs, type, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 05959c8ddbaf4..086f9d5e3de89 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -235,12 +235,15 @@ enum class FixKind : uint8_t { /// Closure return type has to be explicitly specified because it can't be /// inferred in current context e.g. because it's a multi-statement closure. SpecifyClosureReturnType, - - /// Object literal type coudn't be infered because the module where + + /// Object literal type coudn't be inferred because the module where /// the default type that implements the associated literal protocol /// is declared was not imported. SpecifyObjectLiteralTypeImport, + /// Allow any type (and not just class or class-constrained type) to + /// be convertible to AnyObject. + AllowNonClassTypeToConvertToAnyObject, }; class ConstraintFix { @@ -1655,6 +1658,21 @@ class SpecifyObjectLiteralTypeImport final : public ConstraintFix { }; +class AllowNonClassTypeToConvertToAnyObject final : public ContextualMismatch { + AllowNonClassTypeToConvertToAnyObject(ConstraintSystem &cs, Type type, + ConstraintLocator *locator); + +public: + std::string getName() const { + return "allow non-class type to convert to 'AnyObject'"; + } + + bool diagnose(bool asNote = false) const; + + static AllowNonClassTypeToConvertToAnyObject * + create(ConstraintSystem &cs, Type type, ConstraintLocator *locator); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 43a00626b0e8d..46b71306844d5 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2095,9 +2095,17 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, // Subtype relation to AnyObject also allows class-bound // existentials that are not @objc and therefore carry // witness tables. - if (!type1->isClassExistentialType() && - !type1->mayHaveSuperclass()) + if (!type1->isClassExistentialType() && !type1->mayHaveSuperclass()) { + if (shouldAttemptFixes()) { + auto *fix = AllowNonClassTypeToConvertToAnyObject::create( + *this, type1, getConstraintLocator(locator)); + + return recordFix(fix) ? getTypeMatchFailure(locator) + : getTypeMatchSuccess(); + } + return getTypeMatchFailure(locator); + } } // Keep going. @@ -8837,6 +8845,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: case FixKind::AllowInvalidUseOfTrailingClosure: + case FixKind::AllowNonClassTypeToConvertToAnyObject: case FixKind::SpecifyClosureReturnType: llvm_unreachable("handled elsewhere"); } From ec3b783380a984c2ee1531108e08170a85ace793 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 29 Jan 2020 00:37:39 -0800 Subject: [PATCH 09/69] [Diagnostics] Improve diagnostic for invalid conversion to AnyObject --- include/swift/AST/DiagnosticsSema.def | 17 +++++- lib/Sema/CSDiagnostics.cpp | 56 +++++++++++++------ lib/Sema/CSDiagnostics.h | 2 +- test/ClangImporter/attr-swift_private.swift | 2 +- test/Constraints/bridging.swift | 10 ++-- test/Generics/existential_restrictions.swift | 2 +- test/Interpreter/SDK/misc_osx.swift | 2 +- test/Parse/metatype_object_conversion.swift | 7 +-- ...g_metatype_cast_to_reference_no_objc.swift | 4 +- 9 files changed, 68 insertions(+), 34 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index fef7af3b04473..02438f31c3bd5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -338,6 +338,10 @@ ERROR(cannot_convert_initializer_value,none, "cannot convert value of type %0 to specified type %1", (Type,Type)) ERROR(cannot_convert_initializer_value_protocol,none, "value of type %0 does not conform to specified type %1", (Type,Type)) +ERROR(cannot_convert_initializer_value_anyobject,none, + "value of type %0 expected to be instance of class or " + "class-constrained type", + (Type, Type)) ERROR(cannot_convert_initializer_value_nil,none, "'nil' cannot initialize specified type %0", (Type)) @@ -346,6 +350,10 @@ ERROR(cannot_convert_to_return_type,none, (Type,Type)) ERROR(cannot_convert_to_return_type_protocol,none, "return expression of type %0 does not conform to %1", (Type,Type)) +ERROR(cannot_convert_return_type_to_anyobject,none, + "return expression of type %0 expected to be an instance of " + "a class or class-constrained type", + (Type, Type)) ERROR(cannot_convert_to_return_type_nil,none, "'nil' is incompatible with return type %0", (Type)) @@ -440,7 +448,10 @@ NOTE(candidate_performs_illegal_ephemeral_conv,none, ERROR(cannot_convert_argument_value_protocol,none, "argument type %0 does not conform to expected type %1", (Type, Type)) - +ERROR(cannot_convert_argument_value_anyobject,none, + "argument type %0 expected to be an instance of " + "a class or class-constrained type", + (Type, Type)) ERROR(cannot_convert_argument_value_nil,none, "'nil' is not compatible with expected argument type %0", (Type)) @@ -536,6 +547,10 @@ NOTE(assign_protocol_conformance_fix_it,none, ERROR(cannot_convert_assign_protocol,none, "value of type %0 does not conform to %1 in assignment", (Type, Type)) +ERROR(cannot_convert_assign_anyobject,none, + "value of type %0 expected to be an instance of " + "a class or class-constrained type in assignment", + (Type, Type)) ERROR(cannot_convert_assign_nil,none, "'nil' cannot be assigned to type %0", (Type)) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b8cf4a95a75bb..b85e37691bb76 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1977,7 +1977,7 @@ bool ContextualFailure::diagnoseAsError() { CTP = CTP_ClosureResult; } - if (auto msg = getDiagnosticFor(CTP, toType->isExistentialType())) { + if (auto msg = getDiagnosticFor(CTP, toType)) { diagnostic = *msg; break; } @@ -2276,11 +2276,9 @@ bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { if (auto *coerceExpr = dyn_cast(anchor)) { auto fromType = getType(coerceExpr->getSubExpr()); auto toType = getType(coerceExpr->getCastTypeLoc()); - - auto diagnostic = - getDiagnosticFor(CTP_CoerceOperand, - /*forProtocol=*/toType->isExistentialType()); - + + auto diagnostic = getDiagnosticFor(CTP_CoerceOperand, toType); + auto diag = emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); diag.highlight(anchor->getSourceRange()); @@ -2844,15 +2842,24 @@ bool ContextualFailure::isIntegerToStringIndexConversion() const { Optional> ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, - bool forProtocol) { + Type contextualType) { + auto forProtocol = contextualType->isExistentialType(); switch (context) { - case CTP_Initialization: + case CTP_Initialization: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_initializer_value_anyobject; + return forProtocol ? diag::cannot_convert_initializer_value_protocol : diag::cannot_convert_initializer_value; + } case CTP_ReturnStmt: - case CTP_ReturnSingleExpr: + case CTP_ReturnSingleExpr: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_return_type_to_anyobject; + return forProtocol ? diag::cannot_convert_to_return_type_protocol : diag::cannot_convert_to_return_type; + } case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value; case CTP_DefaultParameter: @@ -2861,9 +2868,13 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_YieldByValue: return forProtocol ? diag::cannot_convert_yield_value_protocol : diag::cannot_convert_yield_value; - case CTP_CallArgument: + case CTP_CallArgument: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_argument_value_anyobject; + return forProtocol ? diag::cannot_convert_argument_value_protocol : diag::cannot_convert_argument_value; + } case CTP_ClosureResult: return forProtocol ? diag::cannot_convert_closure_result_protocol : diag::cannot_convert_closure_result; @@ -2879,9 +2890,13 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_CoerceOperand: return forProtocol ? diag::cannot_convert_coerce_protocol : diag::cannot_convert_coerce; - case CTP_AssignSource: + case CTP_AssignSource: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_assign_anyobject; + return forProtocol ? diag::cannot_convert_assign_protocol : diag::cannot_convert_assign; + } case CTP_SubscriptAssignSource: return forProtocol ? diag::cannot_convert_subscript_assign_protocol : diag::cannot_convert_subscript_assign; @@ -2908,7 +2923,7 @@ bool TupleContextualFailure::diagnoseAsError() { else if ((purpose == CTP_Initialization) && !cs.getContextualType(getAnchor())) diagnostic = diag::tuple_types_not_convertible; - else if (auto diag = getDiagnosticFor(purpose, /*forProtocol=*/false)) + else if (auto diag = getDiagnosticFor(purpose, getToType())) diagnostic = *diag; else return false; @@ -2919,7 +2934,7 @@ bool TupleContextualFailure::diagnoseAsError() { bool FunctionTypeMismatch::diagnoseAsError() { auto purpose = getContextualTypePurpose(); - auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false); + auto diagnostic = getDiagnosticFor(purpose, getToType()); if (!diagnostic) return false; @@ -4839,17 +4854,16 @@ bool MissingContextualConformanceFailure::diagnoseAsError() { if (path.empty()) { assert(isa(anchor)); if (isa(cast(anchor)->getDest())) { - diagnostic = - getDiagnosticFor(CTP_SubscriptAssignSource, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(CTP_SubscriptAssignSource, getToType()); } else { - diagnostic = getDiagnosticFor(CTP_AssignSource, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(CTP_AssignSource, getToType()); } } else { const auto &last = path.back(); switch (last.getKind()) { case ConstraintLocator::ContextualType: assert(Context != CTP_Unused); - diagnostic = getDiagnosticFor(Context, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(Context, getToType()); break; case ConstraintLocator::SequenceElementType: { @@ -5277,7 +5291,7 @@ bool InOutConversionFailure::diagnoseAsError() { assert(locator->findLast()); auto contextualType = cs.getContextualType(anchor); auto purpose = getContextualTypePurpose(); - auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false); + auto diagnostic = getDiagnosticFor(purpose, contextualType); if (!diagnostic) return false; @@ -5385,6 +5399,12 @@ bool ArgumentMismatchFailure::diagnoseAsError() { auto argType = getFromType(); auto paramType = getToType(); + if (paramType->isAnyObject()) { + emitDiagnostic(getLoc(), diag::cannot_convert_argument_value_anyobject, + argType, paramType); + return true; + } + Diag diagnostic = diag::cannot_convert_argument_value; // If parameter type is a protocol value, let's says that diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 90f64c5175acf..386ea94c8831a 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -653,7 +653,7 @@ class ContextualFailure : public FailureDiagnostic { ContextualTypePurpose getContextualTypePurpose() const { return CTP; } static Optional> - getDiagnosticFor(ContextualTypePurpose context, bool forProtocol); + getDiagnosticFor(ContextualTypePurpose context, Type contextualType); }; /// Diagnose errors related to converting function type which diff --git a/test/ClangImporter/attr-swift_private.swift b/test/ClangImporter/attr-swift_private.swift index 05ddc81835f30..f249454b15d96 100644 --- a/test/ClangImporter/attr-swift_private.swift +++ b/test/ClangImporter/attr-swift_private.swift @@ -121,7 +121,7 @@ func testCF(_ a: __PrivCFType, b: __PrivCFSub, c: __PrivInt) { makeSureAnyObject(a) makeSureAnyObject(b) #if !IRGEN - makeSureAnyObject(c) // expected-error {{argument type '__PrivInt' (aka 'Int32') does not conform to expected type 'AnyObject'}} + makeSureAnyObject(c) // expected-error {{argument type '__PrivInt' (aka 'Int32') expected to be an instance of a class or class-constrained type}} #endif } diff --git a/test/Constraints/bridging.swift b/test/Constraints/bridging.swift index 4cdc52e3e2626..c40fb0bdf181d 100644 --- a/test/Constraints/bridging.swift +++ b/test/Constraints/bridging.swift @@ -89,7 +89,7 @@ func bridgeToObjC(_ s: BridgedStruct) -> BridgedClass { } func bridgeToAnyObject(_ s: BridgedStruct) -> AnyObject { - return s // expected-error{{return expression of type 'BridgedStruct' does not conform to 'AnyObject'}} + return s // expected-error{{return expression of type 'BridgedStruct' expected to be an instance of a class or class-constrained type}} return s as AnyObject } @@ -344,14 +344,14 @@ func forceUniversalBridgeToAnyObject(a: T, b: U, c: An z = g as AnyObject z = h as AnyObject - z = a // expected-error{{does not conform to 'AnyObject'}} + z = a // expected-error{{value of type 'T' expected to be an instance of a class or class-constrained type in assignment}} z = b - z = c // expected-error{{does not conform to 'AnyObject'}} expected-note {{cast 'Any' to 'AnyObject'}} {{8-8= as AnyObject}} - z = d // expected-error{{does not conform to 'AnyObject'}} + z = c // expected-error{{value of type 'Any' expected to be an instance of a class or class-constrained type in assignment}} expected-note {{cast 'Any' to 'AnyObject'}} {{8-8= as AnyObject}} + z = d // expected-error{{value of type 'KnownUnbridged' expected to be an instance of a class or class-constrained type in assignment}} z = e z = f z = g - z = h // expected-error{{does not conform to 'AnyObject'}} + z = h // expected-error{{value of type 'String' expected to be an instance of a class or class-constrained type in assignment}} _ = z } diff --git a/test/Generics/existential_restrictions.swift b/test/Generics/existential_restrictions.swift index ae652220daa2b..fbf2f29375fc5 100644 --- a/test/Generics/existential_restrictions.swift +++ b/test/Generics/existential_restrictions.swift @@ -25,7 +25,7 @@ func fT(_ t: T) { } func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, ao: AnyObject) { fP(p) // expected-error{{value of protocol type 'P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} fAO(p) // expected-error{{global function 'fAO' requires that 'P' be a class type}} - fAOE(p) // expected-error{{argument type 'P' does not conform to expected type 'AnyObject'}} + fAOE(p) // expected-error{{argument type 'P' expected to be an instance of a class or class-constrained type}} fT(p) fOP(op) diff --git a/test/Interpreter/SDK/misc_osx.swift b/test/Interpreter/SDK/misc_osx.swift index fabd2413023c3..15383cf784077 100644 --- a/test/Interpreter/SDK/misc_osx.swift +++ b/test/Interpreter/SDK/misc_osx.swift @@ -13,5 +13,5 @@ func testFSEventStreamRef(stream: FSEventStreamRef) { FSEventStreamRetain(stream) // no-warning FSEventStreamRelease(stream) - let _: AnyObject = stream // expected-error {{value of type 'FSEventStreamRef' (aka 'OpaquePointer') does not conform to specified type 'AnyObject'}} + let _: AnyObject = stream // expected-error {{value of type 'FSEventStreamRef' (aka 'OpaquePointer') expected to be instance of class or class-constrained type}} } diff --git a/test/Parse/metatype_object_conversion.swift b/test/Parse/metatype_object_conversion.swift index 96f27402dc86f..2dc87ff1e87bb 100644 --- a/test/Parse/metatype_object_conversion.swift +++ b/test/Parse/metatype_object_conversion.swift @@ -12,15 +12,14 @@ func takesAnyObject(_ x: AnyObject) {} func concreteTypes() { takesAnyObject(C.self) - // TODO: Better error messages - takesAnyObject(S.self) // expected-error{{argument type 'S.Type' does not conform to expected type 'AnyObject'}} - takesAnyObject(ClassConstrainedProto.self) // expected-error{{argument type 'ClassConstrainedProto.Protocol' does not conform to expected type 'AnyObject'}} + takesAnyObject(S.self) // expected-error{{argument type 'S.Type' expected to be an instance of a class or class-constrained type}} + takesAnyObject(ClassConstrainedProto.self) // expected-error{{argument type 'ClassConstrainedProto.Protocol' expected to be an instance of a class or class-constrained type}} } func existentialMetatypes(nonClass: NonClassProto.Type, classConstrained: ClassConstrainedProto.Type, compo: (NonClassProto & ClassConstrainedProto).Type) { - takesAnyObject(nonClass) // expected-error{{argument type 'NonClassProto.Type' does not conform to expected type 'AnyObject'}} + takesAnyObject(nonClass) // expected-error{{argument type 'NonClassProto.Type' expected to be an instance of a class or class-constrained type}} takesAnyObject(classConstrained) takesAnyObject(compo) } diff --git a/test/Sema/diag_metatype_cast_to_reference_no_objc.swift b/test/Sema/diag_metatype_cast_to_reference_no_objc.swift index 28566417e0fbf..abca5b92aa279 100644 --- a/test/Sema/diag_metatype_cast_to_reference_no_objc.swift +++ b/test/Sema/diag_metatype_cast_to_reference_no_objc.swift @@ -3,6 +3,6 @@ class C {} func test(c: AnyClass) { - let _: AnyObject = c // expected-error {{value of type 'AnyClass' (aka 'AnyObject.Type') does not conform to specified type 'AnyObject'}} - let _: AnyObject = C.self // expected-error {{value of type 'C.Type' does not conform to specified type 'AnyObject'}} + let _: AnyObject = c // expected-error {{value of type 'AnyClass' (aka 'AnyObject.Type') expected to be instance of class or class-constrained type}} + let _: AnyObject = C.self // expected-error {{value of type 'C.Type' expected to be instance of class or class-constrained type}} } From ffa6bf47294960fb6aaf26da47c213bbb5a10c9d Mon Sep 17 00:00:00 2001 From: Chris Amanse <12277586+chrisamanse@users.noreply.github.com> Date: Wed, 29 Jan 2020 08:21:52 -0800 Subject: [PATCH 10/69] Fix memory leaks in ThreadBarriers.swift (#12212) * Fix memory leaks in ThreadBarriers.swift * Fatal error on pthread cond/mutex destroy failure * Rename pthread to thread * Fix pthread init function calls * Fix guard statement --- .../SwiftPrivateThreadExtras.swift | 5 +-- .../ThreadBarriers.swift | 36 +++++++++++-------- .../stdlib/StringSlicesConcurrentAppend.swift | 3 +- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 002aef8e74145..bc849536a9429 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -162,10 +162,7 @@ public class _stdlib_Barrier { } deinit { - let ret = _stdlib_thread_barrier_destroy(_threadBarrierPtr) - if ret != 0 { - fatalError("_stdlib_thread_barrier_destroy() failed") - } + _stdlib_thread_barrier_destroy(_threadBarrierPtr) } public func wait() { diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 9f5e183447021..6d79bd124e976 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -69,13 +69,12 @@ public func _stdlib_thread_barrier_init( InitializeConditionVariable(barrier.pointee.cond!) #else barrier.pointee.mutex = UnsafeMutablePointer.allocate(capacity: 1) - if pthread_mutex_init(barrier.pointee.mutex!, nil) != 0 { - // FIXME: leaking memory. - return -1 - } barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) - if pthread_cond_init(barrier.pointee.cond!, nil) != 0 { - // FIXME: leaking memory, leaking a mutex. + guard _stdlib_thread_barrier_mutex_and_cond_init(barrier) == 0 else { + barrier.pointee.mutex!.deinitialize(count: 1) + barrier.pointee.mutex!.deallocate() + barrier.pointee.cond!.deinitialize(count: 1) + barrier.pointee.cond!.deallocate() return -1 } #endif @@ -83,20 +82,27 @@ public func _stdlib_thread_barrier_init( return 0 } +private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t>) -> CInt { + guard pthread_mutex_init(barrier.pointee.mutex!, nil) == 0 else { + return -1 + } + guard pthread_cond_init(barrier.pointee.cond!, nil) == 0 else { + pthread_mutex_destroy(barrier.pointee.mutex!) + return -1 + } + return 0 +} + public func _stdlib_thread_barrier_destroy( _ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t> -) -> CInt { +) { #if os(Windows) // Condition Variables do not need to be explicitly destroyed // Mutexes do not need to be explicitly destroyed #else - if pthread_cond_destroy(barrier.pointee.cond!) != 0 { - // FIXME: leaking memory, leaking a mutex. - return -1 - } - if pthread_mutex_destroy(barrier.pointee.mutex!) != 0 { - // FIXME: leaking memory. - return -1 + guard pthread_cond_destroy(barrier.pointee.cond!) == 0 && + pthread_mutex_destroy(barrier.pointee.mutex!) == 0 else { + fatalError("_stdlib_thread_barrier_destroy() failed") } #endif barrier.pointee.cond!.deinitialize(count: 1) @@ -105,7 +111,7 @@ public func _stdlib_thread_barrier_destroy( barrier.pointee.mutex!.deinitialize(count: 1) barrier.pointee.mutex!.deallocate() - return 0 + return } public func _stdlib_thread_barrier_wait( diff --git a/validation-test/stdlib/StringSlicesConcurrentAppend.swift b/validation-test/stdlib/StringSlicesConcurrentAppend.swift index 64441e7a184ea..942449e9dfdc8 100644 --- a/validation-test/stdlib/StringSlicesConcurrentAppend.swift +++ b/validation-test/stdlib/StringSlicesConcurrentAppend.swift @@ -111,8 +111,7 @@ StringTestSuite.test("SliceConcurrentAppend") { expectEqual(0, joinRet1) expectEqual(0, joinRet2) - ret = _stdlib_thread_barrier_destroy(barrierVar!) - expectEqual(0, ret) + _stdlib_thread_barrier_destroy(barrierVar!) barrierVar!.deinitialize(count: 1) barrierVar!.deallocate() From 7351bfc86fc18975e97f9a29dddb4471efc73de5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 3 Jan 2020 16:24:11 -0800 Subject: [PATCH 11/69] [CSFix] Add a fix to add a missing qualifier to shadowed top-level name references --- lib/Sema/CSFix.cpp | 10 ++++++++++ lib/Sema/CSFix.h | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 95e78c3a454be..b8aa2f7fa9a7f 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1217,3 +1217,13 @@ AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, return new (cs.getAllocator()) AllowNonClassTypeToConvertToAnyObject(cs, type, locator); } + +bool AddQualifierToAccessTopLevelName::diagnose(bool asNote) const { + return false; +} + +AddQualifierToAccessTopLevelName * +AddQualifierToAccessTopLevelName::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) AddQualifierToAccessTopLevelName(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 086f9d5e3de89..d4c453070228d 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -244,6 +244,10 @@ enum class FixKind : uint8_t { /// Allow any type (and not just class or class-constrained type) to /// be convertible to AnyObject. AllowNonClassTypeToConvertToAnyObject, + + /// Member shadows a top-level name, such a name could only be accessed by + /// prefixing it with a module name. + AddQualifierToAccessTopLevelName, }; class ConstraintFix { @@ -1655,7 +1659,22 @@ class SpecifyObjectLiteralTypeImport final : public ConstraintFix { static SpecifyObjectLiteralTypeImport *create(ConstraintSystem &cs, ConstraintLocator *locator); +}; + +class AddQualifierToAccessTopLevelName final : public ConstraintFix { + AddQualifierToAccessTopLevelName(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AddQualifierToAccessTopLevelName, locator) {} + +public: + std::string getName() const { + return "qualify reference to access top-level function"; + } + bool diagnose(bool asNote = false) const; + + static AddQualifierToAccessTopLevelName *create(ConstraintSystem &cs, + ConstraintLocator *locator); }; class AllowNonClassTypeToConvertToAnyObject final : public ContextualMismatch { From c9c20afe27893bc7b1c607852abd540ebb5c8205 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 3 Jan 2020 20:31:17 -0800 Subject: [PATCH 12/69] [Diagnostics] Port name shadowing diagnostics Diagnose an attempt to reference a top-level name shadowed by a local member e.g. ```swift extension Sequence { func test() -> Int { return max(1, 2) } } ``` Here `min` refers to a global function `min(_: T, _: T)` in `Swift` module and can only be accessed by adding `Swift.` to it, because `Sequence` has a member named `min` which accepts a single argument. --- include/swift/AST/DiagnosticsSema.def | 12 ++++---- lib/Sema/CSDiagnostics.cpp | 42 +++++++++++++++++++++++++++ lib/Sema/CSDiagnostics.h | 23 +++++++++++++++ lib/Sema/CSFix.cpp | 4 ++- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 02438f31c3bd5..3d46cd38c1d43 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1153,14 +1153,12 @@ NOTE(candidate_expected_different_labels,none, "incorrect labels for candidate (have: '%0', expected: '%1')", (StringRef, StringRef)) +ERROR(member_shadows_function,none, + "use of %0 refers to %1 rather than %2 %3", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName)) ERROR(member_shadows_global_function,none, - "use of %0 refers to %1 %2 rather than %3 %4 in %5 %6", - (DeclNameRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, - DeclName, DescriptiveDeclKind, DeclName)) -ERROR(member_shadows_global_function_near_match,none, - "use of %0 nearly matches %3 %4 in %5 %6 rather than %1 %2", - (DeclNameRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, - DeclName, DescriptiveDeclKind, DeclName)) + "use of %0 refers to %1 rather than %2 %3 in module %4", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, DeclName)) ERROR(instance_member_use_on_type,none, "instance member %1 cannot be used on type %0; " diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b85e37691bb76..f16fb16bdbe17 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6137,3 +6137,45 @@ bool UnableToInferProtocolLiteralType::diagnoseAsError() { return true; } + +bool MissingQuialifierInMemberRefFailure::diagnoseAsError() { + auto selectedOverload = getOverloadChoiceIfAvailable(getLocator()); + if (!selectedOverload) + return false; + + auto *UDE = cast(getRawAnchor()); + + auto baseType = getType(UDE->getBase()); + + auto methodKind = baseType->isAnyExistentialType() + ? DescriptiveDeclKind::StaticMethod + : DescriptiveDeclKind::Method; + + auto choice = selectedOverload->choice.getDeclOrNull(); + if (!choice) + return false; + + auto *DC = choice->getDeclContext(); + if (!(DC->isModuleContext() || DC->isModuleScopeContext())) { + emitDiagnostic(UDE->getLoc(), diag::member_shadows_function, UDE->getName(), + methodKind, choice->getDescriptiveKind(), + choice->getFullName()); + return true; + } + + auto qualifier = DC->getParentModule()->getName(); + + emitDiagnostic(UDE->getLoc(), diag::member_shadows_global_function, + UDE->getName(), methodKind, choice->getDescriptiveKind(), + choice->getFullName(), qualifier); + + SmallString<32> namePlusDot = qualifier.str(); + namePlusDot.push_back('.'); + + emitDiagnostic(UDE->getLoc(), diag::fix_unqualified_access_top_level_multi, + namePlusDot, choice->getDescriptiveKind(), qualifier) + .fixItInsert(UDE->getStartLoc(), namePlusDot); + + emitDiagnostic(choice, diag::decl_declared_here, choice->getFullName()); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 386ea94c8831a..67783a9fa283d 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1936,6 +1936,29 @@ class UnableToInferProtocolLiteralType final : public FailureDiagnostic { bool diagnoseAsError(); }; +/// Diagnose an attempt to reference a top-level name shadowed by a local +/// member e.g. +/// +/// ```swift +/// extension Sequence { +/// func test() -> Int { +/// return max(1, 2) +/// } +/// } +/// ``` +/// +/// Here `min` refers to a global function `min(_: T, _: T)` in `Swift` +/// module and can only be accessed by adding `Swift.` to it, because `Sequence` +/// has a member named `min` which accepts a single argument. +class MissingQuialifierInMemberRefFailure final : public FailureDiagnostic { +public: + MissingQuialifierInMemberRefFailure(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index b8aa2f7fa9a7f..2eecf54297e86 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1219,7 +1219,9 @@ AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, } bool AddQualifierToAccessTopLevelName::diagnose(bool asNote) const { - return false; + auto &cs = getConstraintSystem(); + MissingQuialifierInMemberRefFailure failure(cs, getLocator()); + return failure.diagnose(asNote); } AddQualifierToAccessTopLevelName * From 78fda9ed98830a235ecfe93ff14e949ae3d86200 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 08:57:01 -0800 Subject: [PATCH 13/69] [ConstraintSystem] Use new fix/diagnostic for name shadowing Stop filtering outer overload choices while trying to pre-check expression, instead have it always fetch those and use new fix to only attempt them in diagnostic mode (unless it's min/max situation with conditional conformances). --- lib/Sema/CSSimplify.cpp | 55 ++++++++++++- lib/Sema/TypeCheckConstraints.cpp | 77 +++---------------- test/APINotes/versioned-objc.swift | 2 +- test/Constraints/members.swift | 10 +-- ...okup_min_max_conditional_conformance.swift | 14 ++-- test/Sema/circular_decl_checking.swift | 5 +- 6 files changed, 77 insertions(+), 86 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 870eae283b2f5..ed92d05fa9fed 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3273,6 +3273,30 @@ bool ConstraintSystem::repairFailures( locator)) break; + { + auto *calleeLocator = getCalleeLocator(loc); + if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) { + if (auto overload = findSelectedOverloadFor(calleeLocator)) { + if (auto choice = overload->choice.getDeclOrNull()) { + // If this is an argument of a symetric function/operator let's + // not fix any position rather than first because we'd just end + // up with ambiguity instead of reporting an actual problem with + // mismatched type since each argument can have district bindings. + if (auto *AFD = dyn_cast(choice)) { + auto *paramList = AFD->getParameters(); + auto firstParamType = paramList->get(0)->getInterfaceType(); + if (elt.castTo().getParamIdx() > + 0 && + llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool { + return param->getInterfaceType()->isEqual(firstParamType); + })) + return true; + } + } + } + } + } + conversionsOrFixes.push_back( AllowArgumentMismatch::create(*this, lhs, rhs, loc)); break; @@ -6201,8 +6225,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (candidates.size() == 1) candidates.front()->setFavored(); - generateConstraints(candidates, memberTy, outerAlternatives, - useDC, locator); + // We *might* include any non-members that we found in outer contexts in + // some special cases, for backwards compatibility: first, we have to be + // looking for one of the special names ('min' or 'max'), and second, all + // of the inner (viable) results need to come from conditional + // conformances. The second condition is how the problem here was + // encountered: a type ('Range') was made to conditionally conform to a + // new protocol ('Sequence'), which introduced some extra methods + // ('min' and 'max') that shadowed global functions that people regularly + // called within extensions to that type (usually adding 'clamp'). + bool treatAsViable = + (member.isSimpleName("min") || member.isSimpleName("max")) && + allFromConditionalConformances(DC, baseTy, result.ViableCandidates); + + generateConstraints( + candidates, memberTy, outerAlternatives, useDC, locator, None, + /*requiresFix=*/!treatAsViable, + [&](unsigned, const OverloadChoice &) { + return treatAsViable ? nullptr + : AddQualifierToAccessTopLevelName::create( + *this, locator); + }); } } @@ -8852,6 +8895,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowInvalidUseOfTrailingClosure: case FixKind::AllowNonClassTypeToConvertToAnyObject: case FixKind::SpecifyClosureReturnType: + case FixKind::AddQualifierToAccessTopLevelName: llvm_unreachable("handled elsewhere"); } @@ -9315,7 +9359,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { case ConstraintKind::BindOverload: if (auto *fix = constraint.getFix()) { - if (recordFix(fix)) + // TODO(diagnostics): Impact should be associated with a fix unless + // it's a contextual problem, then only solver can decide what the impact + // would be in each particular situation. + auto impact = + fix->getKind() == FixKind::AddQualifierToAccessTopLevelName ? 10 : 1; + if (recordFix(fix, impact)) return SolutionKind::Error; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index de6d54af3adce..7e5b6714d6de0 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -452,20 +452,6 @@ static bool findNonMembers(ArrayRef lookupResults, return AllDeclRefs; } -/// Whether we should be looking at the outer results for a function called \c -/// name. -/// -/// This is very restrictive because it's a source compatibility issue (see the -/// if (AllConditionalConformances) { (void)findNonMembers(...); } below). -static bool shouldConsiderOuterResultsFor(DeclNameRef name) { - const StringRef specialNames[] = {"min", "max"}; - for (auto specialName : specialNames) - if (name.isSimpleName(specialName)) - return true; - - return false; -} - /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. @@ -479,8 +465,11 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (isa(DC)) lookupOptions |= NameLookupFlags::KnownPrivate; - if (shouldConsiderOuterResultsFor(Name)) - lookupOptions |= NameLookupFlags::IncludeOuterResults; + + // TODO: Include all of the possible members to give a solver a + // chance to diagnose name shadowing which requires explicit + // name/module qualifier to access top-level name. + lookupOptions |= NameLookupFlags::IncludeOuterResults; auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); @@ -625,14 +614,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // better matching candidates. if (localDeclAfterUse) { auto innerDecl = localDeclAfterUse; - - // Perform a thorough lookup if outer results was not included before. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - auto option = lookupOptions; - option |= NameLookupFlags::IncludeOuterResults; - Lookup = lookupUnqualified(DC, Name, Loc, option); - } - while (localDeclAfterUse) { if (Lookup.outerResults().empty()) { Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); @@ -649,13 +630,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true, ResultValues, isValid); } - - // Drop outer results if they are not supposed to be included. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - Lookup.filter([&](LookupResultEntry Result, bool isOuter) { - return !isOuter; - }); - } } // If we have an unambiguous reference to a type decl, form a TypeExpr. @@ -715,7 +689,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, ResultValues.clear(); bool AllMemberRefs = true; - bool AllConditionalConformances = true; ValueDecl *Base = nullptr; DeclContext *BaseDC = nullptr; for (auto Result : Lookup) { @@ -732,26 +705,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, Base = ThisBase; BaseDC = Result.getDeclContext(); - - // Check if this result is derived through a conditional conformance, - // meaning it comes from a protocol (or extension) where there's a - // conditional conformance for the type with the method in question - // (NB. that type may not be the type associated with DC, for tested types - // with static methods). - if (auto Proto = Value->getDeclContext()->getSelfProtocolDecl()) { - auto contextSelfType = - BaseDC->getInnermostTypeContext()->getDeclaredInterfaceType(); - auto conformance = conformsToProtocol( - contextSelfType, Proto, DC, - ConformanceCheckFlags::InExpression | - ConformanceCheckFlags::SkipConditionalRequirements); - - if (conformance.isInvalid() || - conformance.getConditionalRequirements().empty()) { - AllConditionalConformances = false; - } - } - continue; } @@ -774,22 +727,12 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, /*Implicit=*/true); } - // We *might* include any non-members that we found in outer contexts in - // some special cases, for backwards compatibility: first, we have to be - // looking for one of the special names - // ('shouldConsiderOuterResultsFor(Name)'), and second, all of the inner - // results need to come from conditional conformances. The second condition - // is how the problem here was encountered: a type ('Range') was made to - // conditionally conform to a new protocol ('Sequence'), which introduced - // some extra methods ('min' and 'max') that shadowed global functions that - // people regularly called within extensions to that type (usually adding - // 'clamp'). llvm::SmallVector outerAlternatives; - if (AllConditionalConformances) { - (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), - /*breakOnMember=*/false, outerAlternatives, - /*isValid=*/[&](ValueDecl *) { return true; }); - } + (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[](ValueDecl *choice) -> bool { + return !choice->isInvalid(); + }); // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on // type information. diff --git a/test/APINotes/versioned-objc.swift b/test/APINotes/versioned-objc.swift index b2b5cc76ea6c9..9f9b2b64611cd 100644 --- a/test/APINotes/versioned-objc.swift +++ b/test/APINotes/versioned-objc.swift @@ -166,7 +166,7 @@ extension PrintingInterference { func testDroppingRenamedPrints() { // CHECK-DIAGS-4: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method print(self) - // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: missing argument for parameter 'extra' in call + // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: use of 'print' refers to instance method rather than global function 'print(_:separator:terminator:)' in module 'Swift' // CHECK-DIAGS-4-NOT: [[@LINE+1]]:{{[0-9]+}}: print(self, extra: self) diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index c2de930b8b443..1720948609d3c 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -355,7 +355,7 @@ do { // rdar://problem/25341015 extension Sequence { func r25341015_1() -> Int { - return max(1, 2) // expected-error {{use of 'max' refers to instance method 'max(by:)' rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} + return max(1, 2) // expected-error {{use of 'max' refers to instance method rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} } } @@ -381,7 +381,7 @@ func r25341015() { class Bar { func baz() {} func qux() { - baz(1, 2) // expected-error {{argument passed to call that takes no arguments}} + baz(1, 2) // expected-error {{use of 'baz' refers to instance method rather than local function 'baz'}} } } } @@ -405,17 +405,17 @@ func bar_32854314() -> Int { extension Array where Element == Int { func foo() { let _ = min(foo_32854314(), bar_32854314()) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func foo(_ x: Int, _ y: Double) { let _ = min(x, y) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func bar() { let _ = min(1.0, 2) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } } diff --git a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift index 81d2e59582cfa..e79212e6aa318 100644 --- a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift +++ b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift @@ -16,7 +16,7 @@ extension ContainsMinMax { func min() {} } -func foo(_: Int, _: Int) {} +func foo(_: Int, _: Int) {} // expected-note 2 {{'foo' declared here}} protocol ContainsFoo {} extension ContainsFoo { @@ -34,15 +34,14 @@ extension NonConditional { // expected-error@-1{{use of 'min' refers to instance method}} // expected-note@-2{{use 'Swift.' to reference the global function}} - // FIXME(diagnostics): Better diagnostic in this case would be to suggest to add `name_lookup_min_max_conditional_conformance.` - // to call because name `foo` is shadowed by instance method without arguments. Would be fixed by `resolveDeclRefExpr` refactoring. - _ = foo(5, 6) // expected-error {{argument passed to call that takes no arguments}} + _ = foo(5, 6) // expected-error {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-1 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } struct Conditional {} extension Conditional: ContainsMinMax where T: ContainsMinMax {} -extension Conditional: ContainsFoo where T: ContainsFoo {} // expected-note {{requirement from conditional conformance of 'Conditional' to 'ContainsFoo'}} +extension Conditional: ContainsFoo where T: ContainsFoo {} extension Conditional { func f() { @@ -53,9 +52,8 @@ extension Conditional { // expected-warning@-1{{use of 'min' as reference to global function in module 'Swift' will change in future versions of Swift to reference instance method in generic struct 'Conditional' which comes via a conditional conformance}} // expected-note@-2{{use 'Swift.' to continue to reference the global function}} - // FIXME(diagnostics): Same as line 39, there should be only one error here about shadowing. _ = foo(5, 6) - // expected-error@-1 {{referencing instance method 'foo()' on 'Conditional' requires that 'T' conform to 'ContainsFoo'}} - // expected-error@-2 {{argument passed to call that takes no arguments}} + // expected-error@-1 {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-2 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } diff --git a/test/Sema/circular_decl_checking.swift b/test/Sema/circular_decl_checking.swift index acb78f0520803..3e8d6e2a9a542 100644 --- a/test/Sema/circular_decl_checking.swift +++ b/test/Sema/circular_decl_checking.swift @@ -23,9 +23,10 @@ class HasGenericFunc { } } -class HasProp { +class HasProp { // expected-note {{'HasProp' declared here}} var HasProp: HasProp { - return HasProp() // expected-error {{cannot call value of non-function type 'HasProp'}}{{19-21=}} + return HasProp() // expected-error {{use of 'HasProp' refers to instance method rather than class 'HasProp' in module 'circular_decl_checking'}} + // expected-note@-1 {{use 'circular_decl_checking.' to reference the class in module 'circular_decl_checking'}} {{12-12=circular_decl_checking.}} } var SomethingElse: SomethingElse? { // expected-error {{use of undeclared type 'SomethingElse'}} return nil From ec95397a76341957cf190e6fd877420cc4c62eb5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 10:02:44 -0800 Subject: [PATCH 14/69] [CSDiag] NFC: Remove obsolete name shadowing diagnostics --- lib/Sema/CSDiag.cpp | 225 -------------------------------------------- 1 file changed, 225 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 1910e242ce091..5e755aad3e72a 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -201,10 +201,6 @@ class FailureDiagnosis :public ASTVisitor{ ContextualTypePurpose CTP, Type suggestedType = Type()); - bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, - CalleeCandidateInfo &CCI, - ArrayRef argLabels); - private: /// Validate potential contextual type for type-checking one of the /// sub-expressions, usually correct/valid types are the ones which @@ -964,223 +960,6 @@ decomposeArgType(Type argType, ArrayRef argLabels) { return result; } -bool FailureDiagnosis::diagnoseImplicitSelfErrors( - Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, - ArrayRef argLabels) { - // If candidate list is empty it means that problem is somewhere else, - // since we need to have candidates which might be shadowing other funcs. - if (CCI.empty() || !CCI[0].getDecl()) - return false; - - auto &ctx = CS.getASTContext(); - // Call expression is formed as 'foo.bar' where 'foo' might be an - // implicit "Self" reference, such use wouldn't provide good diagnostics - // for situations where instance members have equal names to functions in - // Swift Standard Library e.g. min/max. - auto UDE = dyn_cast(fnExpr); - if (!UDE) - return false; - - auto baseExpr = dyn_cast(UDE->getBase()); - if (!baseExpr) - return false; - - auto baseDecl = baseExpr->getDecl(); - if (!baseExpr->isImplicit() || baseDecl->getFullName() != ctx.Id_self) - return false; - - // Our base expression is an implicit 'self.' reference e.g. - // - // extension Sequence { - // func test() -> Int { - // return max(1, 2) - // } - // } - // - // In this example the Sequence class already has two methods named 'max' - // none of which accept two arguments, but there is a function in - // Swift Standard Library called 'max' which does accept two arguments, - // so user might have called that by mistake without realizing that - // compiler would add implicit 'self.' prefix to the call of 'max'. - auto argType = CS.getType(argExpr); - // If argument wasn't properly type-checked, let's retry without changing AST. - if (!argType || argType->hasUnresolvedType() || argType->hasTypeVariable() || - argType->hasTypeParameter()) { - auto *argTuple = dyn_cast(argExpr); - if (!argTuple) { - // Bail out if we don't have a well-formed argument list. - return false; - } - - // Let's type check individual argument expressions without any - // contextual information to try to recover an argument type that - // matches what the user actually wrote instead of what the typechecker - // expects. - SmallVector elts; - for (unsigned i = 0, e = argTuple->getNumElements(); i < e; ++i) { - ConcreteDeclRef ref = nullptr; - auto *el = argTuple->getElement(i); - auto typeResult = - TypeChecker::getTypeOfExpressionWithoutApplying(el, CS.DC, ref); - if (!typeResult) - return false; - auto flags = ParameterTypeFlags().withInOut(typeResult->is()); - elts.push_back(TupleTypeElt(typeResult->getInOutObjectType(), - argTuple->getElementName(i), - flags)); - } - - argType = TupleType::get(elts, CS.getASTContext()); - } - - auto typeKind = argType->getKind(); - if (typeKind != TypeKind::Tuple && typeKind != TypeKind::Paren) - return false; - - // If argument type couldn't be properly resolved or has errors, - // we can't diagnose anything in here, it points to the different problem. - if (isUnresolvedOrTypeVarType(argType) || argType->hasError()) - return false; - - auto context = CS.DC; - using CandidateMap = - llvm::SmallDenseMap>; - - auto getBaseKind = [](ValueDecl *base) -> DescriptiveDeclKind { - DescriptiveDeclKind kind = DescriptiveDeclKind::Module; - if (!base) - return kind; - - auto context = base->getDeclContext(); - do { - if (isa(context)) - return DescriptiveDeclKind::Extension; - - if (auto nominal = dyn_cast(context)) { - kind = nominal->getDescriptiveKind(); - break; - } - - context = context->getParent(); - } while (context); - - return kind; - }; - - auto diagnoseShadowing = [&](ValueDecl *base, - ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(base ? base->getInterfaceType() : nullptr, - candidates, CCI.hasTrailingClosure, CS, - base); - - calleeInfo.filterListArgs(decomposeArgType(argType, argLabels)); - - auto diagnostic = diag::member_shadows_global_function_near_match; - switch (calleeInfo.closeness) { - case CC_Unavailable: - case CC_Inaccessible: - case CC_SelfMismatch: - case CC_ArgumentLabelMismatch: - case CC_ArgumentCountMismatch: - case CC_GeneralMismatch: - return false; - - case CC_NonLValueInOut: - case CC_OneArgumentNearMismatch: - case CC_OneArgumentMismatch: - case CC_OneGenericArgumentNearMismatch: - case CC_OneGenericArgumentMismatch: - case CC_ArgumentNearMismatch: - case CC_ArgumentMismatch: - case CC_GenericNonsubstitutableMismatch: - break; // Near match cases - - case CC_ExactMatch: - diagnostic = diag::member_shadows_global_function; - break; - } - - auto choice = calleeInfo.candidates[0].getDecl(); - auto baseKind = getBaseKind(base); - auto baseName = getBaseName(choice->getDeclContext()); - - auto origCandidate = CCI[0].getDecl(); - ctx.Diags.diagnose(UDE->getLoc(), diagnostic, UDE->getName(), - origCandidate->getDescriptiveKind(), - origCandidate->getFullName(), - choice->getDescriptiveKind(), - choice->getFullName(), baseKind, baseName); - - auto topLevelDiag = diag::fix_unqualified_access_top_level; - if (baseKind == DescriptiveDeclKind::Module) - topLevelDiag = diag::fix_unqualified_access_top_level_multi; - - emitFixItForExplicitlyQualifiedReference(ctx.Diags, UDE, topLevelDiag, - baseName, - choice->getDescriptiveKind()); - - for (auto &candidate : calleeInfo.candidates) { - if (auto decl = candidate.getDecl()) - ctx.Diags.diagnose(decl, diag::decl_declared_here, decl->getFullName()); - } - - return true; - }; - - // For each of the parent contexts, let's try to find any candidates - // which have the same name and the same number of arguments as callee. - while (context->getParent()) { - auto result = - TypeChecker::lookupUnqualified(context, UDE->getName(), UDE->getLoc()); - context = context->getParent(); - - if (!result || result.empty()) - continue; - - CandidateMap candidates; - for (const auto &candidate : result) { - auto base = candidate.getBaseDecl(); - auto decl = candidate.getValueDecl(); - if ((base && base->isInvalid()) || decl->isInvalid()) - continue; - - // If base is present but it doesn't represent a valid nominal, - // we can't use current candidate as one of the choices. - if (base && !base->getInterfaceType()->getNominalOrBoundGenericNominal()) - continue; - - auto context = decl->getDeclContext(); - // We are only interested in static or global functions, because - // there is no way to call anything else properly. - if (!decl->isStatic() && !context->isModuleScopeContext()) - continue; - - OverloadChoice choice(base ? base->getInterfaceType() : nullptr, - decl, UDE->getFunctionRefKind()); - - if (base) { // Let's group all of the candidates have a common base. - candidates[base].push_back(choice); - continue; - } - - // If there is no base, it means this is one of the global functions, - // let's try to diagnose its shadowing inline. - if (diagnoseShadowing(base, choice)) - return true; - } - - if (candidates.empty()) - continue; - - for (const auto &candidate : candidates) { - if (diagnoseShadowing(candidate.getFirst(), candidate.getSecond())) - return true; - } - } - - return false; -} - // Extract expression for failed argument number static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { if (auto *TE = dyn_cast(argExpr)) @@ -1202,10 +981,6 @@ static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, Expr *fnExpr, Expr *argExpr, ArrayRef argLabels) { - // Try to diagnose errors related to the use of implicit self reference. - if (diagnoseImplicitSelfErrors(fnExpr, argExpr, CCI, argLabels)) - return true; - // If we have a failure where the candidate set differs on exactly one // argument, and where we have a consistent mismatch across the candidate set // (often because there is only one candidate in the set), then diagnose this From 217c343eb6dd11809beb56f49a91c39dfd9de4b5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 11:54:11 -0800 Subject: [PATCH 15/69] [Diagnostics] NFC: Fix name shadowing diagnostic comment to refer to `max` --- lib/Sema/CSDiagnostics.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 67783a9fa283d..2f3c878a40777 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1947,9 +1947,9 @@ class UnableToInferProtocolLiteralType final : public FailureDiagnostic { /// } /// ``` /// -/// Here `min` refers to a global function `min(_: T, _: T)` in `Swift` +/// Here `max` refers to a global function `max(_: T, _: T)` in `Swift` /// module and can only be accessed by adding `Swift.` to it, because `Sequence` -/// has a member named `min` which accepts a single argument. +/// has a member named `max` which accepts a single argument. class MissingQuialifierInMemberRefFailure final : public FailureDiagnostic { public: MissingQuialifierInMemberRefFailure(ConstraintSystem &cs, From 93c23211062cdee54cf0471ca5c04f74724e06c4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 19:49:57 -0800 Subject: [PATCH 16/69] [TypeChecker] Add types nested in protocol to lookup results Although such functionality is not yet supported we have to mirror AST lookup and add such members into results, otherwise there is a risk of inner/outer results mismatch. --- lib/Sema/TypeCheckNameLookup.cpp | 6 ++++++ test/decl/nested/protocol.swift | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 7ad524f45df3f..707d454122c5e 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -195,6 +195,12 @@ namespace { addResult(found); return; } + } else if (isa(found)) { + // Declaring nested types inside other types is currently + // not supported by lookup would still return such members + // so we have to account for that here as well. + addResult(found); + return; } // FIXME: the "isa()" check will be wrong for diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index ea5c973467b10..6d25970753ba7 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -19,7 +19,7 @@ class OuterGenericClass { } } -protocol OuterProtocol { // expected-note{{'OuterProtocol' declared here}} +protocol OuterProtocol { associatedtype Hen protocol InnerProtocol { // expected-error{{protocol 'InnerProtocol' cannot be nested inside another declaration}} associatedtype Rooster @@ -32,7 +32,7 @@ struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int func f() { let _ = InnerProtocol.self } - // expected-error@-1 {{use of unresolved identifier 'InnerProtocol'}} + // expected-error@-1 {{protocol 'InnerProtocol' can only be used as a generic constraint because it has Self or associated type requirements}} } protocol Racoon { From 4bd1ffc67f36a14254dc4b3fdb472f766d264f7e Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 27 Jan 2020 16:27:14 -0800 Subject: [PATCH 17/69] Serialize whether a VarDecl is a top-level global. The current way that VarDecl::isLazilyInitializedGlobal() is implemented does not work in the debugger, since the DeclContext of all VarDecls are deserialized Swift modules. By adding a bit to the VarDecl we can recover the fact that a VarDecl was in fact a global even in the debugger. --- include/swift/AST/Decl.h | 11 +++++++++-- lib/AST/Decl.cpp | 7 ++----- lib/Parse/ParseDecl.cpp | 1 + lib/Serialization/Deserialization.cpp | 3 +++ lib/Serialization/ModuleFormat.h | 1 + lib/Serialization/Serialization.cpp | 1 + 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 789de8f2df163..b2727cd5572fb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -335,7 +335,7 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1+1, /// Encodes whether this is a 'let' binding. Introducer : 1, @@ -358,7 +358,10 @@ class alignas(1 << DeclAlignInBits) Decl { IsLazyStorageProperty : 1, /// Whether this is the backing storage for a property wrapper. - IsPropertyWrapperBackingProperty : 1 + IsPropertyWrapperBackingProperty : 1, + + /// Whether this is a lazily top-level global variable from the main file. + IsTopLevelGlobal : 1 ); SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits, @@ -5084,6 +5087,10 @@ class VarDecl : public AbstractStorageDecl { Bits.VarDecl.IsLazyStorageProperty = IsLazyStorage; } + /// True if this is a top-level global variable from the main source file. + bool isTopLevelGlobal() const { return Bits.VarDecl.IsTopLevelGlobal; } + void setTopLevelGlobal(bool b) { Bits.VarDecl.IsTopLevelGlobal = b; } + /// Retrieve the custom attributes that attach property wrappers to this /// property. The returned list contains all of the attached property wrapper attributes in source order, /// which means the outermost wrapper attribute is provided first. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 25f90c3d45002..8bdb94abff1c9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5312,6 +5312,7 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer, Bits.VarDecl.IsLazyStorageProperty = false; Bits.VarDecl.HasNonPatternBindingInit = false; Bits.VarDecl.IsPropertyWrapperBackingProperty = false; + Bits.VarDecl.IsTopLevelGlobal = false; } Type VarDecl::getType() const { @@ -5410,11 +5411,7 @@ bool VarDecl::isLazilyInitializedGlobal() const { // Top-level global variables in the main source file and in the REPL are not // lazily initialized. - auto sourceFileContext = dyn_cast(getDeclContext()); - if (!sourceFileContext) - return true; - - return !sourceFileContext->isScriptMode(); + return !isTopLevelGlobal(); } SourceRange VarDecl::getSourceRange() const { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 15b3c1b33d293..6edbf335b8d78 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5840,6 +5840,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; setLocalDiscriminator(VD); + VD->setTopLevelGlobal(topLevelDecl); // Set original declaration in `@differentiable` attributes. setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4c62bb3728b1a..e7ae80813ba74 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2657,6 +2657,7 @@ class DeclDeserializer { uint8_t rawIntroducer; bool isGetterMutating, isSetterMutating; bool isLazyStorageProperty; + bool isTopLevelGlobal; DeclID lazyStorageID; unsigned numAccessors, numBackingProperties; uint8_t readImpl, writeImpl, readWriteImpl, opaqueReadOwnership; @@ -2673,6 +2674,7 @@ class DeclDeserializer { hasNonPatternBindingInit, isGetterMutating, isSetterMutating, isLazyStorageProperty, + isTopLevelGlobal, lazyStorageID, opaqueReadOwnership, readImpl, writeImpl, readWriteImpl, @@ -2804,6 +2806,7 @@ class DeclDeserializer { } var->setLazyStorageProperty(isLazyStorageProperty); + var->setTopLevelGlobal(isTopLevelGlobal); // If there are any backing properties, record them. if (numBackingProperties > 0) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 265ea709a7c89..9c32d255477b4 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1191,6 +1191,7 @@ namespace decls_block { BCFixed<1>, // is getter mutating? BCFixed<1>, // is setter mutating? BCFixed<1>, // is this the backing storage for a lazy property? + BCFixed<1>, // top level global? DeclIDField, // if this is a lazy property, this is the backing storage OpaqueReadOwnershipField, // opaque read ownership ReadImplKindField, // read implementation diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index ce49f72c4bccc..18abc58342f53 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3255,6 +3255,7 @@ class Serializer::DeclSerializer : public DeclVisitor { var->isGetterMutating(), var->isSetterMutating(), var->isLazyStorageProperty(), + var->isTopLevelGlobal(), S.addDeclRef(lazyStorage), accessors.OpaqueReadOwnership, accessors.ReadImpl, From 15c1b4eca7a665aa64082fc01d33d1d52f9889f5 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 28 Jan 2020 16:16:04 -0800 Subject: [PATCH 18/69] Compile libraries in testcases with -parse-as-library (NFC) This is in preparation to a change in serialization of global variables where this detail will matter. --- test/DebugInfo/inlinescopes.swift | 2 +- test/SILGen/expressions.swift | 2 +- test/SILGen/global_init_attribute.swift | 2 +- test/SILGen/global_resilience.swift | 2 +- test/Serialization/Recovery/typedefs.swift | 7 +++++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/test/DebugInfo/inlinescopes.swift b/test/DebugInfo/inlinescopes.swift index 769b8a38ce8f0..6c4a3a4fefc3d 100644 --- a/test/DebugInfo/inlinescopes.swift +++ b/test/DebugInfo/inlinescopes.swift @@ -6,7 +6,7 @@ // RUN: %FileCheck %s -check-prefix=TRANSPARENT-CHECK < %t.ll // CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main{{.*}} -// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %4), !dbg ![[CALL:.*]] +// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %{{.*}}), !dbg ![[CALL:.*]] // CHECK-DAG: ![[TOPLEVEL:.*]] = !DIFile(filename: "{{.*}}inlinescopes.swift" import FooBar diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index f642702602900..54654a6ee7b29 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t - +// RUN: echo "public var x = Int()" | %target-swift-frontend -parse-as-library -module-name FooBar -emit-module -o %t - // RUN: %target-swift-emit-silgen -parse-stdlib -module-name expressions %s -I%t -disable-access-control | %FileCheck %s import Swift diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index f47bde0192a73..95167c7127078 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-module -o %t %S/Inputs/def_global.swift +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-module -o %t %S/Inputs/def_global.swift // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -I %t %s | %FileCheck %s // // Test that SILGen uses the "global_init" attribute for all global diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index c05720d0713eb..78b425a9ed684 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift +// RUN: %target-swift-frontend -emit-module -parse-as-library -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift // RUN: %target-swift-emit-silgen -I %t -enable-library-evolution -parse-as-library %s | %FileCheck %s // RUN: %target-swift-emit-sil -I %t -O -enable-library-evolution -parse-as-library %s | %FileCheck --check-prefix=CHECK-OPT %s diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index a9fab839e045b..60d0eda010d56 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -1,4 +1,7 @@ // RUN: %empty-directory(%t) + +// Cannot use -parse-as-library here because that would compile also the +// #if VERIFY path, which contains top-level code. // RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s @@ -23,9 +26,9 @@ import Lib // CHECK-SIL-LABEL: sil hidden [ossa] @$s8typedefs11testSymbolsyyF func testSymbols() { // Check that the symbols are not using 'Bool'. - // CHECK-SIL: function_ref @$s3Lib1xs5Int32Vvau + // CHECK-SIL: global_addr @$s3Lib9usesAssocs5Int32VSgvp _ = Lib.x - // CHECK-SIL: function_ref @$s3Lib9usesAssocs5Int32VSgvau + // CHECK-SIL: global_addr @$s3Lib1xs5Int32Vvp _ = Lib.usesAssoc } // CHECK-SIL: end sil function '$s8typedefs11testSymbolsyyF' From 3fff5dd064fd99eb905cbbec37cc8e6e9c19762c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 29 Jan 2020 09:40:38 -0800 Subject: [PATCH 19/69] [ConstraintSystem] Extend metatype instance type mismatch coverage Originally type mismatches associated with metatypes were only covered for coercions but fix coverage grew since then and now it makes sense to remove special case and let `repairFailures` take care of it. --- lib/Sema/CSSimplify.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 870eae283b2f5..2a24f5f95d981 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4068,16 +4068,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, auto result = matchTypes(instanceType1, instanceType2, subKind, subflags, locator.withPathElement(ConstraintLocator::InstanceType)); - if (shouldAttemptFixes() && result.isFailure()) { - auto *anchor = locator.getAnchor(); - if (anchor && isa(anchor)) { - auto *fix = - ContextualMismatch::create(*this, instanceType1, instanceType2, - getConstraintLocator(locator)); - conversionsOrFixes.push_back(fix); - break; - } - } + + // If matching of the instance types resulted in the failure make sure + // to give `repairFailure` a chance to run to attempt to fix the issue. + if (shouldAttemptFixes() && result.isFailure()) + break; + return result; } From 0bd1e61a20652577b00017c5cd087c2dbe73d2f9 Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Wed, 29 Jan 2020 10:09:32 -0800 Subject: [PATCH 20/69] [build][gardening] adjust one more framework path (#29529) This is a follow to #29507 addressing one more place in which we need to collate -F with the framework path Addresses rdar://problem/58934566 --- cmake/modules/AddSwift.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 931d751a03459..a89442f485d85 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -2133,7 +2133,10 @@ function(add_swift_target_library name) list(APPEND swiftlib_swift_compile_flags_all "-Fsystem" "${ios_support_frameworks_path}") list(APPEND swiftlib_c_compile_flags_all "-iframework" "${ios_support_frameworks_path}") - list(APPEND swiftlib_link_flags_all "-F" "${ios_support_frameworks_path}") + # We collate -F with the framework path to avoid unwanted deduplication + # of options by target_compile_options -- this way no undesired + # side effects are introduced should a new search path be added. + list(APPEND swiftlib_link_flags_all "-F${ios_support_frameworks_path}") endif() if(sdk IN_LIST SWIFT_APPLE_PLATFORMS AND SWIFTLIB_IS_SDK_OVERLAY) From 17d9d20275aac858104e92b3d33e0b5bdcfe0344 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 29 Jan 2020 10:23:18 -0800 Subject: [PATCH 21/69] Improve one of the comments in COWArrayOpts. --- lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp index c48c161cb28ed..b4265a2690aa3 100644 --- a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp @@ -849,11 +849,16 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable, // Check whether we can hoist make_mutable based on the operations that are // in the loop. - // Note that in this case we don't verify that the array buffer is not aliased - // and therefore we must be conservative if the make_mutable is executed - // conditionally (i.e. doesn't dominate all exit blocks). - // The test SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally - // shows the problem. + // + // Hoisting make_mutable releases the original array storage. If an alias of + // that storage is accessed on any path reachable from the loop header that + // doesn't already pass through the make_mutable, then hoisting is + // illegal. hasLoopOnlyDestructorSafeArrayOperations checks that the array + // storage is not accessed within the loop. However, this does not include + // paths exiting the loop. Rather than analyzing code outside the loop, simply + // check that the original make_mutable dominates all exits. The test + // SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally shows + // the problem. if (hasLoopOnlyDestructorSafeArrayOperations() && dominatesExits) { // Done. We can hoist the make_mutable. // We still need the array uses later to check if we can add loads to From 0440e914519f1212e00e30e22192ced00a21cdef Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 29 Jan 2020 10:54:18 -0800 Subject: [PATCH 22/69] Try setting mod times explicitly to fix unreproducible ci failure. --- test/Frontend/type-fingerprint.swift | 29 ++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/test/Frontend/type-fingerprint.swift b/test/Frontend/type-fingerprint.swift index 8d9188e8362c4..aa08da31bedf2 100644 --- a/test/Frontend/type-fingerprint.swift +++ b/test/Frontend/type-fingerprint.swift @@ -12,16 +12,27 @@ // RUN: cp %S/Inputs/type-fingerprint/{main,a}.swift %t // RUN: cp %S/Inputs/type-fingerprint/ofm.json %t // RUN: cp %S/Inputs/type-fingerprint/b0.swift %t/b.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + // RUN: cd %t && %swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output1 -// RUN: cp %t/b.swiftdeps %t/b1.swiftdeps +// only-run-for-debugging: cp %t/b.swiftdeps %t/b1.swiftdeps // Change one type, but uses of all types get recompiled // RUN: cp %S/Inputs/type-fingerprint/b1.swift %t/b.swift + +// Seeing weird failure on CI, so ensure that b.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/b.swift + // RUN: cd %t && %swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output2 -// RUN: cp %t/b.swiftdeps %t/b2.swiftdeps +// Save for debugging: +// only-run-for-debugging: cp %t/b.swiftdeps %t/b2.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 @@ -40,16 +51,26 @@ // RUN: cp %S/Inputs/type-fingerprint/{main,a}.swift %t // RUN: cp %S/Inputs/type-fingerprint/ofm.json %t // RUN: cp %S/Inputs/type-fingerprint/b0.swift %t/b.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + // RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output3 -// RUN: cp %t/b.swiftdeps %t/b3.swiftdeps +// only-run-for-debugging: cp %t/b.swiftdeps %t/b3.swiftdeps // Change one type, only uses of that type get recompiled // RUN: cp %S/Inputs/type-fingerprint/b1.swift %t/b.swift + +// Seeing weird failure on CI, so ensure that b.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/b.swift + // RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output4 -// RUN: cp %t/b.swiftdeps %t/b4.swiftdeps +// only-run-for-debugging: cp %t/b.swiftdeps %t/b4.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINB-RECOMPILED %s < %t/output4 From 4028ac245a8556353b5ba27ffe1c8f3d8f17fe03 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 29 Jan 2020 11:15:53 -0800 Subject: [PATCH 23/69] EscapeAnalysis: add support for access markers. This will be critical when OSSA relies on access markers everywhere and more passes are converted to OSSA. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 1 + test/SILOptimizer/escape_analysis.sil | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 62e0059646990..e5b1eec09a578 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -122,6 +122,7 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) { case ValueKind::StructElementAddrInst: case ValueKind::StructExtractInst: case ValueKind::TupleElementAddrInst: + case ValueKind::BeginAccessInst: case ValueKind::UncheckedTakeEnumDataAddrInst: case ValueKind::UncheckedEnumDataInst: case ValueKind::MarkDependenceInst: diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index eddfac11b7f09..e510fbadcda74 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -1888,3 +1888,25 @@ bb0(%0 : @guaranteed $@sil_unowned Builtin.NativeObject): %1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject return %1 : $Builtin.NativeObject } + +// Test begin_access. It should look like a derived pointer. +// CHECK-LABEL: CG of testAccessMarkerHelper +sil hidden @testAccessMarkerHelper : $@convention(thin) (@inout SomeData) -> () { +bb0(%0 : $*SomeData): + %1 = tuple () + return %1 : $() +} + +// CHECK-LABEL: CG of testAccessMarker +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testAccessMarker : $@convention(thin) (@inout SomeData) -> () { +bb0(%0 : $*SomeData): + %1 = begin_access [modify] [static] %0 : $*SomeData + %2 = function_ref @testAccessMarkerHelper : $@convention(thin) (@inout SomeData) -> () + %3 = apply %2(%1) : $@convention(thin) (@inout SomeData) -> () + end_access %1 : $*SomeData + %5 = tuple () + return %5 : $() +} From b8960133ff57cade7f1d1254585c79502b820b8b Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 29 Jan 2020 11:45:58 -0800 Subject: [PATCH 24/69] Add source annotation to SILPrinter under flag -sil-print-sourceinfo (#29444) * Add source annotation to SILPrinter under flag -sil-print-sourceinfo rdar://58365252 --- include/swift/Basic/SourceManager.h | 3 +++ lib/Basic/SourceLoc.cpp | 15 ++++++++++++++- lib/SIL/SILPrinter.cpp | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/swift/Basic/SourceManager.h b/include/swift/Basic/SourceManager.h index ed4f5dc34126e..7cf754731c1ab 100644 --- a/include/swift/Basic/SourceManager.h +++ b/include/swift/Basic/SourceManager.h @@ -243,6 +243,9 @@ class SourceManager { llvm::Optional resolveOffsetForEndOfLine(unsigned BufferId, unsigned Line) const; + /// Get the length of the line + llvm::Optional getLineLength(unsigned BufferId, unsigned Line) const; + SourceLoc getLocForLineCol(unsigned BufferId, unsigned Line, unsigned Col) const { auto Offset = resolveFromLineCol(BufferId, Line, Col); return Offset.hasValue() ? getLocForOffset(BufferId, Offset.getValue()) : diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index 7c66731e41fe1..76547ff2e91f6 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -331,10 +331,20 @@ SourceManager::resolveOffsetForEndOfLine(unsigned BufferId, return resolveFromLineCol(BufferId, Line, ~0u); } +llvm::Optional +SourceManager::getLineLength(unsigned BufferId, unsigned Line) const { + auto BegOffset = resolveFromLineCol(BufferId, Line, 0); + auto EndOffset = resolveFromLineCol(BufferId, Line, ~0u); + if (BegOffset && EndOffset) { + return EndOffset.getValue() - BegOffset.getValue(); + } + return None; +} + llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, unsigned Line, unsigned Col) const { - if (Line == 0 || Col == 0) { + if (Line == 0) { return None; } const bool LineEnd = Col == ~0u; @@ -353,6 +363,9 @@ llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, return None; } Ptr = LineStart; + if (Col == 0) { + return Ptr - InputBuf->getBufferStart(); + } // The <= here is to allow for non-inclusive range end positions at EOF for (; ; ++Ptr) { --Col; diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 4900797283f88..976b9c790185c 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -64,6 +64,10 @@ llvm::cl::opt SILPrintDebugInfo("sil-print-debuginfo", llvm::cl::init(false), llvm::cl::desc("Include debug info in SIL output")); +llvm::cl::opt +SILPrintSourceInfo("sil-print-sourceinfo", llvm::cl::init(false), + llvm::cl::desc("Include source annotation in SIL output")); + llvm::cl::opt SILPrintGenericSpecializationInfo( "sil-print-generic-specialization-info", llvm::cl::init(false), llvm::cl::desc("Include generic specialization" @@ -618,7 +622,22 @@ class SILPrinter : public SILInstructionVisitor { } *this << '\n'; + const auto &SM = BB->getModule().getASTContext().SourceMgr; + Optional PrevLoc; for (const SILInstruction &I : *BB) { + if (SILPrintSourceInfo) { + auto CurSourceLoc = I.getLoc().getSourceLoc(); + if (CurSourceLoc.isValid()) { + if (!PrevLoc || SM.getLineNumber(CurSourceLoc) > SM.getLineNumber(PrevLoc->getSourceLoc())) { + auto Buffer = SM.findBufferContainingLoc(CurSourceLoc); + auto Line = SM.getLineNumber(CurSourceLoc); + auto LineLength = SM.getLineLength(Buffer, Line); + PrintState.OS << " // " << SM.extractText({SM.getLocForLineCol(Buffer, Line, 0), LineLength.getValueOr(0)}) << + "\tSourceLoc: " << SM.getDisplayNameForLoc(CurSourceLoc) << ":" << Line << "\n"; + PrevLoc = I.getLoc(); + } + } + } Ctx.printInstructionCallBack(&I); if (SILPrintGenericSpecializationInfo) { if (auto AI = ApplySite::isa(const_cast(&I))) From 3c86061de8a2a6474860ae209b3e70ce6dc17866 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 28 Jan 2020 17:11:15 -0800 Subject: [PATCH 25/69] Sema: Fix module interface printing of inherited generic initializers Make sure we use the sugared form of GenericTypeParamType and not the canonical type. Fixes . --- lib/Sema/CodeSynthesis.cpp | 6 ++---- test/ModuleInterface/inherited-generic-parameters.swift | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index a087a878ba1d5..85e8249f4c9bd 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -411,10 +411,8 @@ configureGenericDesignatedInitOverride(ASTContext &ctx, auto *gp = cast(type); if (gp->getDepth() < superclassDepth) return Type(gp).subst(subMap); - return CanGenericTypeParamType::get( - gp->getDepth() - superclassDepth + depth, - gp->getIndex(), - ctx); + return genericParams->getParams()[gp->getIndex()] + ->getDeclaredInterfaceType(); }; auto lookupConformanceFn = diff --git a/test/ModuleInterface/inherited-generic-parameters.swift b/test/ModuleInterface/inherited-generic-parameters.swift index 4a538a33942b7..1be75a69d7311 100644 --- a/test/ModuleInterface/inherited-generic-parameters.swift +++ b/test/ModuleInterface/inherited-generic-parameters.swift @@ -15,6 +15,10 @@ public class Base { // CHECK-NEXT: public init(x: @escaping (In) -> Out) public init(x: @escaping (In) -> Out) {} + +// CHECK-NEXT: public init(_: A, _: A) + public init(_: A, _: A) {} + // CHECK: } } @@ -22,6 +26,7 @@ public class Base { public class Derived : Base { // CHECK-NEXT: {{(@objc )?}}deinit // CHECK-NEXT: override public init(x: @escaping (T) -> T) +// CHECK-NEXT: override public init(_ argument: A, _ argument: A) // CHECK-NEXT: } } From 2e1e4284ac3ebc64a0cedee59aa88b1c7ec5b3be Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 29 Jan 2020 13:31:50 -0800 Subject: [PATCH 26/69] Added ability to add fingerprint via # --- include/swift/AST/FineGrainedDependencies.h | 1 + ...endenciesSourceFileDepGraphConstructor.cpp | 37 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 50cba234d0e46..49f0b755f9e39 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -799,6 +799,7 @@ class SourceFileDepGraph { compoundNamesByRDK); static constexpr char noncascadingOrPrivatePrefix = '#'; + static constexpr char nameFingerprintSeparator = ','; static std::string noncascading(std::string name); diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index a2cb94342861f..eba5fe33192bb 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -848,12 +848,29 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( static StringRef stripPrefix(const StringRef name) { return name.ltrim(SourceFileDepGraph::noncascadingOrPrivatePrefix); } +static StringRef stripFingerprint(const StringRef nameAndFingerprint) { + return nameAndFingerprint.split(SourceFileDepGraph::nameFingerprintSeparator) + .first; +} +static StringRef stripName(const StringRef nameAndFingerprint) { + return nameAndFingerprint.split(SourceFileDepGraph::nameFingerprintSeparator) + .second; +} +static std::string extractName(const StringRef prefixNameFingerprint) { + return stripFingerprint(stripPrefix(prefixNameFingerprint)).str(); +} +static Optional extractFingerprint( + const StringRef prefixNameFingerprint) { + const auto fp = stripName(stripPrefix(prefixNameFingerprint)); + return fp.empty() ? None : Optional(fp.str()); +} static std::vector getBaseNameProvides(ArrayRef simpleNames) { std::vector result; for (StringRef n : simpleNames) - result.push_back(ContextNameFingerprint("", stripPrefix(n).str(), None)); + result.push_back(ContextNameFingerprint("", extractName(n), + extractFingerprint(n))); return result; } @@ -861,7 +878,8 @@ static std::vector getMangledHolderProvides(ArrayRef simpleNames) { std::vector result; for (StringRef n : simpleNames) - result.push_back(ContextNameFingerprint(stripPrefix(n).str(), "", None)); + result.push_back(ContextNameFingerprint(extractName(n), "", + extractFingerprint(n))); return result; } @@ -869,12 +887,15 @@ static std::vector getCompoundProvides( ArrayRef> compoundNames) { std::vector result; for (const auto &p : compoundNames) - result.push_back(ContextNameFingerprint(stripPrefix(p.first), - stripPrefix(p.second), None)); + result.push_back(ContextNameFingerprint(extractName(p.first), + extractName(p.second), + extractFingerprint(p.second))); return result; } -static bool cascades(const std::string &s) { return s.empty() || s[0] != SourceFileDepGraph::noncascadingOrPrivatePrefix; } +static bool cascades(const std::string &s) { + return s.empty() || s[0] != SourceFileDepGraph::noncascadingOrPrivatePrefix; +} // Use '_' as a prefix for a file-private member static bool isPrivate(const std::string &s) { @@ -904,7 +925,8 @@ getCompoundDepends( // (On Linux, the compiler needs more verbosity than: // result.push_back({{n, "", false}, cascades(n)}); result.push_back( - std::make_pair(std::make_tuple(stripPrefix(n), std::string(), false), cascades(n))); + std::make_pair(std::make_tuple(stripPrefix(n), std::string(), false), + cascades(n))); } for (auto &p : compoundNames) { // Likewise, for Linux expand the following out: @@ -932,7 +954,8 @@ SourceFileDepGraph SourceFileDepGraph::simulateLoad( SourceFileDepGraphConstructor c( swiftDepsFilename, includePrivateDeps, hadCompilationError, interfaceHash, getSimpleDepends(simpleNamesByRDK[dependsTopLevel]), - getCompoundDepends(simpleNamesByRDK[dependsNominal], compoundNamesByRDK[dependsMember]), + getCompoundDepends(simpleNamesByRDK[dependsNominal], + compoundNamesByRDK[dependsMember]), getSimpleDepends(simpleNamesByRDK[dependsDynamicLookup]), getExternalDepends(simpleNamesByRDK[dependsExternal]), {}, // precedence groups From e13ea48dcdbee74a5641d98b463737411a1473a7 Mon Sep 17 00:00:00 2001 From: Chris Amanse Date: Wed, 29 Jan 2020 14:14:12 -0800 Subject: [PATCH 27/69] Private method should be for non-Windows only --- stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 6d79bd124e976..54517a901b117 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -82,6 +82,7 @@ public func _stdlib_thread_barrier_init( return 0 } +#if !os(Windows) private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t>) -> CInt { guard pthread_mutex_init(barrier.pointee.mutex!, nil) == 0 else { return -1 @@ -92,6 +93,7 @@ private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutable } return 0 } +#endif public func _stdlib_thread_barrier_destroy( _ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t> From d2ad3dc3259c9a8d3eadcf58a4ac7977d040ca8b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 29 Jan 2020 15:29:13 -0800 Subject: [PATCH 28/69] [NFC] Commit a regression test for rdar://58960414 --- .../Headers/CategoryOverrides.h | 13 ++++++++ .../PrivateHeaders/Private.h | 4 +++ ...bjc_redeclared_properties_categories.swift | 30 +++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h index 03c13252ac497..4c66878d60f8a 100644 --- a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h @@ -19,3 +19,16 @@ typedef struct SomeStruct_s { @interface MyDerivedClass : MyBaseClass @property (nonatomic, strong, nullable) Base *derivedMember; @end + +typedef enum { + Caster, + Grantulated, + Confectioners, + Cane, + Demerara, + Turbinado, +} RefinedSugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private)); + +@interface Refinery : Base +@property (nonatomic, readonly) RefinedSugar sugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private)); +@end diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h index d52dd6c550ec9..0c4ed45702bca 100644 --- a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h @@ -14,3 +14,7 @@ @interface MyBaseClass () @end + +@interface Refinery () +@property (nonatomic, readwrite) RefinedSugar sugar; +@end diff --git a/test/ClangImporter/objc_redeclared_properties_categories.swift b/test/ClangImporter/objc_redeclared_properties_categories.swift index 0281251b87a75..7cf1ae534e37b 100644 --- a/test/ClangImporter/objc_redeclared_properties_categories.swift +++ b/test/ClangImporter/objc_redeclared_properties_categories.swift @@ -1,11 +1,11 @@ // RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s // RUN: echo '#import ' > %t.h -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s --allow-empty +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s --allow-empty // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -F %S/Inputs/frameworks -o %t.pch %t.h -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s import CategoryOverrides @@ -54,3 +54,27 @@ func takesABaseClass(_ x: MyBaseClass) { // CHECK-PRIVATE-NOT: has no member 'derivedMember' x.derivedMember = Base() } + +// A category declared in a (private) header can introduce overrides of a +// property that has mismatched Swift naming conventions. If we see a +// non-__attribute__((swift_private)) decl, sometimes it comes in too. + +extension Refinery { + public enum RefinedSugar { + case caster + case grantulated + case confectioners + case cane + case demerara + case turbinado + } + + public var sugar: Refinery.RefinedSugar { + return .caster // RefinedSugar(self.__sugar) + } +} + +func takesARefinery(_ x: Refinery) { + // CHECK: cannot assign to property: 'sugar' is a get-only property + x.sugar = .caster +} From 286781fc3e8325456278755ebc334cd2e9b7ab49 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 29 Jan 2020 16:48:42 -0800 Subject: [PATCH 29/69] Add a unit test for an individual node change, & add ability to set fingerprints in unit tests. --- .../Driver/FineGrainedDependencyDriverGraph.h | 2 +- .../FineGrainedDependencyDriverGraph.cpp | 7 ++++++- .../FineGrainedDependencyGraphTests.cpp | 16 ++++++++++++++ ...peBodyFingerprintsDependencyGraphTests.cpp | 21 +++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index af7b2f5116c06..9d1e9954d2c15 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -437,7 +437,7 @@ class ModuleDepGraph { /// Call \p fn for each node whose key matches \p key. void forEachMatchingNode(const DependencyKey &key, - function_ref) const; + function_ref) const; void forEachNodeInJob(StringRef swiftDeps, function_ref) const; diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index dd997a283f1ca..5bda1956f9b4c 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -171,6 +171,11 @@ ModuleDepGraph::findJobsToRecompileWhenNodesChange< std::unordered_set>( const std::unordered_set &); +template std::vector +ModuleDepGraph::findJobsToRecompileWhenNodesChange< + std::vector>( + const std::vector &); + std::vector ModuleDepGraph::computeSwiftDepsFromNodes( ArrayRef nodes) const { llvm::StringSet<> swiftDepsOfNodes; @@ -441,7 +446,7 @@ void ModuleDepGraph::forEachNode( void ModuleDepGraph::forEachMatchingNode( const DependencyKey &key, - function_ref fn) const { + function_ref fn) const { nodeMap.forEachValueMatching( key, [&](const std::string &, ModuleDepGraphNode *n) { fn(n); }); } diff --git a/unittests/Driver/FineGrainedDependencyGraphTests.cpp b/unittests/Driver/FineGrainedDependencyGraphTests.cpp index b8af2dddc03a7..28e8286866d9e 100644 --- a/unittests/Driver/FineGrainedDependencyGraphTests.cpp +++ b/unittests/Driver/FineGrainedDependencyGraphTests.cpp @@ -808,3 +808,19 @@ TEST(ModuleDepGraph, MutualInterfaceHash) { const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); EXPECT_TRUE(contains(jobs, &job1)); } + +TEST(ModuleDepGraph, DisabledTypeBodyFingerprints) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + graph.simulateLoad(&job0, {{dependsNominal, {"B2"}}}); + graph.simulateLoad(&job1, {{providesNominal, {"B1", "B2"}}}); + graph.simulateLoad(&job2, {{dependsNominal, {"B1"}}}); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } +} diff --git a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp index ff06b0344c252..041cc9c2ee416 100644 --- a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp +++ b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp @@ -808,3 +808,24 @@ TEST(ModuleDepGraphWithTypeBodyFingerprints, MutualInterfaceHash) { const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); EXPECT_TRUE(contains(jobs, &job1)); } + +TEST(ModuleDepGraph, EnabledTypeBodyFingerprints) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + graph.simulateLoad(&job0, {{dependsNominal, {"B2"}}}); + graph.simulateLoad(&job1, {{providesNominal, {"B1", "B2"}}}); + graph.simulateLoad(&job2, {{dependsNominal, {"B1"}}}); + + + const DependencyKey k = DependencyKey(NodeKind::nominal, + DeclAspect::interface, "B1", ""); + std::vector changedNodes; + graph.forEachMatchingNode( + k, + [&](ModuleDepGraphNode* n) {changedNodes.push_back(n);}); + { + const auto jobs = graph.findJobsToRecompileWhenNodesChange(changedNodes); + EXPECT_TRUE(contains(jobs, &job2)); + EXPECT_FALSE(contains(jobs, &job0)); + } +} From 9bd638579de91b68c77219623ae987008fa4e6dd Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 29 Jan 2020 14:51:28 -0800 Subject: [PATCH 30/69] Sema: Fix duplicate diagnostic spam with 'Self' in properties Fixes / . --- lib/Sema/TypeCheckDeclPrimary.cpp | 11 +++++++---- test/type/self.swift | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index cb4f75303721a..5659c6f8912aa 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1336,13 +1336,13 @@ class DeclChecker : public DeclVisitor { } if (VD->getDeclContext()->getSelfClassDecl()) { - checkDynamicSelfType(VD, VD->getValueInterfaceType()); - if (VD->getValueInterfaceType()->hasDynamicSelfType()) { if (VD->hasStorage()) VD->diagnose(diag::dynamic_self_in_stored_property); else if (VD->isSettable(nullptr)) VD->diagnose(diag::dynamic_self_in_mutable_property); + else + checkDynamicSelfType(VD, VD->getValueInterfaceType()); } } @@ -2153,8 +2153,11 @@ class DeclChecker : public DeclVisitor { checkExplicitAvailability(FD); - if (FD->getDeclContext()->getSelfClassDecl()) - checkDynamicSelfType(FD, FD->getResultInterfaceType()); + // Skip this for accessors, since we should have diagnosed the + // storage itself. + if (!isa(FD)) + if (FD->getDeclContext()->getSelfClassDecl()) + checkDynamicSelfType(FD, FD->getResultInterfaceType()); checkDefaultArguments(FD->getParameters()); diff --git a/test/type/self.swift b/test/type/self.swift index bc2fa859cb96b..10e27b93a4e6e 100644 --- a/test/type/self.swift +++ b/test/type/self.swift @@ -270,3 +270,20 @@ class Foo { Self.value * 2 }() } + +// https://bugs.swift.org/browse/SR-11681 - duplicate diagnostics +struct Box { + let boxed: T +} + +class Boxer { + lazy var s = Box(boxed: self as! Self) + // expected-error@-1 {{stored property cannot have covariant 'Self' type}} + // expected-error@-2 {{mutable property cannot have covariant 'Self' type}} + + var t = Box(boxed: Self()) + // expected-error@-1 {{stored property cannot have covariant 'Self' type}} + // expected-error@-2 {{covariant 'Self' type cannot be referenced from a stored property initializer}} + + required init() {} +} From f4a5301bfd6c71e4673442f7314d74a1d4ed3db8 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 29 Jan 2020 22:36:06 -0800 Subject: [PATCH 31/69] Sema: Fix crash when property type references type alias with unsatisfied requirements The fix for did not handle properties because those types are resolved using the archetype resolver. We would bail out early because of a bogus early return that can just be removed. Fixes . --- lib/Sema/TypeCheckType.cpp | 4 ---- test/Constraints/conditionally_defined_types.swift | 12 ++++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index ad3ab2f99e947..483c9db23cb4d 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1463,10 +1463,6 @@ static Type resolveNestedIdentTypeComponent( inferredAssocType); } - // At this point, we need to have resolved the type of the member. - if (memberType->hasError()) - return memberType; - // If there are generic arguments, apply them now. return applyGenericArguments(memberType, resolution, comp, options); }; diff --git a/test/Constraints/conditionally_defined_types.swift b/test/Constraints/conditionally_defined_types.swift index 2107fa6e6faca..193d40a7f766d 100644 --- a/test/Constraints/conditionally_defined_types.swift +++ b/test/Constraints/conditionally_defined_types.swift @@ -222,3 +222,15 @@ let _ = Conforms.Decl4.Decl2.self // expected-error {{type 'Z2.T' (aka 'Y let _ = Conforms.Decl4.Decl3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl4.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl5.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} + +// rdar://problem/45271663 +protocol PP { associatedtype V } +struct SS {} +enum EE {} +extension EE : PP where A : PP, B : PP { typealias V = EE } + +protocol P2 { associatedtype U } +func f(s: SS) -> SS> where PI.U : PP, PI.V : P2 { + let t: EE.V + // expected-error@-1 {{type 'PI.V.U' does not conform to protocol 'PP'}} +} From be37141b0fc5ad82dafbaaed2936a5a62c4de1e0 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Wed, 29 Jan 2020 23:56:09 -0800 Subject: [PATCH 32/69] Revert "build: simplify version tracking logic" --- lib/Basic/CMakeLists.txt | 58 ++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index fd60da8ff5379..6cd7577ce3f93 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -11,32 +11,59 @@ else() set(UUID_INCLUDE "${UUID_INCLUDE_DIRS}") endif() -function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file("${dir}" ${name}_vc) +# Figure out if we can track VC revisions. +# FIXME: Copied from Clang. +function(find_first_existing_file out_var) + foreach(file ${ARGN}) + if(EXISTS "${file}") + set(${out_var} "${file}" PARENT_SCOPE) + return() + endif() + endforeach() +endfunction() - # Create custom target to generate the VC revision include. - set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") +macro(find_first_existing_vc_file out_var path) + find_first_existing_file(${out_var} + "${path}/.git/logs/HEAD" # Git + "${path}/.svn/wc.db" # SVN 1.7 + "${path}/.svn/entries" # SVN 1.6 + ) +endmacro() - set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") +set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") - add_custom_command(OUTPUT "${version_inc}" - DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" - COMMAND ${CMAKE_COMMAND} "-DNAMES=$" - "-D$_SOURCE_DIR=${dir}" - "-DHEADER_FILE=${version_inc}" - -P "${generate_vcs_version_script}") +function(generate_revision_inc revision_inc_var name dir) + find_first_existing_vc_file(dep_file "${dir}") + # Create custom target to generate the VC revision include. + set(revision_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") + string(TOUPPER ${name} upper_name) + if(DEFINED dep_file) + add_custom_command(OUTPUT "${revision_inc}" + DEPENDS "${dep_file}" "${generate_vcs_version_script}" + COMMAND + ${CMAKE_COMMAND} "-DNAMES=${upper_name}" + "-D${upper_name}_SOURCE_DIR=${dir}" + "-DHEADER_FILE=${revision_inc}" + -P "${generate_vcs_version_script}") + else() + # Generate an empty Revision.inc file if we are not using git or SVN. + file(WRITE "${revision_inc}" "") + endif() # Mark the generated header as being generated. - set_source_files_properties("${version_inc}" + set_source_files_properties("${revision_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) - set(${revision_inc_var} ${version_inc} PARENT_SCOPE) + set(${revision_inc_var} ${revision_inc} PARENT_SCOPE) endfunction() generate_revision_inc(llvm_revision_inc LLVM "${LLVM_MAIN_SRC_DIR}") generate_revision_inc(clang_revision_inc Clang "${CLANG_MAIN_SRC_DIR}") generate_revision_inc(swift_revision_inc Swift "${SWIFT_SOURCE_DIR}") +set(version_inc_files + ${llvm_revision_inc} ${clang_revision_inc} ${swift_revision_inc}) + add_swift_host_library(swiftBasic STATIC AnyValue.cpp Cache.cpp @@ -68,10 +95,7 @@ add_swift_host_library(swiftBasic STATIC Unicode.cpp UUID.cpp Version.cpp - - ${llvm_revision_inc} - ${clang_revision_inc} - ${swift_revision_inc} + ${version_inc_files} # Platform-specific TaskQueue implementations Unix/TaskQueue.inc From dcaa950f7b42893bec920f73223cc3d3bdabf7f9 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Thu, 30 Jan 2020 12:28:32 -0300 Subject: [PATCH 33/69] [CSDiag] Removing unused FailureDiagnosis::visitTryExpr --- lib/Sema/CSDiag.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 1910e242ce091..ddb0d6d86c42d 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -221,7 +221,6 @@ class FailureDiagnosis :public ASTVisitor{ validateContextualType(Type contextualType, ContextualTypePurpose CTP); bool visitExpr(Expr *E); - bool visitTryExpr(TryExpr *E); bool visitApplyExpr(ApplyExpr *AE); bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E); @@ -1425,12 +1424,6 @@ visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { return false; } -/// A TryExpr doesn't change it's argument, nor does it change the contextual -/// type. -bool FailureDiagnosis::visitTryExpr(TryExpr *E) { - return visit(E->getSubExpr()); -} - bool FailureDiagnosis::visitExpr(Expr *E) { // Check each of our immediate children to see if any of them are // independently invalid. From 2d10a8a9a0c5ac7f0de5cf8bda980dbad0b5e4e2 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 30 Jan 2020 09:45:29 -0800 Subject: [PATCH 34/69] [ConstraintSystem] Extend function type conversion mismatch coverage Originally type mismatches associated with conversions between function types were only covered for coercions and assignments but fix coverage grew since then and now it makes sense to remove special case and let `repairFailures` take care of it. --- lib/Sema/CSSimplify.cpp | 17 ++--------------- test/Constraints/enum_cases.swift | 8 ++++---- test/Constraints/tuple_arguments.swift | 3 ++- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 2a24f5f95d981..fabf1338b2a97 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4083,21 +4083,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, auto result = matchFunctionTypes(func1, func2, kind, flags, locator); - if (shouldAttemptFixes() && result.isFailure()) { - // If this is a contextual type mismatch failure - // let's give the solver a chance to "fix" it. - if (auto last = locator.last()) { - if (last->is()) - break; - } - - // If this is a type mismatch in assignment, we don't really care - // (yet) was it argument or result type mismatch, let's produce a - // diagnostic which mentions both function types. - auto *anchor = locator.getAnchor(); - if (anchor && isa(anchor)) - break; - } + if (shouldAttemptFixes() && result.isFailure()) + break; return result; } diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index 5a918dacd6fa1..dbfe34ec5ca59 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -22,14 +22,14 @@ let _ = arr.map(E.bar) // Ok let _ = arr.map(E.two) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (Int, Int) -> E)'}} // expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} -let _ = arr.map(E.tuple) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping ((x: Int, y: Int)) -> E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} +let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) throws -> T'}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} let _ = arr.map(G_E.foo) // Ok let _ = arr.map(G_E.bar) // Ok let _ = arr.map(G_E.two) // expected-error {{cannot convert value of type '(String, String) -> G_E' to expected argument type '(String) throws -> G_E'}} -let _ = arr.map(G_E.tuple) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping ((x: Int, y: Int)) -> G_E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} +let _ = arr.map(G_E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E' to expected argument type '(String) throws -> T'}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} let _ = E.foo("hello") // expected-error {{missing argument label 'bar:' in call}} let _ = E.bar("hello") // Ok diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index 3011132d48d78..0b1852ea221ed 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -1702,7 +1702,8 @@ _ = x.map { (_: ()) in () } // https://bugs.swift.org/browse/SR-9470 do { func f(_: Int...) {} - let _ = [(1, 2, 3)].map(f) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (Int...) -> ())'}} + let _ = [(1, 2, 3)].map(f) // expected-error {{cannot convert value of type '(Int...) -> ()' to expected argument type '((Int, Int, Int)) throws -> T'}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} } // rdar://problem/48443263 - cannot convert value of type '() -> Void' to expected argument type '(_) -> Void' From d171b30d097752bf72958f1f28496961e70e5fe6 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Thu, 30 Jan 2020 09:48:49 -0800 Subject: [PATCH 35/69] Allow fine-grained-dependency-include-intrafile in tests for compatibility with enabled type fingerprints. --- include/swift/Option/Options.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index dd2d0c0d026f4..dbadf83aa6318 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -191,7 +191,7 @@ HelpText<"Emit dot files every time driver imports an fine-grained swiftdeps fil def fine_grained_dependency_include_intrafile : Flag<["-"], "fine-grained-dependency-include-intrafile">, -InternalDebugOpt, +Flags<[FrontendOption, HelpHidden]>, HelpText<"Include within-file dependencies.">; def emit_fine_grained_dependency_sourcefile_dot_files : From 458ae7b6f94dbc47c2412508df7128b79cb7f901 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Thu, 30 Jan 2020 09:49:18 -0800 Subject: [PATCH 36/69] Use -fine-grained-dependency-include-intrafile for compatibility with type fingerprints. --- .../NameBinding/reference-dependencies-fine.swift | 11 +++++++---- .../reference-dependencies-members-fine.swift | 15 +++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/test/NameBinding/reference-dependencies-fine.swift b/test/NameBinding/reference-dependencies-fine.swift index f9e4b38ab5f65..e15fbcf24e500 100644 --- a/test/NameBinding/reference-dependencies-fine.swift +++ b/test/NameBinding/reference-dependencies-fine.swift @@ -4,10 +4,11 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // Merge each entry onto one line and sort to overcome order differences // RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps @@ -339,7 +340,8 @@ struct Outer { } } -// CHECK-TOPLEVEL-DAG: topLevel interface '' privateFunc false +// CHECK-TOPLEVEL-DAG: topLevel interface '' privateFunc true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' privateFunc true private func privateFunc() {} // CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel1 false @@ -498,7 +500,8 @@ struct Sentinel2 {} // CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main13PrivateProto3P '' false // CHECK-NOMINAL-2-DAG: nominal interface Sa '' false -// CHECK-NOMINAL-2-DAG: nominal interface Sb '' false +// CHECK-NOMINAL-2-DAG: nominal interface Sb '' true +// CHECK-NOMINAL-2-DAG: nominal implementation Sb '' true // CHECK-NOMINAL-2-DAG: nominal interface 4main18ClassFromOtherFileC '' false // CHECK-NOMINAL-2-DAG: nominal interface SL '' false // CHECK-NOMINAL-2-DAG: nominal interface s25ExpressibleByFloatLiteralP '' false diff --git a/test/NameBinding/reference-dependencies-members-fine.swift b/test/NameBinding/reference-dependencies-members-fine.swift index c1c9ba58dff74..bbb99fbfb3e98 100644 --- a/test/NameBinding/reference-dependencies-members-fine.swift +++ b/test/NameBinding/reference-dependencies-members-fine.swift @@ -4,9 +4,11 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps // RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t-2.swiftdeps >%t-2-processed.swiftdeps @@ -14,7 +16,6 @@ // RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL-2 %s < %t-processed.swiftdeps -// RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL-NEGATIVE %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-MEMBER %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-MEMBER-NEGATIVE %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=DEPENDS-NOMINAL %s < %t-processed.swiftdeps @@ -54,15 +55,13 @@ protocol SomeProto {} // DEPENDS-MEMBER-DAG: member interface 4main10OtherClassC deinit false extension OtherClass : SomeProto {} -// PROVIDES-NOMINAL-NEGATIVE-NOT: nominal implementation 4main11OtherStructV '' true -// PROVIDES-NOMINAL-NEGATIVE-NOT: nominal interface 4main11OtherStructV '' true -// DEPENDS-NOMINAL-DAG: nominal interface 4main11OtherStructV '' false +// PROVIDES-NOMINAL-DAG: nominal implementation 4main11OtherStructV '' true +// PROVIDES-NOMINAL-DAG: nominal interface 4main11OtherStructV '' true extension OtherStruct { // PROVIDES-MEMBER-DAG: potentialMember interface 4main11OtherStructV '' true // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV foo true // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV bar true - // PROVIDES-MEMBER-NEGATIVE-NOT: member interface 4main11OtherStructV baz true - // DEPENDS-MEMBER-DAG: member interface 4main11OtherStructV baz false + // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV baz true // DEPENDS-MEMBER-NEGATIVE-NOT:: potentialMember interface 4main11OtherStructV baz false func foo() {} var bar: () { return () } From 6debe3d3b500420b474e9d86a5e5ec25bc63ed23 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 30 Jan 2020 09:54:24 -0800 Subject: [PATCH 37/69] [ConstraintSystem] Don't produce conformance fixes for mismatches associated with function result type If there are any requirement failures associated with result types which are part of a function type conversion, let's record general conversion mismatch in order for it to capture and display complete function types. --- lib/Sema/CSSimplify.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index fabf1338b2a97..09a1c7eda4973 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2160,6 +2160,17 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, break; } + + // TODO(diagnostics): If there are any requirement failures associated + // with result types which are part of a function type conversion, + // let's record general conversion mismatch in order for it to capture + // and display complete function types. + // + // Once either reacher locators or better diagnostic presentation for + // nested type failures is available this check could be removed. + if (last->is()) + return getTypeMatchFailure(locator); + } else { // There are no elements in the path auto *anchor = locator.getAnchor(); if (!(anchor && From 38f599dcb78777a4632d7a6865acdd248713ad90 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Thu, 30 Jan 2020 11:48:13 -0800 Subject: [PATCH 38/69] Turn type-body-fingerprints on-by-default --- include/swift/Basic/LangOptions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index ae4e11c5f563d..c125f046281c9 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -304,7 +304,7 @@ namespace swift { /// the interface hash, hash them into per-iterable-decl-context /// fingerprints. Fine-grained dependency types won't dirty every provides /// in a file when the user adds a member to, e.g., a struct. - bool EnableTypeFingerprints = false; + bool EnableTypeFingerprints = true; /// When using fine-grained dependencies, emit dot files for every swiftdeps /// file. From 3375b362ebe6b4677deef859ee7a272f44317fc6 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 30 Jan 2020 14:15:38 -0800 Subject: [PATCH 39/69] Search nested types tables in parent modules Add another cross-cutting module configuration to the nested types table search path. A module can have no overlay but also contain a nested types table suitable for finding a given member name. UIKit is the sharpest example of this state of affairs. UIKit currently defines an overlay in Swift where we find some nested types. But it also defines an inner module that has some other nested types tables. Resolves rdar://58940989 --- lib/Serialization/Deserialization.cpp | 4 ++++ test/Serialization/multi-file-nested-type-extension.swift | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4c62bb3728b1a..8e9d098532b23 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1468,6 +1468,10 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { if (auto overlayModule = LF->getOverlayModule()) { nestedType = findNestedTypeDeclInModule(getFile(), overlayModule, memberName, baseType); + } else if (LF->getParentModule() != extensionModule) { + nestedType = findNestedTypeDeclInModule(getFile(), + LF->getParentModule(), + memberName, baseType); } } } diff --git a/test/Serialization/multi-file-nested-type-extension.swift b/test/Serialization/multi-file-nested-type-extension.swift index fd2d139a25009..181fc38b72c52 100644 --- a/test/Serialization/multi-file-nested-type-extension.swift +++ b/test/Serialization/multi-file-nested-type-extension.swift @@ -10,7 +10,7 @@ // REQUIRES: asserts // CHECK: Statistics -// CHECK: 1 Serialization - # of nested types resolved without full lookup +// CHECK: 2 Serialization - # of nested types resolved without full lookup // Note the Optional here and below; this was once necessary to produce a crash. // Without it, the type of the parameter is initialized "early" enough to not @@ -21,3 +21,9 @@ extension Outer { public func useTypes(_: Outer.Callback?) {} } + +extension OuterClass.Inner { + public static var instance: OuterClass.Inner { + return OuterClass.Inner() + } +} From e121cb09cdab86c1479cc0ebf8ac063f2823e0a8 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 30 Jan 2020 15:04:07 -0800 Subject: [PATCH 40/69] Revert "[SourceKit] Disable module system headers validation" This reverts commit 951b85359a8b4781bae9e0735a893aee07b3dd1e. --- lib/IDE/CompletionInstance.cpp | 5 +++++ tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp | 5 ----- tools/swift-ide-test/swift-ide-test.cpp | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 446d80601a9c3..7d326b96bf116 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -362,6 +362,11 @@ bool swift::ide::CompletionInstance::performOperation( // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; + // This validation may call stat(2) many times. Disable it to prevent + // performance regression. + Invocation.getSearchPathOptions().DisableModulesValidateSystemDependencies = + true; + // Since caching uses the interface hash, and since per type fingerprints // weaken that hash, disable them here: Invocation.getLangOptions().EnableTypeFingerprints = false; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 1ebbcfeb25441..1e2b1fd10a211 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -539,11 +539,6 @@ bool SwiftASTManager::initCompilerInvocation( // We don't care about LLVMArgs FrontendOpts.LLVMArgs.clear(); - // This validation may call stat(2) many times. Disable it to prevent - // performance issues. - Invocation.getSearchPathOptions().DisableModulesValidateSystemDependencies = - true; - // SwiftSourceInfo files provide source location information for decls coming // from loaded modules. For most IDE use cases it either has an undesirable // impact on performance with no benefit (code completion), results in stale diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index fb2b67b7225fb..995eaaaab5eef 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -3436,8 +3436,6 @@ int main(int argc, char *argv[]) { options::DebugForbidTypecheckPrefix; InitInvok.getTypeCheckerOptions().DebugConstraintSolver = options::DebugConstraintSolver; - InitInvok.getSearchPathOptions().DisableModulesValidateSystemDependencies = - true; for (auto ConfigName : options::BuildConfigs) InitInvok.getLangOptions().addCustomConditionalCompilationFlag(ConfigName); From cb20d0ebfa9623a61f581edaba7e805cd0c357fd Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 30 Jan 2020 15:04:56 -0800 Subject: [PATCH 41/69] Revert "[CodeCompletion] Disable module system headers validation" This reverts commit ec70a40205396a9bd9e0310b3b81b4b41838976a. --- lib/IDE/CompletionInstance.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 7d326b96bf116..446d80601a9c3 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -362,11 +362,6 @@ bool swift::ide::CompletionInstance::performOperation( // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; - // This validation may call stat(2) many times. Disable it to prevent - // performance regression. - Invocation.getSearchPathOptions().DisableModulesValidateSystemDependencies = - true; - // Since caching uses the interface hash, and since per type fingerprints // weaken that hash, disable them here: Invocation.getLangOptions().EnableTypeFingerprints = false; From 82ec36df4f33ce59c14969229937c124d9baff07 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 30 Jan 2020 15:42:32 -0800 Subject: [PATCH 42/69] Update tsan-emptyarraystorage.swift to run on remote_run --- test/Sanitizers/tsan-emptyarraystorage.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/Sanitizers/tsan-emptyarraystorage.swift b/test/Sanitizers/tsan-emptyarraystorage.swift index 193a48514bb02..af9deb347e0f8 100644 --- a/test/Sanitizers/tsan-emptyarraystorage.swift +++ b/test/Sanitizers/tsan-emptyarraystorage.swift @@ -6,10 +6,6 @@ // REQUIRES: foundation // UNSUPPORTED: OS=tvos -// FIXME: This should be covered by "tsan_runtime"; older versions of Apple OSs -// don't support TSan. -// UNSUPPORTED: remote_run - import Foundation let sem = DispatchSemaphore(value: 0) From ad1ad43c6e4c6862870f6a095630f2e1ffba5da7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 27 Jan 2020 21:08:36 -0800 Subject: [PATCH 43/69] Generalize SolutionApplicationTarget and use it more widely. Make this type suitable for representing the result of solution application, and do so. --- lib/Sema/CSApply.cpp | 53 ++++++++++++++++++++----------------- lib/Sema/ConstraintSystem.h | 48 +++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 770b79c72a9c2..4195ef7b474e6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7231,14 +7231,14 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -llvm::PointerUnion ConstraintSystem::applySolutionImpl( +Optional ConstraintSystem::applySolutionImpl( Solution &solution, SolutionApplicationTarget target, Type convertType, bool discardedExpr, bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { if (shouldSuppressDiagnostics()) - return nullptr; + return None; bool diagnosedErrorsViaFixes = applySolutionFixes(solution); // If all of the available fixes would result in a warning, @@ -7248,12 +7248,12 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( })) { // If we already diagnosed any errors via fixes, that's it. if (diagnosedErrorsViaFixes) - return nullptr; + return None; // If we didn't manage to diagnose anything well, so fall back to // diagnosing mining the system to construct a reasonable error message. diagnoseFailureFor(target); - return nullptr; + return None; } } @@ -7261,9 +7261,13 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( ExprWalker walker(rewriter); // Apply the solution to the target. - llvm::PointerUnion result; + SolutionApplicationTarget result = target; if (auto expr = target.getAsExpr()) { - result = expr->walk(walker); + Expr *rewrittenExpr = expr->walk(walker); + if (!rewrittenExpr) + return None; + + result.setExpr(rewrittenExpr); } else { auto fn = *target.getAsFunction(); @@ -7284,14 +7288,11 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( }); if (!newBody) - return result; + return None; - result = newBody; + result.setFunctionBody(newBody); } - if (result.isNull()) - return result; - // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. if (!performingDiagnostics) { @@ -7309,10 +7310,10 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( // If any of them failed to type check, bail. if (hadError) - return nullptr; + return None; } - if (auto resultExpr = result.dyn_cast()) { + if (auto resultExpr = result.getAsExpr()) { Expr *expr = target.getAsExpr(); assert(expr && "Can't have expression result without expression target"); // We are supposed to use contextual type only if it is present and @@ -7328,19 +7329,20 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( // If we're supposed to convert the expression to some particular type, // do so now. if (shouldCoerceToContextualType()) { - result = rewriter.coerceToType(resultExpr, convertType, - getConstraintLocator(expr)); - if (!result) - return nullptr; + resultExpr = rewriter.coerceToType(resultExpr, convertType, + getConstraintLocator(expr)); } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { // We referenced an lvalue. Load it. - result = rewriter.coerceToType(resultExpr, - getType(resultExpr)->getRValueType(), - getConstraintLocator(expr)); + resultExpr = rewriter.coerceToType(resultExpr, + getType(resultExpr)->getRValueType(), + getConstraintLocator(expr)); } - if (resultExpr) - solution.setExprTypes(resultExpr); + if (!resultExpr) + return None; + + solution.setExprTypes(resultExpr); + result.setExpr(resultExpr); } rewriter.finalize(); @@ -7512,13 +7514,14 @@ MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { return MutableArrayRef(solutions, numSolutions); } -llvm::PointerUnion SolutionApplicationTarget::walk( - ASTWalker &walker) { +SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { switch (kind) { case Kind::expression: return getAsExpr()->walk(walker); case Kind::function: - return getAsFunction()->getBody()->walk(walker); + return SolutionApplicationTarget( + *getAsFunction(), + cast_or_null(getFunctionBody()->walk(walker))); } } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 472174e6de469..6b4e14edc5ad2 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1147,7 +1147,11 @@ class SolutionApplicationTarget { union { Expr *expression; - AnyFunctionRef function; + + struct { + AnyFunctionRef function; + BraceStmt *body; + } function; }; public: @@ -1156,9 +1160,13 @@ class SolutionApplicationTarget { expression = expr; } - SolutionApplicationTarget(AnyFunctionRef fn) { + SolutionApplicationTarget(AnyFunctionRef fn) + : SolutionApplicationTarget(fn, fn.getBody()) { } + + SolutionApplicationTarget(AnyFunctionRef fn, BraceStmt *body) { kind = Kind::function; - function = fn; + function.function = fn; + function.body = body; } Expr *getAsExpr() const { @@ -1171,18 +1179,32 @@ class SolutionApplicationTarget { } } + void setExpr(Expr *expr) { + assert(kind == Kind::expression); + expression = expr; + } + Optional getAsFunction() const { switch (kind) { case Kind::expression: return None; case Kind::function: - return function; + return function.function; } } + BraceStmt *getFunctionBody() const { + assert(kind == Kind::function); + return function.body; + } + + void setFunctionBody(BraceStmt *stmt) { + assert(kind == Kind::function); + function.body = stmt; + } /// Walk the contents of the application target. - llvm::PointerUnion walk(ASTWalker &walker); + SolutionApplicationTarget walk(ASTWalker &walker); }; enum class ConstraintSystemPhase { @@ -4147,7 +4169,7 @@ class ConstraintSystem { bool minimize); private: - llvm::PointerUnion applySolutionImpl( + Optional applySolutionImpl( Solution &solution, SolutionApplicationTarget target, Type convertType, bool discardedExpr, bool performingDiagnostics); @@ -4165,15 +4187,19 @@ class ConstraintSystem { Type convertType, bool discardedExpr, bool performingDiagnostics) { - return applySolutionImpl(solution, expr, convertType, discardedExpr, - performingDiagnostics).get(); + auto result = applySolutionImpl( + solution, expr, convertType, discardedExpr, performingDiagnostics); + if (result) + return result->getAsExpr(); + return nullptr; } /// Apply a given solution to the body of the given function. BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - return cast_or_null( - applySolutionImpl(solution, fn, Type(), false, false) - .dyn_cast()); + auto result = applySolutionImpl(solution, fn, Type(), false, false); + if (result) + return result->getFunctionBody(); + return nullptr; } /// Reorder the disjunctive clauses for a given expression to From 98db6e642262ea04b3d34b4903c3daaf15c109c0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 27 Jan 2020 21:08:53 -0800 Subject: [PATCH 44/69] =?UTF-8?q?[Constraint=20solver]=20Don=E2=80=99t=20m?= =?UTF-8?q?ark=20a=20moved-from=20instance=20as=20=E2=80=9Cdiagnosed?= =?UTF-8?q?=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Sema/CSApply.cpp | 1 + lib/Sema/CSSolver.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 4195ef7b474e6..1f13de2f7e0b1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7511,6 +7511,7 @@ ArrayRef SolutionResult::getAmbiguousSolutions() const { MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { assert(getKind() == Ambiguous); + markAsDiagnosed(); return MutableArrayRef(solutions, numSolutions); } diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index fbae75e344bc2..20699a855fd48 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1172,7 +1172,6 @@ bool ConstraintSystem::solve(Expr *&expr, solutions.assign(std::make_move_iterator(ambiguousSolutions.begin()), std::make_move_iterator(ambiguousSolutions.end())); dumpSolutions(); - solution.markAsDiagnosed(); return false; } From e3124dcb5f41a9b908adf3d9360a97203c55adee Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 09:32:54 -0800 Subject: [PATCH 45/69] [Constraint system] Expand SolutionApplicationTarget for expressions. Add the final conversion type and the flag indicating whether a given expression is discarded to SolutionApplicationTarget, rather than separating the arguments to the solver implementation. --- lib/Sema/CSApply.cpp | 16 +++++++++++----- lib/Sema/CSSolver.cpp | 4 +++- lib/Sema/ConstraintSystem.h | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1f13de2f7e0b1..8fdcc55d12af1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7232,8 +7232,8 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. Optional ConstraintSystem::applySolutionImpl( - Solution &solution, SolutionApplicationTarget target, Type convertType, - bool discardedExpr, bool performingDiagnostics) { + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { @@ -7316,9 +7316,11 @@ Optional ConstraintSystem::applySolutionImpl( if (auto resultExpr = result.getAsExpr()) { Expr *expr = target.getAsExpr(); assert(expr && "Can't have expression result without expression target"); + // We are supposed to use contextual type only if it is present and // this expression doesn't represent the implicit return of the single // expression function which got deduced to be `Never`. + Type convertType = target.getExprConversionType(); auto shouldCoerceToContextualType = [&]() { return convertType && !(getType(resultExpr)->isUninhabited() && @@ -7331,7 +7333,8 @@ Optional ConstraintSystem::applySolutionImpl( if (shouldCoerceToContextualType()) { resultExpr = rewriter.coerceToType(resultExpr, convertType, getConstraintLocator(expr)); - } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { + } else if (getType(resultExpr)->hasLValueType() && + !target.isDiscardedExpr()) { // We referenced an lvalue. Load it. resultExpr = rewriter.coerceToType(resultExpr, getType(resultExpr)->getRValueType(), @@ -7517,8 +7520,11 @@ MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { switch (kind) { - case Kind::expression: - return getAsExpr()->walk(walker); + case Kind::expression: { + SolutionApplicationTarget result = *this; + result.setExpr(getAsExpr()->walk(walker)); + return result; + } case Kind::function: return SolutionApplicationTarget( diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 20699a855fd48..9be997703876d 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1184,7 +1184,9 @@ bool ConstraintSystem::solve(Expr *&expr, } if (stage == 1) { - diagnoseFailureFor(expr); + diagnoseFailureFor( + SolutionApplicationTarget(expr, convertType, + /*isDiscarded=*/false)); solution.markAsDiagnosed(); return true; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 6b4e14edc5ad2..c88c8b3541478 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1146,7 +1146,15 @@ class SolutionApplicationTarget { } kind; union { - Expr *expression; + struct { + Expr *expression; + + /// The type to which the expression should be converted. + Type convertType; + + /// Whether the expression result will be discarded at the end. + bool isDiscarded; + } expression; struct { AnyFunctionRef function; @@ -1155,9 +1163,11 @@ class SolutionApplicationTarget { }; public: - SolutionApplicationTarget(Expr *expr) { + SolutionApplicationTarget(Expr *expr, Type convertType, bool isDiscarded) { kind = Kind::expression; - expression = expr; + expression.expression = expr; + expression.convertType = convertType; + expression.isDiscarded = isDiscarded; } SolutionApplicationTarget(AnyFunctionRef fn) @@ -1172,16 +1182,26 @@ class SolutionApplicationTarget { Expr *getAsExpr() const { switch (kind) { case Kind::expression: - return expression; + return expression.expression; case Kind::function: return nullptr; } } + Type getExprConversionType() const { + assert(kind == Kind::expression); + return expression.convertType; + } + + bool isDiscardedExpr() const { + assert(kind == Kind::expression); + return expression.isDiscarded; + } + void setExpr(Expr *expr) { assert(kind == Kind::expression); - expression = expr; + expression.expression = expr; } Optional getAsFunction() const { @@ -4171,7 +4191,7 @@ class ConstraintSystem { private: Optional applySolutionImpl( Solution &solution, SolutionApplicationTarget target, - Type convertType, bool discardedExpr, bool performingDiagnostics); + bool performingDiagnostics); public: /// Apply a given solution to the expression, producing a fully @@ -4188,7 +4208,8 @@ class ConstraintSystem { bool discardedExpr, bool performingDiagnostics) { auto result = applySolutionImpl( - solution, expr, convertType, discardedExpr, performingDiagnostics); + solution, SolutionApplicationTarget(expr, convertType, discardedExpr), + performingDiagnostics); if (result) return result->getAsExpr(); return nullptr; @@ -4196,7 +4217,7 @@ class ConstraintSystem { /// Apply a given solution to the body of the given function. BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - auto result = applySolutionImpl(solution, fn, Type(), false, false); + auto result = applySolutionImpl(solution, fn, false); if (result) return result->getFunctionBody(); return nullptr; From eb2862ec1e0899c86aa7a01e767e993552d03e96 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 09:54:39 -0800 Subject: [PATCH 46/69] [Constraint system] Collapse applySolutionImpl() into applySolution(). Now that we have a unified notion of a SolutionApplicationTarget, use it to collapse 3 applySolution-ish functions into one. --- lib/Sema/BuilderTransform.cpp | 10 +++++++-- lib/Sema/CSApply.cpp | 2 +- lib/Sema/ConstraintSystem.h | 36 ++++++------------------------- lib/Sema/TypeCheckConstraints.cpp | 15 +++++++------ 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index d2f21354ab52e..0dff01b97cc29 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1107,8 +1107,14 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( } // Apply the solution to the function body. - return cast_or_null( - cs.applySolutionToBody(solutions.front(), func)); + if (auto result = cs.applySolution( + solutions.front(), + SolutionApplicationTarget(func), + /*performingDiagnostics=*/false)) { + return result->getFunctionBody(); + } + + return nullptr; } ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 8fdcc55d12af1..fa7d40e7b28c6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7231,7 +7231,7 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -Optional ConstraintSystem::applySolutionImpl( +Optional ConstraintSystem::applySolution( Solution &solution, SolutionApplicationTarget target, bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c88c8b3541478..4ea147deb4d39 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -4188,40 +4188,16 @@ class ConstraintSystem { findBestSolution(SmallVectorImpl &solutions, bool minimize); -private: - Optional applySolutionImpl( - Solution &solution, SolutionApplicationTarget target, - bool performingDiagnostics); - public: - /// Apply a given solution to the expression, producing a fully - /// type-checked expression. + /// Apply a given solution to the target, producing a fully + /// type-checked target or \c None if an error occurred. /// - /// \param convertType the contextual type to which the - /// expression should be converted, if any. - /// \param discardedExpr if true, the result of the expression - /// is contextually ignored. + /// \param target the target to which the solution will be applied. /// \param performingDiagnostics if true, don't descend into bodies of /// non-single expression closures, or build curry thunks. - Expr *applySolution(Solution &solution, Expr *expr, - Type convertType, - bool discardedExpr, - bool performingDiagnostics) { - auto result = applySolutionImpl( - solution, SolutionApplicationTarget(expr, convertType, discardedExpr), - performingDiagnostics); - if (result) - return result->getAsExpr(); - return nullptr; - } - - /// Apply a given solution to the body of the given function. - BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - auto result = applySolutionImpl(solution, fn, false); - if (result) - return result->getFunctionBody(); - return nullptr; - } + Optional applySolution( + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics); /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index de6d54af3adce..19d1d18153064 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2267,16 +2267,19 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, cs.applySolution(solution); // Apply the solution to the expression. - result = cs.applySolution( - solution, result, convertType.getType(), - options.contains(TypeCheckExprFlags::IsDiscarded), - options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)); - - if (!result) { + SolutionApplicationTarget target( + result, convertType.getType(), + options.contains(TypeCheckExprFlags::IsDiscarded)); + bool performingDiagnostics = + options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); + auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); + if (!resultTarget) { listener.applySolutionFailed(solution, expr); + // Failure already diagnosed, above, as part of applying the solution. return Type(); } + result = resultTarget->getAsExpr(); // Notify listener that we've applied the solution. result = listener.appliedSolution(solution, result); From 56aeac5d0a80935d170a08ed8f3b3e8c5675f3b3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:40:45 -0800 Subject: [PATCH 47/69] [Constraint system] Push SolutionApplicationTarget into the solveImpl Baby steps toward generalizing the expression-centric solver core. --- lib/Sema/CSSolver.cpp | 15 +++++++++------ lib/Sema/ConstraintSystem.h | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 9be997703876d..961dce47a94ad 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1136,9 +1136,10 @@ bool ConstraintSystem::solve(Expr *&expr, // Take up to two attempts at solving the system. The first attempts to // solve a system that is expected to be well-formed, the second kicks in // when there is an error and attempts to salvage an ill-formed expression. + SolutionApplicationTarget target(expr, convertType, /*isDiscarded=*/false); for (unsigned stage = 0; stage != 2; ++stage) { auto solution = (stage == 0) - ? solveImpl(expr, convertType, listener, allowFreeTypeVariables) + ? solveImpl(target, listener, allowFreeTypeVariables) : salvage(); switch (solution.getKind()) { @@ -1201,14 +1202,13 @@ bool ConstraintSystem::solve(Expr *&expr, } SolutionResult -ConstraintSystem::solveImpl(Expr *&expr, - Type convertType, +ConstraintSystem::solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, FreeTypeVariableBinding allowFreeTypeVariables) { if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - log << "---Constraint solving for the expression at "; - auto R = expr->getSourceRange(); + log << "---Constraint solving at "; + auto R = target.getSourceRange(); if (R.isValid()) { R.print(log, getASTContext().SourceMgr, /*PrintText=*/ false); } else { @@ -1220,6 +1220,7 @@ ConstraintSystem::solveImpl(Expr *&expr, assert(!solverState && "cannot be used directly"); // Set up the expression type checker timer. + Expr *expr = target.getAsExpr(); Timer.emplace(expr, *this); Expr *origExpr = expr; @@ -1240,7 +1241,7 @@ ConstraintSystem::solveImpl(Expr *&expr, // If there is a type that we're expected to convert to, add the conversion // constraint. - if (convertType) { + if (Type convertType = target.getExprConversionType()) { // Determine whether we know more about the contextual type. ContextualTypePurpose ctp = CTP_Unused; bool isOpaqueReturnType = false; @@ -1287,6 +1288,8 @@ ConstraintSystem::solveImpl(Expr *&expr, if (getExpressionTooComplex(solutions)) return SolutionResult::forTooComplex(); + target.setExpr(expr); + switch (solutions.size()) { case 0: return SolutionResult::forUndiagnosedError(); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 4ea147deb4d39..7d3846e646f7a 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1223,6 +1223,17 @@ class SolutionApplicationTarget { assert(kind == Kind::function); function.body = stmt; } + + /// Retrieve the source range of the target. + SourceRange getSourceRange() const { + switch (kind) { + case Kind::expression: + return expression.expression->getSourceRange(); + + case Kind::function: + return function.body->getSourceRange(); + } + } /// Walk the contents of the application target. SolutionApplicationTarget walk(ASTWalker &walker); }; @@ -4078,13 +4089,11 @@ class ConstraintSystem { /// Solve the system of constraints generated from provided expression. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target to generate constraints from. /// \param listener The callback to check solving progress. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. - SolutionResult solveImpl(Expr *&expr, - Type convertType, + SolutionResult solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); From 54ac78d28f4e9fd39148a3dcc2722d6661ac085a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:48:40 -0800 Subject: [PATCH 48/69] [Constrsint solver] Remove ExprTypeCheckListener::constraintGenerationFailed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function isn’t needed, because we can report the failure to the caller directly rather than go through a callback. --- lib/Sema/CSSolver.cpp | 2 -- lib/Sema/TypeCheckConstraints.cpp | 7 ------- lib/Sema/TypeChecker.h | 4 ---- 3 files changed, 13 deletions(-) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 961dce47a94ad..223fd0e3d6793 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1234,8 +1234,6 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, if (auto generatedExpr = generateConstraints(expr, DC)) expr = generatedExpr; else { - if (listener) - listener->constraintGenerationFailed(expr); return SolutionResult::forError(); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 19d1d18153064..4f8bbace048f8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2036,7 +2036,6 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { } void ExprTypeCheckListener::preCheckFailed(Expr *expr) {} -void ExprTypeCheckListener::constraintGenerationFailed(Expr *expr) {} void ExprTypeCheckListener::applySolutionFailed(Solution &solution, Expr *expr) {} @@ -2104,12 +2103,6 @@ class FallbackDiagnosticListener : public ExprTypeCheckListener { maybeProduceFallbackDiagnostic(expr); } - void constraintGenerationFailed(Expr *expr) override { - if (BaseListener) - BaseListener->constraintGenerationFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - void applySolutionFailed(Solution &solution, Expr *expr) override { if (BaseListener) BaseListener->applySolutionFailed(solution, expr); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c4df6c61bb254..09068a8443e18 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -325,10 +325,6 @@ class ExprTypeCheckListener { /// be correctly processed by the constraint solver. virtual void preCheckFailed(Expr *expr); - /// Callback invoked if constraint system failed to generate - /// constraints for a given expression. - virtual void constraintGenerationFailed(Expr *expr); - /// Callback invoked if application of chosen solution to /// expression has failed. virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); From 754afff034df6f5cbcf9c4a4dfc8f40268f83f01 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:53:45 -0800 Subject: [PATCH 49/69] [Constraint solver] Remove ExprTypeCheckListener::preCheckFailed(). Failures will be reported to clients; this callback is unnecessary. --- lib/Sema/TypeCheckConstraints.cpp | 8 -------- lib/Sema/TypeChecker.h | 4 ---- 2 files changed, 12 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 4f8bbace048f8..834e00b6fcf80 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2035,7 +2035,6 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } -void ExprTypeCheckListener::preCheckFailed(Expr *expr) {} void ExprTypeCheckListener::applySolutionFailed(Solution &solution, Expr *expr) {} @@ -2097,12 +2096,6 @@ class FallbackDiagnosticListener : public ExprTypeCheckListener { return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; } - void preCheckFailed(Expr *expr) override { - if (BaseListener) - BaseListener->preCheckFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - void applySolutionFailed(Solution &solution, Expr *expr) override { if (BaseListener) BaseListener->applySolutionFailed(solution, expr); @@ -2169,7 +2162,6 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) { - listener.preCheckFailed(expr); return Type(); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 09068a8443e18..27fc91a4570fe 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -321,10 +321,6 @@ class ExprTypeCheckListener { virtual Expr *appliedSolution(constraints::Solution &solution, Expr *expr); - /// Callback invoked if expression is structurally unsound and can't - /// be correctly processed by the constraint solver. - virtual void preCheckFailed(Expr *expr); - /// Callback invoked if application of chosen solution to /// expression has failed. virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); From f8fd19729528f33f59ca17909c33725d4be41910 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:59:45 -0800 Subject: [PATCH 50/69] [Constraint solver] Remove ExprTypeCheckListener::applySolutionFailed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t need this fallback path. --- lib/Sema/TypeCheckConstraints.cpp | 44 ------------------------------- lib/Sema/TypeChecker.h | 4 --- 2 files changed, 48 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 834e00b6fcf80..29d7dd96c3066 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2035,9 +2035,6 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } -void ExprTypeCheckListener::applySolutionFailed(Solution &solution, - Expr *expr) {} - void ParentConditionalConformance::diagnoseConformanceStack( DiagnosticEngine &diags, SourceLoc loc, ArrayRef conformances) { @@ -2095,45 +2092,6 @@ class FallbackDiagnosticListener : public ExprTypeCheckListener { Expr *appliedSolution(Solution &solution, Expr *expr) override { return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; } - - void applySolutionFailed(Solution &solution, Expr *expr) override { - if (BaseListener) - BaseListener->applySolutionFailed(solution, expr); - - if (hadAnyErrors()) - return; - - // If solution involves invalid or incomplete conformances that's - // a probable cause of failure to apply it without producing an error, - // which is going to be diagnosed later, so let's not produce - // fallback diagnostic in this case. - if (llvm::any_of( - solution.Conformances, - [](const std::pair - &conformance) -> bool { - auto &ref = conformance.second; - return ref.isConcrete() && ref.getConcrete()->isInvalid(); - })) - return; - - maybeProduceFallbackDiagnostic(expr); - } - -private: - bool hadAnyErrors() const { return Context.Diags.hadAnyError(); } - - void maybeProduceFallbackDiagnostic(Expr *expr) const { - if (Options.contains(TypeCheckExprFlags::SubExpressionDiagnostics) || - DiagnosticSuppression::isEnabled(Context.Diags)) - return; - - // Before producing fatal error here, let's check if there are any "error" - // diagnostics already emitted or waiting to be emitted. Because they are - // a better indication of the problem. - if (!(hadAnyErrors() || Context.hasDelayedConformanceErrors())) - Context.Diags.diagnose(expr->getLoc(), - diag::failed_to_produce_diagnostic); - } }; #pragma mark High-level entry points @@ -2259,8 +2217,6 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); if (!resultTarget) { - listener.applySolutionFailed(solution, expr); - // Failure already diagnosed, above, as part of applying the solution. return Type(); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 27fc91a4570fe..71dab9dc6f570 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -320,10 +320,6 @@ class ExprTypeCheckListener { /// failure. virtual Expr *appliedSolution(constraints::Solution &solution, Expr *expr); - - /// Callback invoked if application of chosen solution to - /// expression has failed. - virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); }; /// A conditional conformance that implied some other requirements. That is, \c From 5c6608f5ca1b0d7faef732a7350ddc4c68cdccf1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 11:05:00 -0800 Subject: [PATCH 51/69] [Constraint solver] Remove the FallbackDiagnosticListener. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn’t do anything any more, because we’re handling error diagnostics locally without callbacks. Also collapse typeCheckExpressionImpl() into typeCheckExpression(). --- lib/Sema/TypeCheckConstraints.cpp | 52 +++---------------------------- lib/Sema/TypeChecker.h | 9 ------ 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 29d7dd96c3066..74b6424a0da22 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2062,38 +2062,6 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } -/// Sometimes constraint solver fails without producing any diagnostics, -/// that leads to crashes down the line in AST Verifier or SILGen -/// which, as a result, are much harder to figure out. -/// -/// This class is intended to guard against situations like that by -/// keeping track of failures of different type-check phases, and -/// emitting fallback fatal error if any of them fail without producing -/// error diagnostic, and there were no errors emitted or scheduled to be -/// emitted previously. -class FallbackDiagnosticListener : public ExprTypeCheckListener { - ASTContext &Context; - TypeCheckExprOptions Options; - ExprTypeCheckListener *BaseListener; - -public: - FallbackDiagnosticListener(ASTContext &ctx, TypeCheckExprOptions options, - ExprTypeCheckListener *base) - : Context(ctx), Options(options), BaseListener(base) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - return BaseListener ? BaseListener->builtConstraints(cs, expr) : false; - } - - Expr *foundSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->foundSolution(solution, expr) : expr; - } - - Expr *appliedSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; - } -}; - #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType, @@ -2102,18 +2070,6 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { auto &Context = dc->getASTContext(); - FallbackDiagnosticListener diagListener(Context, options, listener); - return typeCheckExpressionImpl(expr, dc, convertType, convertTypePurpose, - options, diagListener, baseCS); -} - -Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - ConstraintSystem *baseCS) { - auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); @@ -2188,7 +2144,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, SmallVector viable; // Attempt to solve the constraint system. - if (cs.solve(expr, convertTo, &listener, viable, allowFreeTypeVariables)) + if (cs.solve(expr, convertTo, listener, viable, allowFreeTypeVariables)) return Type(); // If the client allows the solution to have unresolved type expressions, @@ -2202,7 +2158,8 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, auto result = expr; auto &solution = viable[0]; - result = listener.foundSolution(solution, result); + if (listener) + result = listener->foundSolution(solution, result); if (!result) return Type(); @@ -2223,7 +2180,8 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, result = resultTarget->getAsExpr(); // Notify listener that we've applied the solution. - result = listener.appliedSolution(solution, result); + if (listener) + result = listener->appliedSolution(solution, result); if (!result) return Type(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 71dab9dc6f570..c82063b17044a 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -856,15 +856,6 @@ class TypeChecker final { TypeCheckExprOptions(), listener); } -private: - static Type typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - constraints::ConstraintSystem *baseCS); - -public: /// Type check the given expression and return its type without /// applying the solution. /// From 40ca1ef1647d72de6ff6bf91c8ab5e83e280e12e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 11:13:55 -0800 Subject: [PATCH 52/69] [Constraint solver] Eliminate ExprTypeCheckListener::foundSolution() Only one client was using this, and its logic is trivially inlined into appliedSolution(). --- lib/Sema/TypeCheckConstraints.cpp | 21 ++++++--------------- lib/Sema/TypeChecker.h | 7 ------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 74b6424a0da22..a204332a66607 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2027,10 +2027,6 @@ bool ExprTypeCheckListener::builtConstraints(ConstraintSystem &cs, Expr *expr) { return false; } -Expr *ExprTypeCheckListener::foundSolution(Solution &solution, Expr *expr) { - return expr; -} - Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } @@ -2158,8 +2154,6 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, auto result = expr; auto &solution = viable[0]; - if (listener) - result = listener->foundSolution(solution, result); if (!result) return Type(); @@ -2578,16 +2572,13 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return false; } - Expr *foundSolution(Solution &solution, Expr *expr) override { - // Figure out what type the constraints decided on. - auto ty = solution.simplifyType(initType); - initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); - - // Just keep going. - return expr; - } - Expr *appliedSolution(Solution &solution, Expr *expr) override { + { + // Figure out what type the constraints decided on. + auto ty = solution.simplifyType(initType); + initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); + } + // Convert the initializer to the type of the pattern. expr = solution.coerceToType(expr, initType, Locator); if (!expr) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c82063b17044a..3c643d706e25e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -305,13 +305,6 @@ class ExprTypeCheckListener { /// constraint system, or false otherwise. virtual bool builtConstraints(constraints::ConstraintSystem &cs, Expr *expr); - /// Callback invoked once a solution has been found. - /// - /// The callback may further alter the expression, returning either a - /// new expression (to replace the result) or a null pointer to indicate - /// failure. - virtual Expr *foundSolution(constraints::Solution &solution, Expr *expr); - /// Callback invokes once the chosen solution has been applied to the /// expression. /// From e0702d9f2e333ad1c9f0a5a88dec67caca99cc62 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 Jan 2020 21:51:57 -0800 Subject: [PATCH 53/69] [Constraint solver] Generalize solve() for arbitrary solution targets. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start cleaning up the main “solve” entry point for solving an expression and applying the solution, so it handles arbitrary solution targets. This is another small step that doesn’t do much on its own, but will help with unifying the various places in the code base where we run the solver. --- lib/Sema/CSApply.cpp | 3 +- lib/Sema/CSSolver.cpp | 81 ++++++++++++++++--------------- lib/Sema/ConstraintSystem.h | 42 ++++++++++------ lib/Sema/TypeCheckConstraints.cpp | 44 +++++++++-------- 4 files changed, 93 insertions(+), 77 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index fa7d40e7b28c6..443f67d35fd29 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7331,7 +7331,8 @@ Optional ConstraintSystem::applySolution( // If we're supposed to convert the expression to some particular type, // do so now. if (shouldCoerceToContextualType()) { - resultExpr = rewriter.coerceToType(resultExpr, convertType, + resultExpr = rewriter.coerceToType(resultExpr, + simplifyType(convertType), getConstraintLocator(expr)); } else if (getType(resultExpr)->hasLValueType() && !target.isDiscardedExpr()) { diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 223fd0e3d6793..2d07b9245ba8b 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1074,7 +1074,8 @@ void ConstraintSystem::shrink(Expr *expr) { } } -static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { +static bool debugConstraintSolverForTarget( + ASTContext &C, SolutionApplicationTarget target) { if (C.TypeCheckerOpts.DebugConstraintSolver) return true; @@ -1082,14 +1083,13 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { // No need to compute the line number to find out it's not present. return false; - // Get the lines on which the expression starts and ends. + // Get the lines on which the target starts and ends. unsigned startLine = 0, endLine = 0; - if (expr->getSourceRange().isValid()) { - auto range = - Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, - expr->getSourceRange()); - startLine = C.SourceMgr.getLineNumber(range.getStart()); - endLine = C.SourceMgr.getLineNumber(range.getEnd()); + SourceRange range = target.getSourceRange(); + if (range.isValid()) { + auto charRange = Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, range); + startLine = C.SourceMgr.getLineNumber(charRange.getStart()); + endLine = C.SourceMgr.getLineNumber(charRange.getEnd()); } assert(startLine <= endLine && "expr ends before it starts?"); @@ -1107,25 +1107,26 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { return startBound != endBound; } -bool ConstraintSystem::solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables) { +Optional> ConstraintSystem::solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables +) { llvm::SaveAndRestore debugForExpr( getASTContext().TypeCheckerOpts.DebugConstraintSolver, - debugConstraintSolverForExpr(getASTContext(), expr)); + debugConstraintSolverForTarget(getASTContext(), target)); /// Dump solutions for debugging purposes. - auto dumpSolutions = [&] { + auto dumpSolutions = [&](const SolutionResult &result) { // Debug-print the set of solutions. if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - if (solutions.size() == 1) { + if (result.getKind() == SolutionResult::Success) { log << "---Solution---\n"; - solutions[0].dump(log); - } else { - for (unsigned i = 0, e = solutions.size(); i != e; ++i) { + result.getSolution().dump(log); + } else if (result.getKind() == SolutionResult::Ambiguous) { + auto solutions = result.getAmbiguousSolutions(); + for (unsigned i : indices(solutions)) { log << "--- Solution #" << i << " ---\n"; solutions[i].dump(log); } @@ -1135,45 +1136,47 @@ bool ConstraintSystem::solve(Expr *&expr, // Take up to two attempts at solving the system. The first attempts to // solve a system that is expected to be well-formed, the second kicks in - // when there is an error and attempts to salvage an ill-formed expression. - SolutionApplicationTarget target(expr, convertType, /*isDiscarded=*/false); + // when there is an error and attempts to salvage an ill-formed program. for (unsigned stage = 0; stage != 2; ++stage) { auto solution = (stage == 0) ? solveImpl(target, listener, allowFreeTypeVariables) : salvage(); switch (solution.getKind()) { - case SolutionResult::Success: + case SolutionResult::Success: { // Return the successful solution. - solutions.clear(); - solutions.push_back(std::move(solution).takeSolution()); - dumpSolutions(); - return false; + dumpSolutions(solution); + std::vector result; + result.push_back(std::move(solution).takeSolution()); + return std::move(result); + } case SolutionResult::Error: - return true; + return None; case SolutionResult::TooComplex: - getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); + getASTContext().Diags.diagnose( + target.getLoc(), diag::expression_too_complex) + .highlight(target.getSourceRange()); solution.markAsDiagnosed(); - return true; + return None; case SolutionResult::Ambiguous: // If salvaging produced an ambiguous result, it has already been // diagnosed. if (stage == 1) { solution.markAsDiagnosed(); - return true; + return None; } if (Options.contains( ConstraintSystemFlags::AllowUnresolvedTypeVariables)) { + dumpSolutions(solution); auto ambiguousSolutions = std::move(solution).takeAmbiguousSolutions(); - solutions.assign(std::make_move_iterator(ambiguousSolutions.begin()), - std::make_move_iterator(ambiguousSolutions.end())); - dumpSolutions(); - return false; + std::vector result( + std::make_move_iterator(ambiguousSolutions.begin()), + std::make_move_iterator(ambiguousSolutions.end())); + return std::move(result); } LLVM_FALLTHROUGH; @@ -1181,15 +1184,13 @@ bool ConstraintSystem::solve(Expr *&expr, case SolutionResult::UndiagnosedError: if (shouldSuppressDiagnostics()) { solution.markAsDiagnosed(); - return true; + return None; } if (stage == 1) { - diagnoseFailureFor( - SolutionApplicationTarget(expr, convertType, - /*isDiscarded=*/false)); + diagnoseFailureFor(target); solution.markAsDiagnosed(); - return true; + return None; } // Loop again to try to salvage. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 7d3846e646f7a..e5383f0a7e518 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1194,6 +1194,11 @@ class SolutionApplicationTarget { return expression.convertType; } + void setExprConversionType(Type type) { + assert(kind == Kind::expression); + expression.convertType = type; + } + bool isDiscardedExpr() const { assert(kind == Kind::expression); return expression.isDiscarded; @@ -1234,6 +1239,18 @@ class SolutionApplicationTarget { return function.body->getSourceRange(); } } + + /// Retrieve the source location for the target. + SourceLoc getLoc() const { + switch (kind) { + case Kind::expression: + return expression.expression->getLoc(); + + case Kind::function: + return function.function.getLoc(); + } + } + /// Walk the contents of the application target. SolutionApplicationTarget walk(ASTWalker &walker); }; @@ -4104,26 +4121,21 @@ class ConstraintSystem { static bool preCheckExpression(Expr *&expr, DeclContext *dc, ConstraintSystem *baseCS = nullptr); - /// Solve the system of constraints generated from provided expression. - /// - /// The expression should have already been pre-checked with - /// preCheckExpression(). + /// Solve the system of constraints generated from provided target. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target that we'll generate constraints from, which + /// may be updated by the solving process. /// \param listener The callback to check solving progress. - /// \param solutions The set of solutions to the system of constraints. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// - /// \returns true is an error occurred, false is system is consistent - /// and solutions were found. - bool solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables - = FreeTypeVariableBinding::Disallow); + /// \returns the set of solutions, if any were found, or \c None if an + /// error occurred. When \c None, an error has been emitted. + Optional> solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables + = FreeTypeVariableBinding::Disallow); /// Solve the system of constraints. /// diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index a204332a66607..0a9f90509187a 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2138,22 +2138,25 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, convertTo = getOptionalType(expr->getLoc(), var); } - SmallVector viable; // Attempt to solve the constraint system. - if (cs.solve(expr, convertTo, listener, viable, allowFreeTypeVariables)) + SolutionApplicationTarget target( + expr, convertTo, + options.contains(TypeCheckExprFlags::IsDiscarded)); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) return Type(); // If the client allows the solution to have unresolved type expressions, // check for them now. We cannot apply the solution with unresolved TypeVars, // because they will leak out into arbitrary places in the resultant AST. if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && - (viable.size() != 1 || + (viable->size() != 1 || (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { return ErrorType::get(Context); } - auto result = expr; - auto &solution = viable[0]; + auto result = target.getAsExpr(); + auto &solution = (*viable)[0]; if (!result) return Type(); @@ -2161,11 +2164,10 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, cs.applySolution(solution); // Apply the solution to the expression. - SolutionApplicationTarget target( - result, convertType.getType(), - options.contains(TypeCheckExprFlags::IsDiscarded)); bool performingDiagnostics = options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); + // FIXME: HACK! + target.setExprConversionType(convertType.getType()); auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); if (!resultTarget) { // Failure already diagnosed, above, as part of applying the solution. @@ -2244,7 +2246,6 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics); // Attempt to solve the constraint system. - SmallVector viable; const Type originalType = expr->getType(); const bool needClearType = originalType && originalType->hasError(); const auto recoverOriginalType = [&] () { @@ -2256,14 +2257,16 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // re-check. if (needClearType) expr->setType(Type()); - if (cs.solve(expr, /*convertType*/Type(), listener, viable, - allowFreeTypeVariables)) { + SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) { recoverOriginalType(); return Type(); } // Get the expression's simplified type. - auto &solution = viable[0]; + expr = target.getAsExpr(); + auto &solution = (*viable)[0]; auto &solutionCS = solution.getConstraintSystem(); Type exprType = solution.simplifyType(solutionCS.getType(expr)); @@ -2330,19 +2333,18 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( ConstraintSystem cs(dc, options); // Attempt to solve the constraint system. - SmallVector viable; - const Type originalType = expr->getType(); if (originalType && originalType->hasError()) expr->setType(Type()); - cs.solve(expr, /*convertType*/ Type(), listener, viable, - allowFreeTypeVariables); - - for (auto &solution : viable) { - auto exprType = solution.simplifyType(cs.getType(expr)); - assert(exprType && !exprType->hasTypeVariable()); - types.insert(exprType.getPointer()); + SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { + expr = target.getAsExpr(); + for (auto &solution : *viable) { + auto exprType = solution.simplifyType(cs.getType(expr)); + assert(exprType && !exprType->hasTypeVariable()); + types.insert(exprType.getPointer()); + } } } From ce357312b6b8164e2e9d8015d17215f9a5d0b6fd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 Jan 2020 21:59:39 -0800 Subject: [PATCH 54/69] [Constraint solver] Reinstate the fallback diagnostic, just in case. --- lib/Sema/CSSolver.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 2d07b9245ba8b..9e8c3678a7f2f 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1107,6 +1107,24 @@ static bool debugConstraintSolverForTarget( return startBound != endBound; } +/// If we aren't certain that we've emitted a diagnostic, emit a fallback +/// diagnostic. +static void maybeProduceFallbackDiagnostic( + ConstraintSystem &cs, SolutionApplicationTarget target) { + if (cs.Options.contains(ConstraintSystemFlags::SubExpressionDiagnostics) || + cs.Options.contains(ConstraintSystemFlags::SuppressDiagnostics)) + return; + + // Before producing fatal error here, let's check if there are any "error" + // diagnostics already emitted or waiting to be emitted. Because they are + // a better indication of the problem. + ASTContext &ctx = cs.getASTContext(); + if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors()) + return; + + ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic); +} + Optional> ConstraintSystem::solve( SolutionApplicationTarget &target, ExprTypeCheckListener *listener, @@ -1152,6 +1170,7 @@ Optional> ConstraintSystem::solve( } case SolutionResult::Error: + maybeProduceFallbackDiagnostic(*this, target); return None; case SolutionResult::TooComplex: From 5c956ba2e147e309da7c4e619587adade1584b7c Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 30 Jan 2020 22:09:14 -0800 Subject: [PATCH 55/69] [AST/ASTDumper] Fix linker error for `SWIFT_BUILD_ONLY_SYNTAXPARSERLIB` build With such a build we avoid linking the `clangAST` library. --- lib/AST/ASTDumper.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index ae20364d0bb82..dd93348812558 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3762,6 +3762,10 @@ void Type::dump() const { } void Type::dump(raw_ostream &os, unsigned indent) const { + #if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. + #endif + PrintType(os, indent).visit(*this, ""); os << "\n"; } From a7d6cd8126147b6aaa7043d02d0d77c1e44dd331 Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Thu, 30 Jan 2020 23:05:29 -0800 Subject: [PATCH 56/69] [Build System: build-script] Adds a new cache_utils module to build_swift which replaces the existing module from swift_build_support. --- utils/build_swift/build_swift/cache_utils.py | 69 ++++++++++ .../tests/build_swift/test_cache_utils.py | 121 ++++++++++++++++++ utils/build_swift/tests/utils.py | 36 +++++- .../swift_build_support/cache_util.py | 58 --------- .../swift_build_support/products/ninja.py | 4 +- .../swift_build_support/toolchain.py | 6 +- .../tests/test_cache_util.py | 99 -------------- 7 files changed, 224 insertions(+), 169 deletions(-) create mode 100644 utils/build_swift/build_swift/cache_utils.py create mode 100644 utils/build_swift/tests/build_swift/test_cache_utils.py delete mode 100644 utils/swift_build_support/swift_build_support/cache_util.py delete mode 100644 utils/swift_build_support/tests/test_cache_util.py diff --git a/utils/build_swift/build_swift/cache_utils.py b/utils/build_swift/build_swift/cache_utils.py new file mode 100644 index 0000000000000..ede762d7d2c78 --- /dev/null +++ b/utils/build_swift/build_swift/cache_utils.py @@ -0,0 +1,69 @@ +# 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 + + +""" +Cache related utitlity functions and decorators. +""" + + +from __future__ import absolute_import, unicode_literals + +import functools + + +__all__ = [ + 'cache', + 'reify', +] + + +def cache(func): + """Decorator that caches result of a function call. + + NOTE: This decorator does not play nice with methods as the created cache + is not instance-local, rather it lives in the decorator. + NOTE: When running in Python 3.2 or newer this decorator is replaced with + the standard `functools.lru_cache` using a maxsize of None. + """ + + # Use the standard functools.lru_cache decorator for Python 3.2 and newer. + if hasattr(functools, 'lru_cache'): + return functools.lru_cache(maxsize=None)(func) + + # Otherwise use a naive caching strategy. + _cache = {} + + @functools.wraps(func) + def wrapper(*args, **kwargs): + key = tuple(args) + tuple(kwargs.items()) + + if key not in _cache: + result = func(*args, **kwargs) + _cache[key] = result + return result + + return _cache[key] + return wrapper + + +def reify(func): + """Decorator that replaces the wrapped method with the result after the + first call. Used to wrap property-like methods with no arguments. + """ + + class wrapper(object): + def __get__(self, obj, type=None): + if obj is None: + return self + + result = func(obj) + setattr(obj, func.__name__, result) + return result + + return functools.update_wrapper(wrapper(), func) diff --git a/utils/build_swift/tests/build_swift/test_cache_utils.py b/utils/build_swift/tests/build_swift/test_cache_utils.py new file mode 100644 index 0000000000000..8dba0d434c49c --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_cache_utils.py @@ -0,0 +1,121 @@ +# 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 + + +from __future__ import absolute_import, unicode_literals + +import unittest + +from build_swift import cache_utils + +from .. import utils + + +try: + # Python 3.3 + from unittest import mock +except ImportError: + mock = None + + +class _CallCounter(object): + """Callable helper class used to count and return the number of times an + instance has been called. + """ + + def __init__(self): + self._counter = 0 + + def __call__(self, *args, **kwargs): + count = self._counter + self._counter += 1 + return count + + +class TestCache(unittest.TestCase): + """Unit tests for the cache decorator in the cache_utils module. + """ + + @utils.requires_module('unittest.mock') + @utils.requires_python('3.2') # functools.lru_cache + def test_replaced_with_functools_lru_cache_python_3_2(self): + with mock.patch('functools.lru_cache') as mock_lru_cache: + @cache_utils.cache + def func(): + return None + + mock_lru_cache.assert_called() + + def test_call_with_no_args(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(), 0) + self.assertEqual(func(), 0) + + def test_call_with_args(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(0), 0) + self.assertEqual(func(0), 0) + + self.assertEqual(func(1), 1) + self.assertEqual(func(1), 1) + + self.assertEqual(func(2), 2) + self.assertEqual(func(2), 2) + + def test_call_with_args_and_kwargs(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(n=0), 0) + self.assertEqual(func(n=0), 0) + + self.assertEqual(func(a=1, b='b'), 1) + self.assertEqual(func(a=1, b='b'), 1) + + self.assertEqual(func(0, x=1, y=2.0), 2) + self.assertEqual(func(0, x=1, y=2.0), 2) + + +class TestReify(unittest.TestCase): + """Unit tests for the reify decorator in the cache_utils module. + """ + + def test_replaces_attr_after_first_call(self): + class Counter(object): + def __init__(self): + self._counter = 0 + + @cache_utils.reify + def count(self): + count = self._counter + self._counter += 1 + return count + + counter = Counter() + + self.assertEqual(counter.count, 0) + self.assertEqual(counter.count, 0) + + # Assert that the count property has been replaced with the constant. + self.assertEqual(getattr(counter, 'count'), 0) diff --git a/utils/build_swift/tests/utils.py b/utils/build_swift/tests/utils.py index c21d8361bb8fd..85fc24350e9ff 100644 --- a/utils/build_swift/tests/utils.py +++ b/utils/build_swift/tests/utils.py @@ -15,9 +15,11 @@ import sys import unittest -from six import StringIO +from build_swift import cache_utils +from build_swift.versions import Version -from swift_build_support.swift_build_support import cache_util +import six +from six import StringIO __all__ = [ @@ -27,6 +29,7 @@ 'requires_attr', 'requires_module', 'requires_platform', + 'requires_python', 'BUILD_SCRIPT_IMPL_PATH', 'BUILD_SWIFT_PATH', @@ -38,6 +41,8 @@ # ----------------------------------------------------------------------------- # Constants +_PYTHON_VERSION = Version(platform.python_version()) + TESTS_PATH = os.path.abspath(os.path.dirname(__file__)) BUILD_SWIFT_PATH = os.path.abspath(os.path.join(TESTS_PATH, os.pardir)) UTILS_PATH = os.path.abspath(os.path.join(BUILD_SWIFT_PATH, os.pardir)) @@ -124,9 +129,10 @@ def __exit__(self, exc_type, exc_value, traceback): sys.stderr = self._old_stdout -@cache_util.cached +@cache_utils.cache def requires_attr(obj, attr): - """ + """Decorator used to skip tests if an object does not have the required + attribute. """ try: @@ -137,7 +143,7 @@ def requires_attr(obj, attr): attr, obj)) -@cache_util.cached +@cache_utils.cache def requires_module(fullname): """Decorator used to skip tests if a module is not imported. """ @@ -148,7 +154,7 @@ def requires_module(fullname): return unittest.skip('Unable to import "{}"'.format(fullname)) -@cache_util.cached +@cache_utils.cache def requires_platform(name): """Decorator used to skip tests if not running on the given platform. """ @@ -157,4 +163,20 @@ def requires_platform(name): return lambda func: func return unittest.skip( - 'Required platform "{}"" does not match system'.format(name)) + 'Required platform "{}" does not match system'.format(name)) + + +@cache_utils.cache +def requires_python(version): + """Decorator used to skip tests if the running Python version is not + greater or equal to the required version. + """ + + if isinstance(version, six.string_types): + version = Version(version) + + if _PYTHON_VERSION >= version: + return lambda func: func + + return unittest.skip( + 'Requires Python version {} or greater'.format(version)) diff --git a/utils/swift_build_support/swift_build_support/cache_util.py b/utils/swift_build_support/swift_build_support/cache_util.py deleted file mode 100644 index 493b558ea9084..0000000000000 --- a/utils/swift_build_support/swift_build_support/cache_util.py +++ /dev/null @@ -1,58 +0,0 @@ -# swift_build_support/cache_util.py -----------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -""" -Cache related utilities -""" -# ---------------------------------------------------------------------------- - -from functools import update_wrapper - -__all__ = [ - 'cached', - 'reify' -] - - -def cached(func): - """Decorator that caches result of method or function. - - Note: Support method or function. - """ - cache = {} - - def wrapper(*args, **kwargs): - key = tuple(args) + tuple(kwargs.items()) - if key not in cache: - result = func(*args, **kwargs) - cache[key] = result - return result - else: - return cache[key] - - return update_wrapper(wrapper, func) - - -def reify(func): - """Decorator that replaces the wrapped method with the result after the - first call. - - Note: Support method that takes no arguments. - """ - class Wrapper(object): - def __get__(self, obj, objtype=None): - if obj is None: - return self - result = func(obj) - setattr(obj, func.__name__, result) - return result - - return update_wrapper(Wrapper(), func) diff --git a/utils/swift_build_support/swift_build_support/products/ninja.py b/utils/swift_build_support/swift_build_support/products/ninja.py index 0ffd054ccafc4..53ebb15c6e32e 100644 --- a/utils/swift_build_support/swift_build_support/products/ninja.py +++ b/utils/swift_build_support/swift_build_support/products/ninja.py @@ -18,10 +18,10 @@ import platform import sys +from build_swift.build_swift import cache_utils from build_swift.build_swift.wrappers import xcrun from . import product -from .. import cache_util from .. import shell @@ -44,7 +44,7 @@ def __init__(self, product_class, args, toolchain, workspace): self.args = args self.toolchain = toolchain - @cache_util.reify + @cache_utils.reify def ninja_bin_path(self): return os.path.join(self.build_dir, 'ninja') diff --git a/utils/swift_build_support/swift_build_support/toolchain.py b/utils/swift_build_support/swift_build_support/toolchain.py index 1395397a80332..b7c8247374e2a 100644 --- a/utils/swift_build_support/swift_build_support/toolchain.py +++ b/utils/swift_build_support/swift_build_support/toolchain.py @@ -18,10 +18,10 @@ import platform +from build_swift.build_swift import cache_utils from build_swift.build_swift.shell import which from build_swift.build_swift.wrappers import xcrun -from . import cache_util from . import shell @@ -44,7 +44,7 @@ def _register(name, *tool): def _getter(self): return self.find_tool(*tool) _getter.__name__ = name - setattr(Toolchain, name, cache_util.reify(_getter)) + setattr(Toolchain, name, cache_utils.reify(_getter)) if platform.system() == 'Windows': @@ -162,7 +162,7 @@ def __init__(self): suffixes = ['38', '37', '36', '35'] super(FreeBSD, self).__init__(suffixes) - @cache_util.reify + @cache_utils.reify def _release_date(self): """Return the release date for FreeBSD operating system on this host. If the release date cannot be ascertained, return None. diff --git a/utils/swift_build_support/tests/test_cache_util.py b/utils/swift_build_support/tests/test_cache_util.py deleted file mode 100644 index c65f308b48295..0000000000000 --- a/utils/swift_build_support/tests/test_cache_util.py +++ /dev/null @@ -1,99 +0,0 @@ -# tests/test_cache_util.py --------------------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- - -import unittest - -from swift_build_support import cache_util - - -my_func_called = 0 -my_kfunc_called = 0 - - -@cache_util.cached -def my_func(arg1, arg2): - global my_func_called - my_func_called += 1 - return "my_func_result(%s, %s)" % (arg1, arg2) - - -@cache_util.cached -def my_kfunc(arg1, arg2): - global my_kfunc_called - my_kfunc_called += 1 - return "my_kfunc_result(%s, %s)" % (arg1, arg2) - - -class MyClass(object): - def __init__(self, prop=None): - self.my_method_called = 0 - self.my_prop_called = 0 - self.prop_value = prop - - @cache_util.cached - def my_method(self, arg1, arg2): - self.my_method_called += 1 - return "my_meth_result(%s, %s)" % (arg1, arg2) - - @cache_util.reify - def my_prop(self): - self.my_prop_called += 1 - return "==%s==" % (self.prop_value) - - -class CacheUtilTestCase(unittest.TestCase): - def test_cached_func(self): - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 1) - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 1) - self.assertEqual(my_func("bar", 42), "my_func_result(bar, 42)") - self.assertEqual(my_func_called, 2) - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 2) - - def test_cached_kwfunc(self): - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 1) - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 1) - self.assertEqual(my_kfunc("bar", arg2=42), "my_kfunc_result(bar, 42)") - self.assertEqual(my_kfunc_called, 2) - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 2) - - def test_cached_method(self): - obj1 = MyClass() - self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj1.my_method_called, 1) - self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj1.my_method_called, 1) - self.assertEqual(obj1.my_method("bar", 12), "my_meth_result(bar, 12)") - self.assertEqual(obj1.my_method_called, 2) - - # Test for instance independency. - obj2 = MyClass() - self.assertEqual(obj2.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj2.my_method_called, 1) - self.assertEqual(obj1.my_method_called, 2) - - def test_reify(self): - obj1 = MyClass(prop='foo') - self.assertEqual(obj1.my_prop, '==foo==') - self.assertEqual(obj1.my_prop_called, 1) - self.assertEqual(obj1.my_prop, '==foo==') - self.assertEqual(obj1.my_prop_called, 1) - - # Test for instance independency. - obj2 = MyClass(prop='bar') - self.assertEqual(obj2.my_prop, '==bar==') - self.assertEqual(obj1.my_prop, '==foo==') From fce335bf360e439fccab15e729d655565d96b30d Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 31 Jan 2020 08:52:45 -0800 Subject: [PATCH 57/69] SILGen: Fix withoutActuallyEscaping of 'c' closures They don't have a context and therefore are not consumed. Fixes a failing assert. rdar://59046275 --- lib/SILGen/SILGenExpr.cpp | 12 ++++++++---- test/SILGen/without_actually_escaping.swift | 13 +++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 74f9b04070f75..879c9082c6272 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -5023,13 +5023,17 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( return visit(E->getSubExpr(), C); }; - // Handle @convention(block). No withoutActuallyEscaping verification yet. - if (silFnTy->getExtInfo().getRepresentation() != - SILFunctionTypeRepresentation::Thick) { + // Handle @convention(block) an @convention(c). No withoutActuallyEscaping + // verification yet. + auto closureRepresentation = silFnTy->getExtInfo().getRepresentation(); + if (closureRepresentation != SILFunctionTypeRepresentation::Thick) { auto escapingClosure = SGF.B.createConvertFunction(E, functionValue, escapingFnTy, /*WithoutActuallyEscaping=*/true); - return visitSubExpr(escapingClosure, true /*isClosureConsumable*/); + bool isBlockConvention = + closureRepresentation == SILFunctionTypeRepresentation::Block; + return visitSubExpr(escapingClosure, + isBlockConvention /*isClosureConsumable*/); } // Convert it to an escaping function value. diff --git a/test/SILGen/without_actually_escaping.swift b/test/SILGen/without_actually_escaping.swift index 2979157df33b5..1d5e1569e627e 100644 --- a/test/SILGen/without_actually_escaping.swift +++ b/test/SILGen/without_actually_escaping.swift @@ -100,3 +100,16 @@ func withoutActuallyEscapingConflict() { modifyAndPerform(&localVar, closure: $0) } } + +// CHECK-LABEL: sil [ossa] @$s25without_actually_escaping0A25ActuallyEscapingCFunction8functionyyyXC_tF +// CHECK: bb0([[ARG:%.*]] : $@convention(c) @noescape () -> ()): +// CHECK: [[E:%.*]] = convert_function [[ARG]] : $@convention(c) @noescape () -> () to [without_actually_escaping] $@convention(c) () -> () +// CHECK: [[F:%.*]] = function_ref @$s25without_actually_escaping0A25ActuallyEscapingCFunction8functionyyyXC_tFyyyXCXEfU_ : $@convention(thin) (@convention(c) () -> ()) -> () +// CHECK: apply [[F]]([[E]]) : $@convention(thin) (@convention(c) () -> ()) -> () +public func withoutActuallyEscapingCFunction(function: (@convention(c) () -> Void)) { + withoutActuallyEscaping(function) { f in + var pointer: UnsafeRawPointer? = nil + pointer = unsafeBitCast(f, to: UnsafeRawPointer.self) + print(pointer) + } +} From 6b9c570b38fbf210213a26651cbe2f17045e7f48 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 31 Jan 2020 15:53:43 -0800 Subject: [PATCH 58/69] build: correct the handling for the static variants The static variants should use the same flags as the dynamic variants. We missed the name conversion in the variable computation causing the static and dynamic builds to diverge. --- utils/build-script-impl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index b5b3264410f36..255ac4beb3844 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1416,7 +1416,7 @@ for host in "${ALL_HOSTS[@]}"; do module_cache="${build_dir}/module-cache" # Add any specific cmake options specified by build-script - product_cmake_options_name=$(to_varname "${product}")_CMAKE_OPTIONS + product_cmake_options_name=$(to_varname "${product/_static}")_CMAKE_OPTIONS product_cmake_options=(${!product_cmake_options_name}) # convert to array cmake_options+=("${product_cmake_options[@]}") From 950c73f47163e2c0c09511dd9109576ebdcf1e31 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 16:19:42 -0800 Subject: [PATCH 59/69] [Serialization] Bump module format version. The change to introduce a "top-level" bit for VarDecls requires a module format version bump, per https://github.com/apple/swift/pull/29024. Fixes rdar://problem/59078925 --- lib/Serialization/ModuleFormat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9c32d255477b4..c2769a9c5ef97 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 534; // add SIL parameter differentiability +const uint16_t SWIFTMODULE_VERSION_MINOR = 535; // top-level var decls /// A standard hash seed used for all string hashes in a serialized module. /// From a92724b09c90f2bc9dfa0a7e5c49bd98ebb5b023 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 31 Jan 2020 15:28:30 -0800 Subject: [PATCH 60/69] AST: Fix computeSelfParam() to respect __consuming on class methods Fixes . --- lib/AST/ASTContext.cpp | 7 ++----- test/SILGen/value_ownership_class.swift | 7 +++++++ test/decl/protocol/protocols.swift | 7 ++++--- 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 test/SILGen/value_ownership_class.swift diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ef6969430da40..a5d494d2805df 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2431,7 +2431,8 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isInitializingCtor) { // initializing constructors of value types always have an implicitly // inout self. - selfAccess = SelfAccessKind::Mutating; + if (!containerTy->hasReferenceSemantics()) + selfAccess = SelfAccessKind::Mutating; } else { // allocating constructors have metatype 'self'. isStatic = true; @@ -2459,10 +2460,6 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isStatic) return AnyFunctionType::Param(MetatypeType::get(selfTy, Ctx)); - // Reference types have 'self' of type T. - if (containerTy->hasReferenceSemantics()) - return AnyFunctionType::Param(selfTy); - auto flags = ParameterTypeFlags(); switch (selfAccess) { case SelfAccessKind::Consuming: diff --git a/test/SILGen/value_ownership_class.swift b/test/SILGen/value_ownership_class.swift new file mode 100644 index 0000000000000..0c8fbe81ea54a --- /dev/null +++ b/test/SILGen/value_ownership_class.swift @@ -0,0 +1,7 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +class ConsumingClass { + __consuming func consumingMethod() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s21value_ownership_class14ConsumingClassC15consumingMethodyyF : $@convention(method) (@owned ConsumingClass) -> () { diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index f0b6bf4977bc6..2ed52c88bd3b7 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -101,9 +101,10 @@ struct DoesNotConform : Up { // Circular protocols -protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 3 {{protocol 'CircleMiddle' refines itself}} -protocol CircleStart : CircleEnd { func circle_start() } -// expected-note@-1 3 {{protocol 'CircleStart' declared here}} +protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 2 {{protocol 'CircleMiddle' refines itself}} +// expected-note@-1 {{protocol 'CircleMiddle' declared here}} +protocol CircleStart : CircleEnd { func circle_start() } // expected-error {{protocol 'CircleStart' refines itself}} +// expected-note@-1 2 {{protocol 'CircleStart' declared here}} protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 3 {{protocol 'CircleEnd' declared here}} protocol CircleEntry : CircleTrivial { } From 62ca2aaf22f76f63ce8924da5e1ff1dbda356d1a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 31 Jan 2020 15:29:11 -0800 Subject: [PATCH 61/69] SIL: Fix an old FIXME --- lib/SIL/SILFunctionType.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 6e0fac061678f..8b667f595bdb2 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -983,10 +983,8 @@ class DestructureInputs { for (auto i : indices(substTupleTy.getElementTypes())) { auto &elt = substTupleTy->getElement(i); auto ownership = elt.getParameterFlags().getValueOwnership(); - // FIXME(swift3): Once the entire parameter list is no longer a - // target for substitution, re-enable this. - // assert(ownership == ValueOwnership::Default); - // assert(!elt.isVararg()); + assert(ownership == ValueOwnership::Default); + assert(!elt.isVararg()); visit(ownership, forSelf, origType.getTupleElementType(i), CanType(elt.getRawType()), rep); From bf124b8a4a24d14f8a5bb01300f379583966495d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 17:55:15 -0800 Subject: [PATCH 62/69] [Type checker] Use typeCheckCondition() rather than fake it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Type checking an expression pattern had a hack to work around some diagnostics issues with conditions. These issues have been addressed by the new diagnostic infrastructure, so remove this unnecessary use of ExprTypeCheckListener and check the expression pattern condition using… typeCheckCondition(). --- lib/Sema/TypeCheckConstraints.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 0a9f90509187a..1a5e16abeff3f 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3209,30 +3209,7 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, /*Implicit=*/true); // Check the expression as a condition. - // - // TODO: Type-check of `~=` operator can't (yet) use `typeCheckCondition` - // because that utilizes contextual type which interferes with diagnostics. - // We don't yet have a full access to pattern-matching context in - // constraint system, which is required to enable these situations - // to be properly diagnosed. - struct ConditionListener : public ExprTypeCheckListener { - // Add the appropriate Boolean constraint. - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - // Otherwise, the result must be convertible to Bool. - auto boolDecl = cs.getASTContext().getBoolDecl(); - if (!boolDecl) - return true; - - // Condition must convert to Bool. - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), - boolDecl->getDeclaredType(), - cs.getConstraintLocator(expr)); - return false; - } - }; - - ConditionListener listener; - bool hadError = !typeCheckExpression(matchCall, DC, &listener); + bool hadError = typeCheckCondition(matchCall, DC); // Save the type-checked expression in the pattern. EP->setMatchExpr(matchCall); // Set the type on the pattern. From 4b0e7b27808247e63be70c8f46abf71e4583cc19 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 18:20:46 -0800 Subject: [PATCH 63/69] [Type checker] Sink logic for @autoclosure default parameters into the solver Rather than use an ExprTypeCheckListener subclass to introduce the autoclosure expression, do it at the end of solving. --- lib/Sema/CSDiagnostics.cpp | 3 +++ lib/Sema/CSSimplify.cpp | 1 + lib/Sema/TypeCheckConstraints.cpp | 44 +++++++++++++------------------ lib/Sema/TypeChecker.h | 4 +++ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b85e37691bb76..c0ff3daf3f172 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -560,6 +560,7 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( case CTP_ReturnSingleExpr: return diag::cannot_convert_to_return_type; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return diag::cannot_convert_default_arg_value; case CTP_YieldByValue: return diag::cannot_convert_yield_value; @@ -2085,6 +2086,7 @@ getContextualNilDiagnostic(ContextualTypePurpose CTP) { case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value_nil; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return diag::cannot_convert_default_arg_value_nil; case CTP_YieldByValue: return diag::cannot_convert_yield_value_nil; @@ -2863,6 +2865,7 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return forProtocol ? diag::cannot_convert_default_arg_value_protocol : diag::cannot_convert_default_arg_value; case CTP_YieldByValue: diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 09a1c7eda4973..e017aeb2cee63 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9130,6 +9130,7 @@ void ConstraintSystem::addContextualConversionConstraint( case CTP_ThrowStmt: case CTP_EnumCaseRawValue: case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: case CTP_ClosureResult: case CTP_DictionaryKey: case CTP_DictionaryValue: diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1a5e16abeff3f..d1ca555020aea 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2107,6 +2107,14 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, } } + // For an @autoclosure default parameter, we want to convert to the result + // type. Stash the autoclosure default parameter type. + FunctionType *autoclosureDefaultParamType = nullptr; + if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { + autoclosureDefaultParamType = convertType.getType()->castTo(); + convertType.setType(autoclosureDefaultParamType->getResult()); + } + // Tell the constraint system what the contextual type is. This informs // diagnostics and is a hint for various performance optimizations. // FIXME: Look through LoadExpr. This is an egregious hack due to the @@ -2130,6 +2138,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType; Type convertTo = convertType.getType(); + if (options.contains(TypeCheckExprFlags::ExpressionTypeMustBeOptional)) { assert(!convertTo && "convertType and type check options conflict"); auto *convertTypeLocator = @@ -2175,6 +2184,12 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, } result = resultTarget->getAsExpr(); + // For an @autoclosure default parameter type, add the autoclosure + // conversion. + if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { + result = cs.buildAutoClosureExpr(result, autoclosureDefaultParamType); + } + // Notify listener that we've applied the solution. if (listener) result = listener->appliedSolution(solution, result); @@ -2204,32 +2219,9 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, Type paramType, bool isAutoClosure) { assert(paramType && !paramType->hasError()); - - if (isAutoClosure) { - class AutoClosureListener : public ExprTypeCheckListener { - FunctionType *ParamType; - - public: - AutoClosureListener(FunctionType *paramType) - : ParamType(paramType) {} - - Expr *appliedSolution(constraints::Solution &solution, - Expr *expr) override { - auto &cs = solution.getConstraintSystem(); - return cs.buildAutoClosureExpr(expr, ParamType); - } - }; - - auto *fnType = paramType->castTo(); - AutoClosureListener listener(fnType); - return typeCheckExpression(defaultValue, DC, - TypeLoc::withoutLoc(fnType->getResult()), - CTP_DefaultParameter, TypeCheckExprOptions(), - &listener); - } - - return typeCheckExpression(defaultValue, DC, TypeLoc::withoutLoc(paramType), - CTP_DefaultParameter); + return typeCheckExpression( + defaultValue, DC, TypeLoc::withoutLoc(paramType), + isAutoClosure ? CTP_AutoclosureDefaultParameter : CTP_DefaultParameter); } Type TypeChecker:: diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 3c643d706e25e..341f00a9b0e63 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -133,6 +133,10 @@ enum ContextualTypePurpose { CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. + /// Default value in @autoclosure parameter + /// 'foo(a : @autoclosure () -> Int = 42)'. + CTP_AutoclosureDefaultParameter, + CTP_CalleeResult, ///< Constraint is placed on the result of a callee. CTP_CallArgument, ///< Call to function or operator requires type. CTP_ClosureResult, ///< Closure result expects a specific type. From e056d46ba42da7338b7ef9672739694874b06b3c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 20:54:32 -0800 Subject: [PATCH 64/69] [Type checker] Remove unused overload of typeCheckExpression(). --- lib/Sema/TypeChecker.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 341f00a9b0e63..93d9e36e79d3f 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -847,12 +847,6 @@ class TypeChecker final { ExprTypeCheckListener *listener = nullptr, constraints::ConstraintSystem *baseCS = nullptr); - static Type typeCheckExpression(Expr *&expr, DeclContext *dc, - ExprTypeCheckListener *listener) { - return TypeChecker::typeCheckExpression(expr, dc, TypeLoc(), CTP_Unused, - TypeCheckExprOptions(), listener); - } - /// Type check the given expression and return its type without /// applying the solution. /// From 9fa03cd6480db89a940730446562a7bafb4406de Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:11:16 -0800 Subject: [PATCH 65/69] [Type checker] Eliminate TypeCheckExprFlags::ConvertTypeIsOnlyAHint. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag is recoverable from the contextual type purpose; don’t duplicate the logic. --- lib/Sema/ConstraintSystem.h | 2 +- lib/Sema/TypeCheckConstraints.cpp | 41 ++++++++++++++++++++++++++++--- lib/Sema/TypeChecker.h | 14 +++-------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index e5383f0a7e518..f18bbd8cc8ff4 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -789,7 +789,7 @@ using OpenedTypeMap = /// Describes contextual type information about a particular expression /// within a constraint system. -struct ContextualTypeInfo { +struct ContextualTypeInfo { TypeLoc typeLoc; ContextualTypePurpose purpose; bool isOpaqueReturnType = false; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d1ca555020aea..1244e32537770 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2058,6 +2058,40 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } +/// Whether the contextual type provided for the given purpose is only a +/// hint, and not a requirement. +static bool contextualTypeIsOnlyAHint(ContextualTypePurpose ctp, + TypeCheckExprOptions options) { + switch (ctp) { + case CTP_Initialization: + return !options.contains( + TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType); + case CTP_ForEachStmt: + return true; + case CTP_Unused: + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_YieldByValue: + case CTP_YieldByReference: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: + case CTP_CalleeResult: + case CTP_CallArgument: + case CTP_ClosureResult: + case CTP_ArrayElement: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_AssignSource: + case CTP_SubscriptAssignSource: + case CTP_Condition: + case CTP_CannotFail: + return false; + } +} + #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType, @@ -2128,7 +2162,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // If the convertType is *only* provided for that hint, then null it out so // that we don't later treat it as an actual conversion constraint. - if (options.contains(TypeCheckExprFlags::ConvertTypeIsOnlyAHint)) + if (contextualTypeIsOnlyAHint(convertTypePurpose, options)) convertType = TypeLoc(); // If the client can handle unresolved type variables, leave them in the @@ -2655,7 +2689,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, TypeLoc contextualType; auto contextualPurpose = CTP_Unused; - TypeCheckExprOptions flags = TypeCheckExprFlags::ConvertTypeIsOnlyAHint; + TypeCheckExprOptions flags = None; // Set the contextual purpose even if the pattern doesn't have a type so // if there's an error we can use that information to inform diagnostics. @@ -2674,7 +2708,6 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // opaque type. if (auto opaqueType = patternType->getAs()){ flags |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; - flags -= TypeCheckExprFlags::ConvertTypeIsOnlyAHint; } // Only provide a TypeLoc if it makes sense to allow diagnostics. @@ -3042,7 +3075,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Type-check the for-each loop sequence and element pattern. auto resultTy = TypeChecker::typeCheckExpression( seq, dc, TypeLoc::withoutLoc(sequenceProto->getDeclaredType()), - CTP_ForEachStmt, TypeCheckExprFlags::ConvertTypeIsOnlyAHint, &listener); + CTP_ForEachStmt, None, &listener); if (!resultTy) return true; return false; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 93d9e36e79d3f..e24a6a7e879c0 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -174,12 +174,6 @@ enum class TypeCheckExprFlags { /// left in-tact. AllowUnresolvedTypeVariables = 0x08, - /// If set, the 'convertType' specified to typeCheckExpression should not - /// produce a conversion constraint, but it should be used to guide the - /// solution in terms of performance optimizations of the solver, and in terms - /// of guiding diagnostics. - ConvertTypeIsOnlyAHint = 0x10, - /// If set, this expression isn't embedded in a larger expression or /// statement. This should only be used for syntactic restrictions, and should /// not affect type checking itself. @@ -821,11 +815,9 @@ class TypeChecker final { /// to be possible. /// /// \param convertType The type that the expression is being converted to, - /// or null if the expression is standalone. If the 'ConvertTypeIsOnlyAHint' - /// option is specified, then this is only a hint, it doesn't produce a full - /// conversion constraint. The location information is only used for - /// diagnostics should the conversion fail; it is safe to pass a TypeLoc - /// without location information. + /// or null if the expression is standalone. The location information is + /// only used for diagnostics should the conversion fail; it is safe to pass + /// a TypeLoc without location information. /// /// \param options Options that control how type checking is performed. /// From 25c2e02bc9cd7a8c77b74b152bae97eb3584a992 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:20:14 -0800 Subject: [PATCH 66/69] [Type checker] Eliminate TypeCheckExprFlags::DisableStructuralChecks. This is always set along with SubExpressionDiagnostics, so use that for both purposes. That flag will go away when `CSDiag.cpp` goes away. --- lib/Sema/CSDiag.cpp | 9 ++------- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeChecker.h | 4 ---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index ddb0d6d86c42d..5ded14fcf79c8 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -394,15 +394,10 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // type check operation. Expr *preCheckedExpr = subExpr; - // Disable structural checks, because we know that the overall expression - // has type constraint problems, and we don't want to know about any - // syntactic issues in a well-typed subexpression (which might be because - // the context is missing). - TypeCheckExprOptions TCEOptions = TypeCheckExprFlags::DisableStructuralChecks; - // Make sure that typechecker knows that this is an attempt // to diagnose a problem. - TCEOptions |= TypeCheckExprFlags::SubExpressionDiagnostics; + TypeCheckExprOptions TCEOptions = + TypeCheckExprFlags::SubExpressionDiagnostics; // Claim that the result is discarded to preserve the lvalue type of // the expression. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1244e32537770..2e89c44c8f7a4 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2240,7 +2240,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // Unless the client has disabled them, perform syntactic checks on the // expression now. if (!cs.shouldSuppressDiagnostics() && - !options.contains(TypeCheckExprFlags::DisableStructuralChecks)) { + !options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)) { bool isExprStmt = options.contains(TypeCheckExprFlags::IsExprStmt); performSyntacticExprDiagnostics(result, dc, isExprStmt); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e24a6a7e879c0..e88546ac33913 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -163,10 +163,6 @@ enum class TypeCheckExprFlags { /// disables constraints forcing an lvalue result to be loadable. IsDiscarded = 0x01, - /// Whether the client wants to disable the structural syntactic restrictions - /// that we force for style or other reasons. - DisableStructuralChecks = 0x02, - /// If set, the client wants a best-effort solution to the constraint system, /// but can tolerate a solution where all of the constraints are solved, but /// not all type variables have been determined. In this case, the constraint From a493004a1394161c1f3e8f5b2f79dcf99c06f3f1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:40:21 -0800 Subject: [PATCH 67/69] [Type checker] Remove unused IsInOutYield flag. --- lib/Sema/TypeChecker.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e88546ac33913..47e4aef224621 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -175,9 +175,6 @@ enum class TypeCheckExprFlags { /// not affect type checking itself. IsExprStmt = 0x20, - /// This is an inout yield. - IsInOutYield = 0x100, - /// If set, a conversion constraint should be specified so that the result of /// the expression is an optional type. ExpressionTypeMustBeOptional = 0x200, From c5ed8d67a981c5af62f501bf086f4f07166f9b37 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:40:39 -0800 Subject: [PATCH 68/69] [Constraint solver] Fix tautological assert. --- 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 c0ff3daf3f172..6f0a4f38a3c6c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -641,7 +641,7 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() { switch (last.getKind()) { case ConstraintLocator::ContextualType: { auto purpose = getContextualTypePurpose(); - assert(!(purpose == CTP_Unused && purpose == CTP_CannotFail)); + assert(!(purpose == CTP_Unused || purpose == CTP_CannotFail)); // If this is call to a closure e.g. `let _: A = { B() }()` // let's point diagnostic to its result. From 3190433d58cd8b4fde573792d15894200379591c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 22:03:13 -0800 Subject: [PATCH 69/69] [Constraint system] Give SolutionApplicationTarget purpose. A contextual type purpose, that is, so it captures more about what the entity is that is being solved. --- lib/Sema/ConstraintSystem.h | 33 ++++++++++++++++++++++++++++--- lib/Sema/TypeCheckConstraints.cpp | 8 +++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index f18bbd8cc8ff4..96c87701064b8 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1149,8 +1149,11 @@ class SolutionApplicationTarget { struct { Expr *expression; + /// The purpose of the contextual type. + ContextualTypePurpose contextualPurpose; + /// The type to which the expression should be converted. - Type convertType; + TypeLoc convertType; /// Whether the expression result will be discarded at the end. bool isDiscarded; @@ -1163,9 +1166,18 @@ class SolutionApplicationTarget { }; public: - SolutionApplicationTarget(Expr *expr, Type convertType, bool isDiscarded) { + SolutionApplicationTarget(Expr *expr, + ContextualTypePurpose contextualPurpose, + Type convertType, bool isDiscarded) + : SolutionApplicationTarget(expr, contextualPurpose, + TypeLoc::withoutLoc(convertType), + isDiscarded) { } + + SolutionApplicationTarget(Expr *expr, ContextualTypePurpose contextualPurpose, + TypeLoc convertType, bool isDiscarded) { kind = Kind::expression; expression.expression = expr; + expression.contextualPurpose = contextualPurpose; expression.convertType = convertType; expression.isDiscarded = isDiscarded; } @@ -1189,16 +1201,31 @@ class SolutionApplicationTarget { } } + ContextualTypePurpose getExprContextualTypePurpose() const { + assert(kind == Kind::expression); + return expression.contextualPurpose; + } + Type getExprConversionType() const { + assert(kind == Kind::expression); + return expression.convertType.getType(); + } + + TypeLoc getExprConversionTypeLoc() const { assert(kind == Kind::expression); return expression.convertType; } void setExprConversionType(Type type) { + assert(kind == Kind::expression); + expression.convertType = TypeLoc::withoutLoc(type); + } + + void setExprConversionTypeLoc(TypeLoc type) { assert(kind == Kind::expression); expression.convertType = type; } - + bool isDiscardedExpr() const { assert(kind == Kind::expression); return expression.isDiscarded; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 2e89c44c8f7a4..d81a3936ecd85 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2183,7 +2183,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // Attempt to solve the constraint system. SolutionApplicationTarget target( - expr, convertTo, + expr, convertTypePurpose, convertTo, options.contains(TypeCheckExprFlags::IsDiscarded)); auto viable = cs.solve(target, listener, allowFreeTypeVariables); if (!viable) @@ -2283,7 +2283,8 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // re-check. if (needClearType) expr->setType(Type()); - SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + SolutionApplicationTarget target( + expr, CTP_Unused, Type(), /*isDiscarded=*/false); auto viable = cs.solve(target, listener, allowFreeTypeVariables); if (!viable) { recoverOriginalType(); @@ -2363,7 +2364,8 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( if (originalType && originalType->hasError()) expr->setType(Type()); - SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + SolutionApplicationTarget target( + expr, CTP_Unused, Type(), /*isDiscarded=*/false); if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { expr = target.getAsExpr(); for (auto &solution : *viable) {