From aaa4e45fa819ad83f70553bc162532199d436217 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Mon, 28 Sep 2020 16:26:30 -0700 Subject: [PATCH 01/20] [ClangImporter] Update umbrella header diagnostic handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In apple/llvm-project#1860, the diagnostic for a missing import in an umbrella header was improved by moving it to the end of the file and including a fix-it suggesting the import that would be needed. This breaks two things on the Swift side: • One Swift test assumes the old source location will be used. • The `ClangSourceBufferImporter` doesn’t work correctly when a diagnostic is emitted at EOF. It tries to create a virtual file covering EOF..; diff --git a/test/ClangImporter/diags_from_module.swift b/test/ClangImporter/diags_from_module.swift index 8e74e7a8ca062..5cc81a87e1291 100644 --- a/test/ClangImporter/diags_from_module.swift +++ b/test/ClangImporter/diags_from_module.swift @@ -37,7 +37,7 @@ import Module // CHECK-PRIMARY: diags_from_module.swift:[[@LINE-4]]:8: error: could not build Objective-C module 'Module' // CHECK-WARN: Sub2.h:7:2: warning: here is some warning about something -// CHECK-WARN: :1:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' +// CHECK-WARN: Module.h:20:1: warning: umbrella header for module 'Module' does not include header 'NotInModule.h' // FIXME: show [-Wincomplete-umbrella] // CHECK-NO-WARN-NOT: warning about something From ab3c5dee3ea2bea8e29d1e70ed8b9e7a2aeff1be Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Sep 2020 16:59:21 -0700 Subject: [PATCH 02/20] [Concurrency] Introduce Actor protocol to which actor classes all conform. Introduce a new Actor protocol, which is a class-bound protocol with only one requirement: func enqueue(partialTask: PartialAsyncTask) All actor classes implicitly conform to this protocol, and will synthesize a (currently empty) definition of `enqueue(partialTask:)` unless a suitable one is provided explicitly. --- include/swift/AST/Decl.h | 1 + include/swift/AST/DiagnosticsSema.def | 7 ++ include/swift/AST/KnownIdentifiers.def | 4 + include/swift/AST/KnownProtocols.def | 1 + lib/AST/ASTContext.cpp | 3 + lib/AST/Decl.cpp | 5 +- lib/AST/ProtocolConformance.cpp | 6 ++ lib/IRGen/GenMeta.cpp | 1 + lib/Sema/CMakeLists.txt | 1 + lib/Sema/DerivedConformanceActor.cpp | 104 +++++++++++++++++++++++ lib/Sema/DerivedConformances.cpp | 10 +++ lib/Sema/DerivedConformances.h | 26 +++++- lib/Sema/TypeCheckConcurrency.cpp | 9 ++ lib/Sema/TypeCheckConcurrency.h | 5 ++ lib/Sema/TypeCheckProtocol.cpp | 15 +++- stdlib/public/Concurrency/Actor.swift | 25 ++++++ stdlib/public/Concurrency/CMakeLists.txt | 1 + test/decl/protocol/special/Actor.swift | 61 +++++++++++++ 18 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 lib/Sema/DerivedConformanceActor.cpp create mode 100644 stdlib/public/Concurrency/Actor.swift create mode 100644 test/decl/protocol/special/Actor.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 588ef350f497d..b615c7348e393 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4175,6 +4175,7 @@ enum class KnownDerivableProtocolKind : uint8_t { Decodable, AdditiveArithmetic, Differentiable, + Actor, }; /// ProtocolDecl - A declaration of a protocol, for example: diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e27bdb0847cd8..99225191ee057 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4195,6 +4195,13 @@ ERROR(actorisolated_not_actor_instance_member,none, "'@actorIsolated' can only be applied to instance members of actors", ()) +ERROR(partial_task_type_missing,none, + "missing 'PartialAsyncTask' type, probably because the '_Concurrency' " + "module was not imported", ()) +ERROR(enqueue_partial_task_not_in_context,none, + "'enqueue(partialTask:)' can only be implemented in the definition of " + "actor class %0", (Type)) + //------------------------------------------------------------------------------ // MARK: Type Check Types //------------------------------------------------------------------------------ diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 37898169a6707..f992f6b51dec0 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -44,6 +44,7 @@ IDENTIFIER(Change) IDENTIFIER_WITH_NAME(code_, "_code") IDENTIFIER(CodingKeys) IDENTIFIER(combine) +IDENTIFIER_(Concurrency) IDENTIFIER(container) IDENTIFIER(CoreGraphics) IDENTIFIER(CoreMedia) @@ -67,6 +68,7 @@ IDENTIFIER(encode) IDENTIFIER(encodeIfPresent) IDENTIFIER(Encoder) IDENTIFIER(encoder) +IDENTIFIER(enqueue) IDENTIFIER(erasing) IDENTIFIER(error) IDENTIFIER(errorDomain) @@ -105,6 +107,8 @@ IDENTIFIER(oldValue) IDENTIFIER(Optional) IDENTIFIER_(OptionalNilComparisonType) IDENTIFIER(parameter) +IDENTIFIER(partialTask) +IDENTIFIER(PartialAsyncTask) IDENTIFIER(projected) IDENTIFIER(projectedValue) IDENTIFIER(Protocol) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index e530e38c8bee6..1d4a8484b726c 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -58,6 +58,7 @@ #define BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(name) \ BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(name, "_" #name) +PROTOCOL(Actor) PROTOCOL(Sequence) PROTOCOL(IteratorProtocol) PROTOCOL(RawRepresentable) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4b8be5ff67243..f09ac7b231b5c 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -947,6 +947,9 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::Differentiable: M = getLoadedModule(Id_Differentiation); break; + case KnownProtocolKind::Actor: + M = getLoadedModule(Id_Concurrency); + break; default: M = getStdlibModule(); break; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..99b035da4e37a 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5047,7 +5047,8 @@ void ProtocolDecl::computeKnownProtocolKind() const { auto module = getModuleContext(); if (module != module->getASTContext().getStdlibModule() && !module->getName().is("Foundation") && - !module->getName().is("_Differentiation")) { + !module->getName().is("_Differentiation") && + !module->getName().is("_Concurrency")) { const_cast(this)->Bits.ProtocolDecl.KnownProtocol = 1; return; } @@ -5093,6 +5094,8 @@ Optional return KnownDerivableProtocolKind::AdditiveArithmetic; case KnownProtocolKind::Differentiable: return KnownDerivableProtocolKind::Differentiable; + case KnownProtocolKind::Actor: + return KnownDerivableProtocolKind::Actor; default: return None; } } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 5b2ff91752ca6..62b4791e29c88 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1256,6 +1256,12 @@ void NominalTypeDecl::prepareConformanceTable() const { addSynthesized(KnownProtocolKind::RawRepresentable); } } + + // Actor classes conform to the actor protocol. + if (auto classDecl = dyn_cast(mutableThis)) { + if (classDecl->isActor()) + addSynthesized(KnownProtocolKind::Actor); + } } bool NominalTypeDecl::lookupConformance( diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index d18859efc34f0..7dfa9a3bd3094 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5045,6 +5045,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::AdditiveArithmetic: case KnownProtocolKind::Differentiable: case KnownProtocolKind::FloatingPoint: + case KnownProtocolKind::Actor: return SpecialProtocol::None; } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index d35c849fb435f..6d83a9b33d58b 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_host_library(swiftSema STATIC ConstraintLocator.cpp ConstraintSystem.cpp DebuggerTestingTransform.cpp + DerivedConformanceActor.cpp DerivedConformanceAdditiveArithmetic.cpp DerivedConformanceCaseIterable.cpp DerivedConformanceCodable.cpp diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp new file mode 100644 index 0000000000000..dc9328867505e --- /dev/null +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -0,0 +1,104 @@ +//===--- DerivedConformanceEquatableHashable.cpp - Derived Equatable & co -===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements implicit derivation of the Actor protocol. +// +//===----------------------------------------------------------------------===// +#include "DerivedConformances.h" +#include "TypeChecker.h" +#include "swift/AST/ParameterList.h" + +using namespace swift; + +bool DerivedConformance::canDeriveActor( + NominalTypeDecl *nominal, DeclContext *dc) { + auto classDecl = dyn_cast(nominal); + return classDecl && classDecl->isActor() && dc == nominal; +} + +static DeclName getEnqueuePartialTaskName(ASTContext &ctx) { + return DeclName(ctx, ctx.Id_enqueue, { ctx.Id_partialTask }); +} + +static Type getPartialAsyncTaskType(ASTContext &ctx) { + auto concurrencyModule = ctx.getLoadedModule(ctx.Id_Concurrency); + if (!concurrencyModule) + return Type(); + + SmallVector decls; + concurrencyModule->lookupQualified( + concurrencyModule, DeclNameRef(ctx.Id_PartialAsyncTask), + NL_QualifiedDefault, decls); + for (auto decl : decls) { + if (auto typeDecl = dyn_cast(decl)) + return typeDecl->getDeclaredInterfaceType(); + } + + return Type(); +} + +static std::pair +deriveBodyActor_enqueuePartialTask( + AbstractFunctionDecl *enqueuePartialTask, void *) { + ASTContext &ctx = enqueuePartialTask->getASTContext(); + + // FIXME: Call into runtime API to enqueue the task, once we figure out + // what that runtime API should look like. + + auto body = BraceStmt::create( + ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; +} + +/// Derive the declaration of Actor's enqueue(partialTask:). +static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { + ASTContext &ctx = derived.Context; + + Type partialTaskType = getPartialAsyncTaskType(ctx); + if (!partialTaskType) { + derived.Nominal->diagnose(diag::partial_task_type_missing); + return nullptr; + } + + auto parentDC = derived.getConformanceContext(); + auto partialTaskParamDecl = new (ctx) ParamDecl( + SourceLoc(), SourceLoc(), ctx.Id_partialTask, + SourceLoc(), ctx.Id_partialTask, parentDC); + partialTaskParamDecl->setInterfaceType(partialTaskType); + partialTaskParamDecl->setSpecifier(ParamSpecifier::Default); + + ParameterList *params = ParameterList::createWithoutLoc(partialTaskParamDecl); + auto func = FuncDecl::createImplicit( + ctx, StaticSpellingKind::None, getEnqueuePartialTaskName(ctx), + SourceLoc(), /*Async=*/false, /*Throws=*/false, /*GenericParams=*/nullptr, + params, TupleType::getEmpty(ctx), parentDC); + func->copyFormalAccessFrom(derived.Nominal); + func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); + + // FIXME: This function should be "actor-unsafe", not "actor-independent", but + // the latter is all we have at the moment. + func->getAttrs().add(new (ctx) ActorIndependentAttr(/*IsImplicit=*/true)); + + derived.addMembersToConformanceContext({func}); + return func; +} + +ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { + auto func = dyn_cast(requirement); + if (!func) + return nullptr; + + if (func->getName() == getEnqueuePartialTaskName(Context)) + return deriveActor_enqueuePartialTask(*this); + + return nullptr; +} diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index b22a351eaf6be..f82cf58c39acb 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/Stmt.h" @@ -74,6 +75,10 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, return canDeriveHashable(Nominal); } + if (*derivableKind == KnownDerivableProtocolKind::Actor) { + return canDeriveActor(Nominal, DC); + } + if (*derivableKind == KnownDerivableProtocolKind::AdditiveArithmetic) return canDeriveAdditiveArithmetic(Nominal, DC); @@ -359,6 +364,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, return getRequirement(KnownProtocolKind::Hashable); } + // Actor.enqueue(partialTask: PartialTask) + if (isEnqueuePartialTask(ctx, name)) { + return getRequirement(KnownProtocolKind::Actor); + } + return nullptr; } diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index c5dba4bb20172..7deee41999f2f 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -18,15 +18,31 @@ #ifndef SWIFT_SEMA_DERIVEDCONFORMANCES_H #define SWIFT_SEMA_DERIVEDCONFORMANCES_H +#include "swift/Basic/LLVM.h" #include namespace swift { +class AbstractFunctionDecl; +class AccessorDecl; +class AssociatedTypeDecl; +class ASTContext; +struct ASTNode; class Decl; +class DeclContext; class DeclRefExpr; -class AccessorDecl; +class EnumDecl; +class EnumElementDecl; +class Expr; +class GuardStmt; +class Identifier; class NominalTypeDecl; +class ParamDecl; +class Pattern; class PatternBindingDecl; +class ProtocolDecl; +class StructDecl; class Type; +class TypeDecl; class ValueDecl; class VarDecl; @@ -277,6 +293,14 @@ class DerivedConformance { /// \returns the derived member, which will also be added to the type. ValueDecl *deriveDecodable(ValueDecl *requirement); + /// Whether we can derive the given Actor requirement in the given context. + static bool canDeriveActor(NominalTypeDecl *nominal, DeclContext *dc); + + /// Derive an Actor requirement for an actor class. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveActor(ValueDecl *requirement); + /// Declare a read-only property. std::pair declareDerivedProperty(Identifier name, Type propertyInterfaceType, diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index b5c19ab93c570..597354a2c3d00 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -749,3 +749,12 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) { ctx.evaluator, ActorIsolationRequest{value}, ActorIsolation::forUnspecified()); } + +bool swift::isEnqueuePartialTask(ASTContext &ctx, DeclName name) { + if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { + auto argumentNames = name.getArgumentNames(); + return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; + } + + return false; +} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index ea0f92a7ff3a6..b1ecd19fec9ed 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -20,8 +20,10 @@ namespace swift { class ActorIsolation; +class ASTContext; class ClassDecl; class DeclContext; +class DeclName; class Expr; class FuncDecl; class ValueDecl; @@ -36,6 +38,9 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc); /// Determine how the given value declaration is isolated. ActorIsolation getActorIsolation(ValueDecl *value); +/// Whether the given declaration name is enqueue(partialTask:). +bool isEnqueuePartialTask(ASTContext &ctx, DeclName name); + } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 08df65e1a80f6..0b37eeb01549e 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4216,8 +4216,18 @@ void ConformanceChecker::resolveValueWitnesses() { auto witness = Conformance->getWitnessUncached(requirement).getDecl(); if (!witness) return; - // Objective-C checking for @objc requirements. auto &C = witness->getASTContext(); + + // Ensure that Actor.enqueue(partialTask:) is implemented within the + // class itself. + if (isEnqueuePartialTask(C, requirement->getName()) && + Proto->isSpecificProtocol(KnownProtocolKind::Actor) && + DC != witness->getDeclContext()) { + witness->diagnose(diag::enqueue_partial_task_not_in_context, Adoptee); + return; + } + + // Objective-C checking for @objc requirements. if (requirement->isObjC() && requirement->getName() == witness->getName() && !requirement->getAttrs().isUnavailable(getASTContext())) { @@ -5804,6 +5814,9 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, case KnownDerivableProtocolKind::Differentiable: return derived.deriveDifferentiable(Requirement); + case KnownDerivableProtocolKind::Actor: + return derived.deriveActor(Requirement); + case KnownDerivableProtocolKind::OptionSet: llvm_unreachable( "When possible, OptionSet is derived via memberwise init synthesis"); diff --git a/stdlib/public/Concurrency/Actor.swift b/stdlib/public/Concurrency/Actor.swift new file mode 100644 index 0000000000000..8f62d14b02dd7 --- /dev/null +++ b/stdlib/public/Concurrency/Actor.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 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 +// +//===----------------------------------------------------------------------===// + +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +/// Common protocol to which all actor classes conform. +/// +/// The \c Actor protocol provides the core functionality of an actor class, +/// which involves enqueuing new partial tasks to be executed at some +/// point. Actor classes implicitly conform to this protocol as part of their +/// primary class definition. +public protocol Actor: AnyObject { + /// Enqueue a new partial task that will be executed in the actor's context. + func enqueue(partialTask: PartialAsyncTask) +} diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index fa491ec431295..e63b3e475255b 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -11,6 +11,7 @@ #===----------------------------------------------------------------------===# add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + Actor.swift PartialAsyncTask.swift SWIFT_MODULE_DEPENDS_OSX Darwin diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift new file mode 100644 index 0000000000000..5bf3c4fd9c6d3 --- /dev/null +++ b/test/decl/protocol/special/Actor.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency + +// Synthesis of for actor classes. +import _Concurrency + +actor class A1 { + var x: Int = 17 +} + +actor class A2: Actor { + var x: Int = 17 +} + +actor class A3: Actor { + var x: Int = 17 +} + +actor class A4: A1 { +} + +actor class A5: A2 { +} + +actor class A6: A1, Actor { // expected-error{{redundant conformance of 'A6' to protocol 'Actor'}} + // expected-note@-1{{'A6' inherits conformance to protocol 'Actor' from superclass here}} +} + +actor class A7 { + // Okay: satisfy the requirement explicitly + @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } +} + +// Bad actors, that incorrectly try to satisfy the various requirements. + +// Method that is not usable as a witness. +actor class BA1 { + func enqueue(partialTask: PartialAsyncTask) { } // expected-error{{invalid redeclaration of synthesized implementation for protocol requirement 'enqueue(partialTask:)'}} +} + +// Method that isn't part of the main class definition cannot be used to +// satisfy the requirement, because then it would not have a vtable slot. +actor class BA2 { } + +extension BA2 { + // expected-error@+1{{'enqueue(partialTask:)' can only be implemented in the definition of actor class 'BA2'}} + @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } +} + + +// Make sure the conformances actually happen. +func acceptActor(_: T.Type) { } + +func testConformance() { + acceptActor(A1.self) + acceptActor(A2.self) + acceptActor(A3.self) + acceptActor(A4.self) + acceptActor(A5.self) + acceptActor(A6.self) + acceptActor(A7.self) +} From 8f3c912f46dc6a2ee3734f741de50bdebf803ec0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Sep 2020 21:16:59 -0700 Subject: [PATCH 03/20] [Concurrency] Fix new source file header. --- lib/Sema/DerivedConformanceActor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index dc9328867505e..7299c18137fca 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -1,4 +1,4 @@ -//===--- DerivedConformanceEquatableHashable.cpp - Derived Equatable & co -===// +//===--- DerivedConformanceActor.cpp - Derived Actor Conformance ----------===// // // This source file is part of the Swift.org open source project // From c2b8565a107920f67ed9074fd4d28d3d7a190fa4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 13:24:34 -0700 Subject: [PATCH 04/20] [Concurrency] Implicitly synthesize actor queue storage and enqueue. When an actor class has its `enqueue(partialTask:)` implicitly synthesized, also synthesize a stored property for the actor's queue. The type of the property is defined by the _Concurrency library (`_DefaultActorQueue`), and it will be initialized with a call to `_defaultActorQueueCreate` (also provided by the _Concurrency library). Also synthesize the body of the implicitly-generated `enqueue(partialTask:)`, which will be a call to `_defaultActorQueueEnqueuePartialTask(actor:queue:partialTask:)`. Together, all of these allow us to experiment with the form of the queue and the queue operation without affecting the type checker. When `enqueue(partialTask:)` is not implicitly synthesized, the queue storage is not synthesized either. In such cases, the user has taken over the execution of tasks for the actor, rather than using the default implementation. --- include/swift/AST/DiagnosticsSema.def | 6 +- include/swift/AST/KnownIdentifiers.def | 1 + lib/Sema/DerivedConformanceActor.cpp | 202 +++++++++++++++++- lib/Sema/TypeCheckStorage.cpp | 26 ++- stdlib/public/Concurrency/Actor.swift | 30 +++ .../synthesized_conformance_actor.swift | 41 ++++ test/decl/protocol/special/Actor.swift | 11 + 7 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 test/SILGen/synthesized_conformance_actor.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 99225191ee057..f353c230f813d 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4195,9 +4195,9 @@ ERROR(actorisolated_not_actor_instance_member,none, "'@actorIsolated' can only be applied to instance members of actors", ()) -ERROR(partial_task_type_missing,none, - "missing 'PartialAsyncTask' type, probably because the '_Concurrency' " - "module was not imported", ()) +ERROR(concurrency_lib_missing,none, + "missing '%0' declaration, probably because the '_Concurrency' " + "module was not imported", (StringRef)) ERROR(enqueue_partial_task_not_in_context,none, "'enqueue(partialTask:)' can only be implemented in the definition of " "actor class %0", (Type)) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index f992f6b51dec0..a9c4d53248615 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -141,6 +141,7 @@ IDENTIFIER(withKeywordArguments) IDENTIFIER(wrapped) IDENTIFIER(wrappedValue) IDENTIFIER(wrapperValue) +IDENTIFIER_WITH_NAME(actorStorage, "$__actor_storage") // Kinds of layout constraints IDENTIFIER_WITH_NAME(UnknownLayout, "_UnknownLayout") diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 7299c18137fca..92f2e2f89021c 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -15,6 +15,8 @@ //===----------------------------------------------------------------------===// #include "DerivedConformances.h" #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" +#include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" using namespace swift; @@ -46,16 +48,146 @@ static Type getPartialAsyncTaskType(ASTContext &ctx) { return Type(); } +/// Look for the default actor queue type. +static Type getDefaultActorQueueType(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + UnqualifiedLookupOptions options; + options |= UnqualifiedLookupFlags::TypeLookup; + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_DefaultActorQueue")), dc, loc, options); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + if (auto typeDecl = dyn_cast(result.getValueDecl())) + return typeDecl->getDeclaredInterfaceType(); + } + + return Type(); +} + +/// Look for the initialization function for the default actor storage. +static FuncDecl *getDefaultActorQueueCreate(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_defaultActorQueueCreate")), dc, loc, + UnqualifiedLookupOptions()); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + // FIXME: Validate this further, because we're assuming the exact type. + if (auto func = dyn_cast(result.getValueDecl())) + return func; + } + + return nullptr; +} + +/// Look for the default enqueue operation. +static FuncDecl *getDefaultActorQueueEnqueue(DeclContext *dc, SourceLoc loc) { + ASTContext &ctx = dc->getASTContext(); + auto desc = UnqualifiedLookupDescriptor( + DeclNameRef(ctx.getIdentifier("_defaultActorQueueEnqueuePartialTask")), + dc, loc, UnqualifiedLookupOptions()); + auto lookup = + evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); + for (const auto &result : lookup) { + // FIXME: Validate this further, because we're assuming the exact type. + if (auto func = dyn_cast(result.getValueDecl())) + return func; + } + + return nullptr; +} + static std::pair deriveBodyActor_enqueuePartialTask( AbstractFunctionDecl *enqueuePartialTask, void *) { + // func enqueue(partialTask: PartialAsyncTask) { + // _defaultActorQueueEnqueuePartialTask( + // actor: self, queue: &self.$__actor_storage, partialTask: partialTask) + // } ASTContext &ctx = enqueuePartialTask->getASTContext(); - // FIXME: Call into runtime API to enqueue the task, once we figure out - // what that runtime API should look like. + // Dig out the $__actor_storage property. + auto classDecl = enqueuePartialTask->getDeclContext()->getSelfClassDecl(); + VarDecl *storageVar = nullptr; + for (auto decl : classDecl->lookupDirect(ctx.Id_actorStorage)) { + storageVar = dyn_cast(decl); + if (storageVar) + break; + } + + // Produce an empty brace statement on failure. + auto failure = [&]() -> std::pair { + auto body = BraceStmt::create( + ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + return { body, /*isTypeChecked=*/true }; + }; + + if (!storageVar) { + classDecl->diagnose( + diag::concurrency_lib_missing, ctx.Id_actorStorage.str()); + return failure(); + } + + // Call into the runtime to enqueue the task. + auto fn = getDefaultActorQueueEnqueue(classDecl, classDecl->getLoc()); + if (!fn) { + classDecl->diagnose( + diag::concurrency_lib_missing, "_defaultActorQueueEnqueuePartialTask"); + return failure(); + } + + // Reference to _defaultActorQueueEnqueuePartialTask. + auto fnRef = new (ctx) DeclRefExpr(fn, DeclNameLoc(), /*Implicit=*/true); + fnRef->setType(fn->getInterfaceType()); + + // self argument to the function. + auto selfDecl = enqueuePartialTask->getImplicitSelfDecl(); + Type selfType = enqueuePartialTask->mapTypeIntoContext( + selfDecl->getValueInterfaceType()); + Expr *selfArg = new (ctx) DeclRefExpr( + selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, + selfType); + selfArg = ErasureExpr::create(ctx, selfArg, ctx.getAnyObjectType(), { }); + selfArg->setImplicit(); + + // Address of the actor storage. + auto module = classDecl->getModuleContext(); + Expr *selfBase = new (ctx) DeclRefExpr( + selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, + selfType); + SubstitutionMap storageVarSubs = classDecl->getDeclaredTypeInContext() + ->getMemberSubstitutionMap(module, storageVar); + ConcreteDeclRef storageVarDeclRef(storageVar, storageVarSubs); + Type storageVarType = classDecl->mapTypeIntoContext( + storageVar->getValueInterfaceType()); + Type storageVarRefType = LValueType::get(storageVarType); + Expr *storageVarRefExpr = new (ctx) MemberRefExpr( + selfBase, SourceLoc(), storageVarDeclRef, DeclNameLoc(), + /*Implicit=*/true); + storageVarRefExpr->setType(storageVarRefType); + storageVarRefExpr = new (ctx) InOutExpr( + SourceLoc(), storageVarRefExpr, storageVarType, /*isImplicit=*/true); + + // The partial asynchronous task. + auto partialTaskParam = enqueuePartialTask->getParameters()->get(0); + Expr *partialTask = new (ctx) DeclRefExpr( + partialTaskParam, DeclNameLoc(), /*Implicit=*/true, + AccessSemantics::Ordinary, + enqueuePartialTask->mapTypeIntoContext( + partialTaskParam->getValueInterfaceType())); + + // Form the call itself. + auto call = CallExpr::createImplicit( + ctx, fnRef, { selfArg, storageVarRefExpr, partialTask }, + { ctx.getIdentifier("actor"), ctx.getIdentifier("queue"), + ctx.Id_partialTask }); + call->setType(fn->getResultInterfaceType()); + call->setThrows(false); auto body = BraceStmt::create( - ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); + ctx, SourceLoc(), { call }, SourceLoc(), /*implicit=*/true); return { body, /*isTypeChecked=*/true }; } @@ -63,19 +195,39 @@ deriveBodyActor_enqueuePartialTask( static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { ASTContext &ctx = derived.Context; + // Retrieve the types and declarations we'll need to form this operation. Type partialTaskType = getPartialAsyncTaskType(ctx); if (!partialTaskType) { - derived.Nominal->diagnose(diag::partial_task_type_missing); + derived.Nominal->diagnose( + diag::concurrency_lib_missing, ctx.Id_PartialAsyncTask.str()); return nullptr; } auto parentDC = derived.getConformanceContext(); + Type defaultActorQueueType = getDefaultActorQueueType( + parentDC, derived.ConformanceDecl->getLoc()); + if (!defaultActorQueueType) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, "_DefaultActorQueue"); + return nullptr; + } + + auto actorStorageCreateFn = getDefaultActorQueueCreate( + parentDC, derived.ConformanceDecl->getLoc()); + if (!actorStorageCreateFn) { + derived.Nominal->diagnose( + diag::concurrency_lib_missing, "_defaultActorQueueCreate"); + return nullptr; + } + + // Partial task parameter to enqueue(partialTask:). auto partialTaskParamDecl = new (ctx) ParamDecl( SourceLoc(), SourceLoc(), ctx.Id_partialTask, SourceLoc(), ctx.Id_partialTask, parentDC); partialTaskParamDecl->setInterfaceType(partialTaskType); partialTaskParamDecl->setSpecifier(ParamSpecifier::Default); + // enqueue(partialTask:) method. ParameterList *params = ParameterList::createWithoutLoc(partialTaskParamDecl); auto func = FuncDecl::createImplicit( ctx, StaticSpellingKind::None, getEnqueuePartialTaskName(ctx), @@ -83,12 +235,50 @@ static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { params, TupleType::getEmpty(ctx), parentDC); func->copyFormalAccessFrom(derived.Nominal); func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); + func->setSynthesized(); // FIXME: This function should be "actor-unsafe", not "actor-independent", but // the latter is all we have at the moment. func->getAttrs().add(new (ctx) ActorIndependentAttr(/*IsImplicit=*/true)); - derived.addMembersToConformanceContext({func}); + // Actor storage property and its initialization. + auto actorStorage = new (ctx) VarDecl( + /*isStatic=*/false, VarDecl::Introducer::Var, SourceLoc(), + ctx.Id_actorStorage, parentDC); + actorStorage->setInterfaceType(defaultActorQueueType); + actorStorage->setImplicit(); + actorStorage->setAccess(AccessLevel::Private); + actorStorage->getAttrs().add(new (ctx) FinalAttr(/*Implicit=*/true)); + + // Pattern binding to initialize the actor storage. + Pattern *actorStoragePattern = NamedPattern::createImplicit( + ctx, actorStorage); + actorStoragePattern = TypedPattern::createImplicit( + ctx, actorStoragePattern, defaultActorQueueType); + + // Initialization expression. + // FIXME: We want the equivalent of type(of: self) here, but we cannot refer + // to self, so for now we use the static type instead. + Type nominalType = derived.Nominal->getDeclaredTypeInContext(); + Expr *metatypeArg = TypeExpr::createImplicit(nominalType, ctx); + Type anyObjectMetatype = ExistentialMetatypeType::get(ctx.getAnyObjectType()); + metatypeArg = ErasureExpr::create(ctx, metatypeArg, anyObjectMetatype, { }); + Expr *actorStorageCreateFnRef = new (ctx) DeclRefExpr( + actorStorageCreateFn, DeclNameLoc(), /*Implicit=*/true); + actorStorageCreateFnRef->setType(actorStorageCreateFn->getInterfaceType()); + + auto actorStorageInit = CallExpr::createImplicit( + ctx, actorStorageCreateFnRef, { metatypeArg}, { Identifier() }); + actorStorageInit->setType(actorStorageCreateFn->getResultInterfaceType()); + actorStorageInit->setThrows(false); + + auto actorStoragePatternBinding = PatternBindingDecl::createImplicit( + ctx, StaticSpellingKind::None, actorStoragePattern, actorStorageInit, + parentDC); + actorStoragePatternBinding->setInitializerChecked(0); + + derived.addMembersToConformanceContext( + { func, actorStorage, actorStoragePatternBinding }); return func; } @@ -97,7 +287,7 @@ ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { if (!func) return nullptr; - if (func->getName() == getEnqueuePartialTaskName(Context)) + if (isEnqueuePartialTask(Context, func->getName())) return deriveActor_enqueuePartialTask(*this); return nullptr; diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 69525ed1881e7..1f550ab32c01d 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -30,6 +30,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" #include "swift/AST/PropertyWrappers.h" +#include "swift/AST/ProtocolConformance.h" #include "swift/AST/SourceFile.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" @@ -113,6 +114,21 @@ static void computeLoweredStoredProperties(NominalTypeDecl *decl) { if (var->hasAttachedPropertyWrapper()) (void) var->getPropertyWrapperBackingProperty(); } + + // If this is an actor class, check conformance to the Actor protocol to + // ensure that the actor storage will get created (if needed). + if (auto classDecl = dyn_cast(decl)) { + if (classDecl->isActor()) { + ASTContext &ctx = decl->getASTContext(); + if (auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor)) { + SmallVector conformances; + classDecl->lookupConformance( + decl->getModuleContext(), actorProto, conformances); + for (auto conformance : conformances) + TypeChecker::checkConformance(conformance->getRootNormalConformance()); + } + } + } } ArrayRef @@ -128,10 +144,16 @@ StoredPropertiesRequest::evaluate(Evaluator &evaluator, if (isa(decl->getModuleScopeContext())) computeLoweredStoredProperties(decl); + ASTContext &ctx = decl->getASTContext(); for (auto *member : decl->getMembers()) { if (auto *var = dyn_cast(member)) - if (!var->isStatic() && var->hasStorage()) - results.push_back(var); + if (!var->isStatic() && var->hasStorage()) { + // Actor storage always goes at the beginning. + if (var->getName() == ctx.Id_actorStorage) + results.insert(results.begin(), var); + else + results.push_back(var); + } } return decl->getASTContext().AllocateCopy(results); diff --git a/stdlib/public/Concurrency/Actor.swift b/stdlib/public/Concurrency/Actor.swift index 8f62d14b02dd7..212651ecfb6b3 100644 --- a/stdlib/public/Concurrency/Actor.swift +++ b/stdlib/public/Concurrency/Actor.swift @@ -23,3 +23,33 @@ public protocol Actor: AnyObject { /// Enqueue a new partial task that will be executed in the actor's context. func enqueue(partialTask: PartialAsyncTask) } + +/// A native actor queue, which schedules partial tasks onto a serial queue. +public struct _NativeActorQueue { + // TODO: This is just a stub for now +} + +/// The default type to be used for an actor's queue when an actor does not +/// provide its own implementation of `enqueue(partialTask:)`. +public typealias _DefaultActorQueue = _NativeActorQueue + +/// Called to create a new default actor queue instance for a class of the given +/// type. The implementation will call this within the actor's initializer to +/// initialize the actor queue. +public func _defaultActorQueueCreate( + _ actorClass: AnyObject.Type +) -> _DefaultActorQueue { + _DefaultActorQueue() +} + +/// Called by the synthesized implementation of enqueue(partialTask:). +/// +/// The implementation is provided with the address of the synthesized instance +/// property for the actor queue, so that it need not be at a fixed offset. +public func _defaultActorQueueEnqueuePartialTask( + actor: AnyObject, + queue: inout _DefaultActorQueue, + partialTask: PartialAsyncTask +) { + // TODO: Implement queueing. +} diff --git a/test/SILGen/synthesized_conformance_actor.swift b/test/SILGen/synthesized_conformance_actor.swift new file mode 100644 index 0000000000000..fe0c141e8bdab --- /dev/null +++ b/test/SILGen/synthesized_conformance_actor.swift @@ -0,0 +1,41 @@ +// RUN: %target-swift-frontend -emit-silgen %s -swift-version 5 -enable-experimental-concurrency | %FileCheck -check-prefix CHECK %s +// REQUIRES: concurrency + +import _Concurrency + +public protocol DefaultInit { + init() +} + +public actor class A1 { + var x: Int = 17 + var y: T = T() + + public func f() { } +} + +extension Int: DefaultInit { } + +func buildIt() { + _ = A1() +} + +// variable initialization expression of A1.$__actor_storage +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33_550E67F1F00BFF89F882603E5B70A41BLL12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) () -> @out _NativeActorQueue { +// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue): +// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1.Type +// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1.Type, $@thick AnyObject.Type +// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue +// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue + +// A1.enqueue(partialTask:) +// CHECK-LABEL: sil hidden [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) (@in_guaranteed PartialAsyncTask, @guaranteed A1) -> () { +// CHECK: bb0([[PARTIAL_TASK:%.*]] : $*PartialAsyncTask, [[SELF:%.*]] : @guaranteed $A1): +// CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $A1 +// CHECK-NEXT: [[SELF_ANY_OBJECT:%.*]] = init_existential_ref [[SELF_COPY]] : $A1 : $A1, $AnyObject +// CHECK-NEXT: [[PROPERTY_REF:%.*]] = ref_element_addr [[SELF]] : $A1, #A1.$__actor_storage +// FIXME: Need to eliminate this exclusivity check. +// CHECK-NEXT: [[DYNAMIC_ACCESS:%.*]] = begin_access [modify] [dynamic] [[PROPERTY_REF]] : $*_NativeActorQueue +// CHECK: [[ENQUEUE_FN:%.*]] = function_ref @$s12_Concurrency36_defaultActorQueueEnqueuePartialTask5actor5queue07partialG0yyXl_AA07_NativecD0VzAA0f5AsyncG0VtF : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () +// CHECK-NEXT: apply [[ENQUEUE_FN]]([[SELF_ANY_OBJECT]], [[DYNAMIC_ACCESS]], [[PARTIAL_TASK]]) : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () +// CHECK-NEXT: end_access [[DYNAMIC_ACCESS]] : $*_NativeActorQueue diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index 5bf3c4fd9c6d3..76c8f83b8a6ab 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift -enable-experimental-concurrency +// REQUIRES: concurrency // Synthesis of for actor classes. import _Concurrency @@ -25,11 +26,18 @@ actor class A6: A1, Actor { // expected-error{{redundant conformance of 'A6' to // expected-note@-1{{'A6' inherits conformance to protocol 'Actor' from superclass here}} } +// Explicitly satisfying the requirement. + actor class A7 { // Okay: satisfy the requirement explicitly @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } } +// A non-actor class can conform to the Actor protocol, if it does it properly. +class C1: Actor { + func enqueue(partialTask: PartialAsyncTask) { } +} + // Bad actors, that incorrectly try to satisfy the various requirements. // Method that is not usable as a witness. @@ -46,6 +54,9 @@ extension BA2 { @actorIndependent func enqueue(partialTask: PartialAsyncTask) { } } +// No synthesis for non-actor classes. +class C2: Actor { // expected-error{{type 'C2' does not conform to protocol 'Actor'}} +} // Make sure the conformances actually happen. func acceptActor(_: T.Type) { } From d1f43032fcf0b8738e6b05bd7de307d9af0e261c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 29 Sep 2020 15:29:55 -0500 Subject: [PATCH 05/20] [ownership] Move ownership passed TempLValueOpt for the stdlib and add an ossa test case. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 6 +- lib/SILOptimizer/Transforms/TempLValueOpt.cpp | 5 +- test/SILOptimizer/templvalueopt_ossa.sil | 251 ++++++++++++++++++ 3 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 test/SILOptimizer/templvalueopt_ossa.sil diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index f93042ad18bcb..8f6fcc0a52e8b 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -283,13 +283,13 @@ void addFunctionPasses(SILPassPipelinePlan &P, // splits up copy_addr. P.addCopyForwarding(); + // Optimize copies from a temporary (an "l-value") to a destination. + P.addTempLValueOpt(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now // handle the stdlib functions. P.addNonTransparentFunctionOwnershipModelEliminator(); - // Optimize copies from a temporary (an "l-value") to a destination. - P.addTempLValueOpt(); - // Split up opaque operations (copy_addr, retain_value, etc.). P.addLowerAggregateInstrs(); diff --git a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp index 8511de4452e75..36303388a2a02 100644 --- a/lib/SILOptimizer/Transforms/TempLValueOpt.cpp +++ b/lib/SILOptimizer/Transforms/TempLValueOpt.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "cow-opts" + #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SIL/SILFunction.h" @@ -105,8 +106,8 @@ void TempLValueOptPass::run() { } // Do the optimizations. for (CopyAddrInst *copyInst : copyInsts) { - changed |=combineCopyAndDestroy(copyInst); - changed |=tempLValueOpt(copyInst); + changed |= combineCopyAndDestroy(copyInst); + changed |= tempLValueOpt(copyInst); } } diff --git a/test/SILOptimizer/templvalueopt_ossa.sil b/test/SILOptimizer/templvalueopt_ossa.sil new file mode 100644 index 0000000000000..9e0a5562c5103 --- /dev/null +++ b/test/SILOptimizer/templvalueopt_ossa.sil @@ -0,0 +1,251 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -temp-lvalue-opt | %FileCheck %s + +import Swift +import Builtin + +// CHECK-LABEL: sil [ossa] @test_enum_with_initialize +// CHECK: bb0(%0 : $*Optional, %1 : $*Any): +// CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] +// CHECK-NEXT: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_enum_with_initialize' +sil [ossa] @test_enum_with_initialize : $@convention(thin) (@in Any) -> @out Optional { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to [initialization] %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @test_enum_without_initialize +// CHECK: bb0(%0 : $*Optional, %1 : $*Any): +// CHECK-NEXT: destroy_addr %0 +// CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] +// CHECK-NEXT: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_enum_without_initialize' +sil [ossa] @test_enum_without_initialize : $@convention(thin) (@inout Optional, @in Any) -> () { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +protocol Proto {} + +struct ConformingStruct : Proto { + @_hasStorage let a: Any +} + +// CHECK-LABEL: sil [ossa] @test_existential +// CHECK: bb0(%0 : $*Proto, %1 : $*ConformingStruct): +// CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_existential' +sil [ossa] @test_existential : $@convention(thin) (@in ConformingStruct) -> @out Proto { +bb0(%0 : $*Proto, %1 : $*ConformingStruct): + %2 = alloc_stack $Proto + %3 = init_existential_addr %2 : $*Proto, $ConformingStruct + copy_addr [take] %1 to [initialization] %3 : $*ConformingStruct + copy_addr [take] %2 to [initialization] %0 : $*Proto + dealloc_stack %2 : $*Proto + %6 = tuple () + return %6 : $() +} + +enum Either { + case left(Left), right(Right) +} + +public struct TestStruct { + @_hasStorage var e: Either +} + +struct AddressOnlyPayload { + @_hasStorage let a: Any + @_hasStorage let i: Int +} + +// There should only be a single copy_addr after optimization. +// +// CHECK-LABEL: sil [ossa] @test_initialize_struct +// CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): +// CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 +// CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] +// CHECK-NEXT: [[A:%[0-9]+]] = struct_element_addr [[LEFT]] +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[A]] +// CHECK-NEXT: [[I:%[0-9]+]] = struct_element_addr [[LEFT]] +// CHECK-NEXT: store %2 to [trivial] [[I]] +// CHECK-NEXT: inject_enum_addr [[E]] +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'test_initialize_struct' +sil [ossa] @test_initialize_struct : $@convention(method) (@in Any, Int) -> @out TestStruct { +bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): + %3 = alloc_stack $TestStruct + %4 = alloc_stack $Either + %5 = alloc_stack $AddressOnlyPayload + %6 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.a + copy_addr [take] %1 to [initialization] %6 : $*Any + %8 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.i + store %2 to [trivial] %8 : $*Int + %10 = init_enum_data_addr %4 : $*Either, #Either.left!enumelt + copy_addr [take] %5 to [initialization] %10 : $*AddressOnlyPayload + inject_enum_addr %4 : $*Either, #Either.left!enumelt + dealloc_stack %5 : $*AddressOnlyPayload + %14 = struct_element_addr %3 : $*TestStruct, #TestStruct.e + copy_addr [take] %4 to [initialization] %14 : $*Either + dealloc_stack %4 : $*Either + copy_addr %3 to [initialization] %0 : $*TestStruct + destroy_addr %3 : $*TestStruct + dealloc_stack %3 : $*TestStruct + %20 = tuple () + return %20 : $() +} + +// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: } // end sil function 'bail_on_write_to_dest' +sil [ossa] @bail_on_write_to_dest : $@convention(thin) (@inout Optional, @in Any) -> () { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + destroy_addr %0 : $*Optional + copy_addr [take] %2 to [initialization] %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liferange +// CHECK: bb0(%0 : $*Optional, %1 : $*Any): +// CHECK-NEXT: destroy_addr +// CHECK-NEXT: init_enum_data_addr +// CHECK-NEXT: copy_addr +// CHECK-NEXT: inject_enum_addr +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'write_to_dest_ok_if_before_liferange' +sil [ossa] @write_to_dest_ok_if_before_liferange : $@convention(thin) (@inout Optional, @in Any) -> () { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + destroy_addr %0 : $*Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to [initialization] %0 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +enum Enum { + case A(Optional), B +} + +struct StructWithEnum : Proto { + @_hasStorage let e: Enum +} + +// CHECK-LABEL: sil [ossa] @move_projections +// CHECK: bb0(%0 : $*Proto, %1 : $*Any): +// CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*Proto, $StructWithEnum +// CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e +// CHECK-NEXT: [[ENUMA:%[0-9]+]] = init_enum_data_addr [[E]] : $*Enum, #Enum.A!enumelt +// CHECK-NEXT: [[OPTIONAL:%[0-9]+]] = init_enum_data_addr [[ENUMA]] : $*Optional, #Optional.some!enumelt +// CHECK-NEXT: copy_addr [take] %1 to [initialization] [[OPTIONAL]] : $*Any +// CHECK-NEXT: inject_enum_addr [[ENUMA]] : $*Optional, #Optional.some!enumelt +// CHECK-NEXT: inject_enum_addr [[E]] : $*Enum, #Enum.A!enumelt +// CHECK-NOT: copy_addr +// CHECK: } // end sil function 'move_projections' +sil [ossa] @move_projections : $@convention(thin) (@in Any) -> @out Proto { +bb0(%0 : $*Proto, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + %4 = init_existential_addr %0 : $*Proto, $StructWithEnum + %5 = struct_element_addr %4 : $*StructWithEnum, #StructWithEnum.e + %6 = init_enum_data_addr %5 : $*Enum, #Enum.A!enumelt + copy_addr [take] %2 to [initialization] %6 : $*Optional + inject_enum_addr %5 : $*Enum, #Enum.A!enumelt + dealloc_stack %2 : $*Optional + %10 = tuple () + return %10 : $() +} + +// CHECK-LABEL: sil [ossa] @cant_move_projections +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: load +// CHECK: copy_addr +// CHECK: } // end sil function 'cant_move_projections' +sil [ossa] @cant_move_projections : $@convention(thin) (@in Any, @in_guaranteed Builtin.RawPointer) -> () { +bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %0 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + %4 = load [trivial] %1 : $*Builtin.RawPointer + %5 = pointer_to_address %4 : $Builtin.RawPointer to $*Optional + copy_addr [take] %2 to [initialization] %5 : $*Optional + dealloc_stack %2 : $*Optional + %10 = tuple () + return %10 : $() +} + +sil [ossa] @init_optional : $@convention(thin) () -> @out Optional + +// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: apply +// CHECK: } // end sil function 'instructions_after_copy_addr' +sil [ossa] @instructions_after_copy_addr : $@convention(thin) (@in Any) -> @out Optional { +bb0(%0 : $*Optional, %1 : $*Any): + %2 = alloc_stack $Optional + %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %1 to [initialization] %3 : $*Any + inject_enum_addr %2 : $*Optional, #Optional.some!enumelt + copy_addr [take] %2 to [initialization] %0 : $*Optional + %4 = function_ref @init_optional : $@convention(thin) () -> @out Optional + %5 = apply %4(%2) : $@convention(thin) () -> @out Optional + destroy_addr %2 : $*Optional + dealloc_stack %2 : $*Optional + %6 = tuple () + return %6 : $() +} + +// CHECK-LABEL: sil [ossa] @dont_optimize_swap +// CHECK: alloc_stack +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: copy_addr +// CHECK: dealloc_stack +// CHECK: } // end sil function 'dont_optimize_swap' +sil [ossa] @dont_optimize_swap : $@convention(thin) (@inout T, @inout T) -> () { +bb0(%0 : $*T, %1 : $*T): + %2 = alloc_stack $T + copy_addr [take] %0 to [initialization] %2 : $*T + copy_addr [take] %1 to [initialization] %0 : $*T + copy_addr [take] %2 to [initialization] %1 : $*T + dealloc_stack %2 : $*T + %78 = tuple () + return %78 : $() +} + From aeef419de894d646132ce1a11fca61c8d8d9e536 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 15:25:49 -0700 Subject: [PATCH 06/20] [Concurrency] Ensure that enqueue(partialTask:) is first in actor vtables. Actor classes never have non-actor superclasses, so we can ensure that all actor classes have a common vtable prefix for the `enqueue(partialTask:)` operation. This allows us to treat all actor classes uniformly, without having to go through the Actor witness table every time. --- include/swift/AST/Decl.h | 8 +++ lib/AST/Decl.cpp | 50 +++++++++++++++++++ lib/Sema/DerivedConformanceActor.cpp | 2 +- lib/Sema/DerivedConformances.cpp | 2 +- lib/Sema/TypeCheckConcurrency.cpp | 9 ---- lib/Sema/TypeCheckConcurrency.h | 4 -- lib/Sema/TypeCheckDecl.cpp | 9 ++++ lib/Sema/TypeCheckProtocol.cpp | 8 +-- .../synthesized_conformance_actor.swift | 30 +++++++---- 9 files changed, 95 insertions(+), 27 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b615c7348e393..c2b01e73e78cd 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -6329,6 +6329,14 @@ class FuncDecl : public AbstractFunctionDecl { bool isMainTypeMainMethod() const; + /// Whether the given name is enqueue(partialTask:), which is used for + /// actors. + static bool isEnqueuePartialTaskName(ASTContext &ctx, DeclName name); + + /// Determine whether this function is the witness to the Actor protocol's + /// enqueue(partialTask:) operation within an actor. + bool isActorEnqueuePartialTaskWitness() const; + SelfAccessKind getSelfAccessKind() const; void setSelfAccessKind(SelfAccessKind mod) { diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 99b035da4e37a..8cabb3003ce76 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7508,6 +7508,56 @@ bool FuncDecl::isMainTypeMainMethod() const { getParameters()->size() == 0; } +bool FuncDecl::isEnqueuePartialTaskName(ASTContext &ctx, DeclName name) { + if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { + auto argumentNames = name.getArgumentNames(); + return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; + } + + return false; +} + +bool FuncDecl::isActorEnqueuePartialTaskWitness() const { + if (!isEnqueuePartialTaskName(getASTContext(), getName())) + return false; + + auto classDecl = getDeclContext()->getSelfClassDecl(); + if (!classDecl) + return false; + + if (!classDecl->isActor()) + return false; + + ASTContext &ctx = getASTContext(); + auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor); + if (!actorProto) + return false; + + FuncDecl *requirement = nullptr; + for (auto protoMember : actorProto->getParsedMembers()) { + if (auto protoFunc = dyn_cast(protoMember)) { + if (isEnqueuePartialTaskName(ctx, protoFunc->getName())) { + requirement = protoFunc; + break; + } + } + } + + if (!requirement) + return false; + + SmallVector conformances; + classDecl->lookupConformance( + classDecl->getModuleContext(), actorProto, conformances); + for (auto conformance : conformances) { + auto witness = conformance->getWitnessDecl(requirement); + if (witness == this) + return true; + } + + return false; +} + ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc, bool Failable, SourceLoc FailabilityLoc, bool Throws, diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 92f2e2f89021c..27b26bfecc0d9 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -287,7 +287,7 @@ ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { if (!func) return nullptr; - if (isEnqueuePartialTask(Context, func->getName())) + if (FuncDecl::isEnqueuePartialTaskName(Context, func->getName())) return deriveActor_enqueuePartialTask(*this); return nullptr; diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index f82cf58c39acb..c4dd45728b060 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -365,7 +365,7 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, } // Actor.enqueue(partialTask: PartialTask) - if (isEnqueuePartialTask(ctx, name)) { + if (FuncDecl::isEnqueuePartialTaskName(ctx, name)) { return getRequirement(KnownProtocolKind::Actor); } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 597354a2c3d00..b5c19ab93c570 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -749,12 +749,3 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) { ctx.evaluator, ActorIsolationRequest{value}, ActorIsolation::forUnspecified()); } - -bool swift::isEnqueuePartialTask(ASTContext &ctx, DeclName name) { - if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) { - auto argumentNames = name.getArgumentNames(); - return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask; - } - - return false; -} diff --git a/lib/Sema/TypeCheckConcurrency.h b/lib/Sema/TypeCheckConcurrency.h index b1ecd19fec9ed..b2ec4f05784ec 100644 --- a/lib/Sema/TypeCheckConcurrency.h +++ b/lib/Sema/TypeCheckConcurrency.h @@ -23,7 +23,6 @@ class ActorIsolation; class ASTContext; class ClassDecl; class DeclContext; -class DeclName; class Expr; class FuncDecl; class ValueDecl; @@ -38,9 +37,6 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc); /// Determine how the given value declaration is isolated. ActorIsolation getActorIsolation(ValueDecl *value); -/// Whether the given declaration name is enqueue(partialTask:). -bool isEnqueuePartialTask(ASTContext &ctx, DeclName name); - } // end namespace swift #endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */ diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 3a1f7aaf921af..dfa0aeaa3a639 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2704,6 +2704,15 @@ SemanticMembersRequest::evaluate(Evaluator &evaluator, for (auto *member : idc->getMembers()) { if (auto *afd = dyn_cast(member)) { + // If this is a witness to Actor.enqueue(partialTask:), put it at the + // beginning of the vtable. + if (auto func = dyn_cast(afd)) { + if (func->isActorEnqueuePartialTaskWitness()) { + result.insert(result.begin(), func); + continue; + } + } + // Add synthesized members to a side table and sort them by their mangled // name, since they could have been added to the class in any order. if (afd->isSynthesized()) { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 0b37eeb01549e..dc0c1c182f14a 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -4219,10 +4219,12 @@ void ConformanceChecker::resolveValueWitnesses() { auto &C = witness->getASTContext(); // Ensure that Actor.enqueue(partialTask:) is implemented within the - // class itself. - if (isEnqueuePartialTask(C, requirement->getName()) && + // actor class itself. + if (FuncDecl::isEnqueuePartialTaskName(C, requirement->getName()) && Proto->isSpecificProtocol(KnownProtocolKind::Actor) && - DC != witness->getDeclContext()) { + DC != witness->getDeclContext() && + Adoptee->getClassOrBoundGenericClass() && + Adoptee->getClassOrBoundGenericClass()->isActor()) { witness->diagnose(diag::enqueue_partial_task_not_in_context, Adoptee); return; } diff --git a/test/SILGen/synthesized_conformance_actor.swift b/test/SILGen/synthesized_conformance_actor.swift index fe0c141e8bdab..365b3fee56e92 100644 --- a/test/SILGen/synthesized_conformance_actor.swift +++ b/test/SILGen/synthesized_conformance_actor.swift @@ -16,20 +16,17 @@ public actor class A1 { extension Int: DefaultInit { } +public actor class A2 { + func f() { } + @actorIndependent public func enqueue(partialTask: PartialAsyncTask) { } +} + func buildIt() { _ = A1() } -// variable initialization expression of A1.$__actor_storage -// CHECK-LABEL: sil hidden [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33_550E67F1F00BFF89F882603E5B70A41BLL12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) () -> @out _NativeActorQueue { -// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue): -// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1.Type -// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1.Type, $@thick AnyObject.Type -// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue -// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue - // A1.enqueue(partialTask:) -// CHECK-LABEL: sil hidden [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) (@in_guaranteed PartialAsyncTask, @guaranteed A1) -> () { +// CHECK-LABEL: sil [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) (@in_guaranteed PartialAsyncTask, @guaranteed A1) -> () { // CHECK: bb0([[PARTIAL_TASK:%.*]] : $*PartialAsyncTask, [[SELF:%.*]] : @guaranteed $A1): // CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $A1 // CHECK-NEXT: [[SELF_ANY_OBJECT:%.*]] = init_existential_ref [[SELF_COPY]] : $A1 : $A1, $AnyObject @@ -39,3 +36,18 @@ func buildIt() { // CHECK: [[ENQUEUE_FN:%.*]] = function_ref @$s12_Concurrency36_defaultActorQueueEnqueuePartialTask5actor5queue07partialG0yyXl_AA07_NativecD0VzAA0f5AsyncG0VtF : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () // CHECK-NEXT: apply [[ENQUEUE_FN]]([[SELF_ANY_OBJECT]], [[DYNAMIC_ACCESS]], [[PARTIAL_TASK]]) : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> () // CHECK-NEXT: end_access [[DYNAMIC_ACCESS]] : $*_NativeActorQueue + +// variable initialization expression of A1.$__actor_storage +// CHECK-LABEL: sil [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33{{.*}}12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) () -> @out _NativeActorQueue { +// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue): +// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1.Type +// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1.Type, $@thick AnyObject.Type +// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue +// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue + +// Ensure that enqueue(partialTask:) is the first slot in the vtable. +// CHECK-LABEL: sil_vtable [serialized] A1 { +// CHECK-NEXT: #A1.enqueue: (A1) -> (PartialAsyncTask) -> () : @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF + +// CHECK-LABEL: sil_vtable [serialized] A2 { +// CHECK-NEXT: #A2.enqueue: (A2) -> (PartialAsyncTask) -> () : @$s29synthesized_conformance_actor2A2C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF // A2.enqueue(partialTask:) From 445d74762264a5ecd41897caaf31b0d1922129aa Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 29 Sep 2020 19:06:40 -0400 Subject: [PATCH 07/20] AST: Move GenericParamList and friends to GenericParamList.{h,cpp} --- include/swift/AST/Decl.h | 360 +----------------- include/swift/AST/GenericEnvironment.h | 3 +- include/swift/AST/GenericParamList.h | 392 ++++++++++++++++++++ include/swift/AST/GenericSignature.h | 1 + include/swift/AST/GenericSignatureBuilder.h | 1 + include/swift/AST/TypeAlignments.h | 23 +- include/swift/AST/TypeCheckRequests.h | 1 + include/swift/Parse/Parser.h | 1 + lib/AST/ASTDumper.cpp | 1 + lib/AST/ASTScopeCreation.cpp | 1 + lib/AST/ASTScopeLookup.cpp | 1 + lib/AST/ASTScopePrinting.cpp | 1 + lib/AST/ASTScopeSourceRange.cpp | 1 + lib/AST/ASTWalker.cpp | 1 + lib/AST/CMakeLists.txt | 1 + lib/AST/ConformanceLookupTable.cpp | 1 + lib/AST/Decl.cpp | 109 ------ lib/AST/GenericParamList.cpp | 129 +++++++ lib/AST/GenericSignatureBuilder.cpp | 1 + lib/AST/NameLookup.cpp | 1 + lib/AST/NameLookupRequests.cpp | 1 + lib/AST/TypeRepr.cpp | 1 + lib/AST/USRGeneration.cpp | 1 + lib/IDE/Formatting.cpp | 1 + lib/Parse/ParseDecl.cpp | 1 + lib/Parse/ParseGeneric.cpp | 1 + lib/Parse/ParseType.cpp | 1 + lib/Sema/TypeChecker.h | 1 + unittests/AST/TestContext.cpp | 1 + 29 files changed, 569 insertions(+), 470 deletions(-) create mode 100644 include/swift/AST/GenericParamList.h create mode 100644 lib/AST/GenericParamList.cpp diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 588ef350f497d..0b9800d0a6b68 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -70,6 +70,7 @@ namespace swift { class BraceStmt; class DeclAttributes; class GenericContext; + class GenericParamList; class GenericSignature; class GenericTypeParamDecl; class GenericTypeParamType; @@ -88,6 +89,7 @@ namespace swift { class ProtocolType; struct RawComment; enum class ResilienceExpansion : unsigned; + class TrailingWhereClause; class TypeAliasDecl; class Stmt; class SubscriptDecl; @@ -1011,364 +1013,6 @@ void *allocateMemoryForDecl(AllocatorTy &allocator, size_t baseSize, return mem; } -enum class RequirementReprKind : unsigned { - /// A type bound T : P, where T is a type that depends on a generic - /// parameter and P is some type that should bound T, either as a concrete - /// supertype or a protocol to which T must conform. - TypeConstraint, - - /// A same-type requirement T == U, where T and U are types that shall be - /// equivalent. - SameType, - - /// A layout bound T : L, where T is a type that depends on a generic - /// parameter and L is some layout specification that should bound T. - LayoutConstraint, - - // Note: there is code that packs this enum in a 2-bit bitfield. Audit users - // when adding enumerators. -}; - -/// A single requirement in a 'where' clause, which places additional -/// restrictions on the generic parameters or associated types of a generic -/// function, type, or protocol. -/// -/// This always represents a requirement spelled in the source code. It is -/// never generated implicitly. -/// -/// \c GenericParamList assumes these are POD-like. -class RequirementRepr { - SourceLoc SeparatorLoc; - RequirementReprKind Kind : 2; - bool Invalid : 1; - TypeRepr *FirstType; - - /// The second element represents the right-hand side of the constraint. - /// It can be e.g. a type or a layout constraint. - union { - TypeRepr *SecondType; - LayoutConstraintLoc SecondLayout; - }; - - /// Set during deserialization; used to print out the requirements accurately - /// for the generated interface. - StringRef AsWrittenString; - - RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, - TypeRepr *FirstType, TypeRepr *SecondType) - : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), - FirstType(FirstType), SecondType(SecondType) { } - - RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, - TypeRepr *FirstType, LayoutConstraintLoc SecondLayout) - : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), - FirstType(FirstType), SecondLayout(SecondLayout) { } - - void printImpl(ASTPrinter &OS) const; - -public: - /// Construct a new type-constraint requirement. - /// - /// \param Subject The type that must conform to the given protocol or - /// composition, or be a subclass of the given class type. - /// \param ColonLoc The location of the ':', or an invalid location if - /// this requirement was implied. - /// \param Constraint The protocol or protocol composition to which the - /// subject must conform, or superclass from which the subject must inherit. - static RequirementRepr getTypeConstraint(TypeRepr *Subject, - SourceLoc ColonLoc, - TypeRepr *Constraint) { - return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint }; - } - - /// Construct a new same-type requirement. - /// - /// \param FirstType The first type. - /// \param EqualLoc The location of the '==' in the same-type constraint, or - /// an invalid location if this requirement was implied. - /// \param SecondType The second type. - static RequirementRepr getSameType(TypeRepr *FirstType, - SourceLoc EqualLoc, - TypeRepr *SecondType) { - return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType }; - } - - /// Construct a new layout-constraint requirement. - /// - /// \param Subject The type that must conform to the given layout - /// requirement. - /// \param ColonLoc The location of the ':', or an invalid location if - /// this requirement was implied. - /// \param Layout The layout requirement to which the - /// subject must conform. - static RequirementRepr getLayoutConstraint(TypeRepr *Subject, - SourceLoc ColonLoc, - LayoutConstraintLoc Layout) { - return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject, - Layout}; - } - - /// Determine the kind of requirement - RequirementReprKind getKind() const { return Kind; } - - /// Determine whether this requirement is invalid. - bool isInvalid() const { return Invalid; } - - /// Mark this requirement invalid. - void setInvalid() { Invalid = true; } - - /// For a type-bound requirement, return the subject of the - /// conformance relationship. - TypeRepr *getSubjectRepr() const { - assert(getKind() == RequirementReprKind::TypeConstraint || - getKind() == RequirementReprKind::LayoutConstraint); - return FirstType; - } - - /// For a type-bound requirement, return the protocol or to which - /// the subject conforms or superclass it inherits. - TypeRepr *getConstraintRepr() const { - assert(getKind() == RequirementReprKind::TypeConstraint); - return SecondType; - } - - LayoutConstraint getLayoutConstraint() const { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout.getLayoutConstraint(); - } - - LayoutConstraintLoc &getLayoutConstraintLoc() { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout; - } - - const LayoutConstraintLoc &getLayoutConstraintLoc() const { - assert(getKind() == RequirementReprKind::LayoutConstraint); - return SecondLayout; - } - - /// Retrieve the first type of a same-type requirement. - TypeRepr *getFirstTypeRepr() const { - assert(getKind() == RequirementReprKind::SameType); - return FirstType; - } - - /// Retrieve the second type of a same-type requirement. - TypeRepr *getSecondTypeRepr() const { - assert(getKind() == RequirementReprKind::SameType); - return SecondType; - } - - /// Retrieve the location of the ':' or '==' in an explicitly-written - /// conformance or same-type requirement respectively. - SourceLoc getSeparatorLoc() const { - return SeparatorLoc; - } - - SourceRange getSourceRange() const; - - /// Retrieve the first or subject type representation from the \c repr, - /// or \c nullptr if \c repr is null. - static TypeRepr *getFirstTypeRepr(const RequirementRepr *repr) { - if (!repr) return nullptr; - return repr->FirstType; - } - - /// Retrieve the second or constraint type representation from the \c repr, - /// or \c nullptr if \c repr is null. - static TypeRepr *getSecondTypeRepr(const RequirementRepr *repr) { - if (!repr) return nullptr; - assert(repr->getKind() == RequirementReprKind::TypeConstraint || - repr->getKind() == RequirementReprKind::SameType); - return repr->SecondType; - } - - SWIFT_DEBUG_DUMP; - void print(raw_ostream &OS) const; - void print(ASTPrinter &Printer) const; -}; - -using GenericParamSource = PointerUnion; - -/// GenericParamList - A list of generic parameters that is part of a generic -/// function or type, along with extra requirements placed on those generic -/// parameters and types derived from them. -class GenericParamList final : - private llvm::TrailingObjects { - friend TrailingObjects; - - SourceRange Brackets; - unsigned NumParams; - SourceLoc WhereLoc; - MutableArrayRef Requirements; - - GenericParamList *OuterParameters; - - GenericParamList(SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - MutableArrayRef Requirements, - SourceLoc RAngleLoc); - - // Don't copy. - GenericParamList(const GenericParamList &) = delete; - GenericParamList &operator=(const GenericParamList &) = delete; - -public: - /// create - Create a new generic parameter list within the given AST context. - /// - /// \param Context The ASTContext in which the generic parameter list will - /// be allocated. - /// \param LAngleLoc The location of the opening angle bracket ('<') - /// \param Params The list of generic parameters, which will be copied into - /// ASTContext-allocated memory. - /// \param RAngleLoc The location of the closing angle bracket ('>') - static GenericParamList *create(ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc RAngleLoc); - - /// create - Create a new generic parameter list and "where" clause within - /// the given AST context. - /// - /// \param Context The ASTContext in which the generic parameter list will - /// be allocated. - /// \param LAngleLoc The location of the opening angle bracket ('<') - /// \param Params The list of generic parameters, which will be copied into - /// ASTContext-allocated memory. - /// \param WhereLoc The location of the 'where' keyword, if any. - /// \param Requirements The list of requirements, which will be copied into - /// ASTContext-allocated memory. - /// \param RAngleLoc The location of the closing angle bracket ('>') - static GenericParamList *create(const ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - ArrayRef Requirements, - SourceLoc RAngleLoc); - - MutableArrayRef getParams() { - return {getTrailingObjects(), NumParams}; - } - - ArrayRef getParams() const { - return {getTrailingObjects(), NumParams}; - } - - using iterator = GenericTypeParamDecl **; - using const_iterator = const GenericTypeParamDecl * const *; - - unsigned size() const { return NumParams; } - iterator begin() { return getParams().begin(); } - iterator end() { return getParams().end(); } - const_iterator begin() const { return getParams().begin(); } - const_iterator end() const { return getParams().end(); } - - /// Retrieve the location of the 'where' keyword, or an invalid - /// location if 'where' was not present. - SourceLoc getWhereLoc() const { return WhereLoc; } - - /// Retrieve the set of additional requirements placed on these - /// generic parameters and types derived from them. - /// - /// This list may contain both explicitly-written requirements as well as - /// implicitly-generated requirements, and may be non-empty even if no - /// 'where' keyword is present. - MutableArrayRef getRequirements() { return Requirements; } - - /// Retrieve the set of additional requirements placed on these - /// generic parameters and types derived from them. - /// - /// This list may contain both explicitly-written requirements as well as - /// implicitly-generated requirements, and may be non-empty even if no - /// 'where' keyword is present. - ArrayRef getRequirements() const { return Requirements; } - - /// Retrieve the outer generic parameter list. - /// - /// This is used for extensions of nested types, and in SIL mode, where a - /// single lexical context can have multiple logical generic parameter - /// lists. - GenericParamList *getOuterParameters() const { return OuterParameters; } - - /// Set the outer generic parameter list. See \c getOuterParameters - /// for more information. - void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } - - void setDeclContext(DeclContext *dc); - - SourceLoc getLAngleLoc() const { return Brackets.Start; } - SourceLoc getRAngleLoc() const { return Brackets.End; } - - SourceRange getSourceRange() const { return Brackets; } - - /// Retrieve the source range covering the where clause. - SourceRange getWhereClauseSourceRange() const { - if (WhereLoc.isInvalid()) - return SourceRange(); - - auto endLoc = Requirements.back().getSourceRange().End; - return SourceRange(WhereLoc, endLoc); - } - - /// Configure the depth of the generic parameters in this list. - void setDepth(unsigned depth); - - /// Create a copy of the generic parameter list and all of its generic - /// parameter declarations. The copied generic parameters are re-parented - /// to the given DeclContext. - GenericParamList *clone(DeclContext *dc) const; - - void print(raw_ostream &OS) const; - SWIFT_DEBUG_DUMP; - - bool walk(ASTWalker &walker); - - /// Finds a generic parameter declaration by name. This should only - /// be used from the SIL parser. - GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; -}; - -/// A trailing where clause. -class alignas(RequirementRepr) TrailingWhereClause final : - private llvm::TrailingObjects { - friend TrailingObjects; - - SourceLoc WhereLoc; - - /// The number of requirements. The actual requirements are tail-allocated. - unsigned NumRequirements; - - TrailingWhereClause(SourceLoc whereLoc, - ArrayRef requirements); - -public: - /// Create a new trailing where clause with the given set of requirements. - static TrailingWhereClause *create(ASTContext &ctx, SourceLoc whereLoc, - ArrayRef requirements); - - /// Retrieve the location of the 'where' keyword. - SourceLoc getWhereLoc() const { return WhereLoc; } - - /// Retrieve the set of requirements. - MutableArrayRef getRequirements() { - return {getTrailingObjects(), NumRequirements}; - } - - /// Retrieve the set of requirements. - ArrayRef getRequirements() const { - return {getTrailingObjects(), NumRequirements}; - } - - /// Compute the source range containing this trailing where clause. - SourceRange getSourceRange() const { - return SourceRange(WhereLoc, - getRequirements().back().getSourceRange().End); - } - - void print(llvm::raw_ostream &OS, bool printWhereKeyword) const; -}; - // A private class for forcing exact field layout. class alignas(8) _GenericContext { // Not really public. See GenericContext. diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index a47067b940fa7..3c269644ea980 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -18,8 +18,9 @@ #define SWIFT_AST_GENERIC_ENVIRONMENT_H #include "swift/AST/SubstitutionMap.h" -#include "swift/AST/GenericSignature.h" #include "swift/AST/GenericParamKey.h" +#include "swift/AST/GenericParamList.h" +#include "swift/AST/GenericSignature.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" diff --git a/include/swift/AST/GenericParamList.h b/include/swift/AST/GenericParamList.h new file mode 100644 index 0000000000000..2d3b8c34475c3 --- /dev/null +++ b/include/swift/AST/GenericParamList.h @@ -0,0 +1,392 @@ +//===--- GenericParamList.h - Generic parameter list AST --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the GenericParamList class, and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_GENERIC_PARAM_LIST_H +#define SWIFT_GENERIC_PARAM_LIST_H + +#include "swift/AST/Decl.h" +#include "swift/AST/LayoutConstraint.h" +#include "swift/Basic/SourceLoc.h" +#include "llvm/ADT/StringRef.h" + +namespace swift { + +class ASTPrinter; +class TypeRepr; + +enum class RequirementReprKind : unsigned { + /// A type bound T : P, where T is a type that depends on a generic + /// parameter and P is some type that should bound T, either as a concrete + /// supertype or a protocol to which T must conform. + TypeConstraint, + + /// A same-type requirement T == U, where T and U are types that shall be + /// equivalent. + SameType, + + /// A layout bound T : L, where T is a type that depends on a generic + /// parameter and L is some layout specification that should bound T. + LayoutConstraint, + + // Note: there is code that packs this enum in a 2-bit bitfield. Audit users + // when adding enumerators. +}; + +/// A single requirement in a 'where' clause, which places additional +/// restrictions on the generic parameters or associated types of a generic +/// function, type, or protocol. +/// +/// This always represents a requirement spelled in the source code. It is +/// never generated implicitly. +/// +/// \c GenericParamList assumes these are POD-like. + +class RequirementRepr { + SourceLoc SeparatorLoc; + RequirementReprKind Kind : 2; + bool Invalid : 1; + TypeRepr *FirstType; + + /// The second element represents the right-hand side of the constraint. + /// It can be e.g. a type or a layout constraint. + union { + TypeRepr *SecondType; + LayoutConstraintLoc SecondLayout; + }; + + /// Set during deserialization; used to print out the requirements accurately + /// for the generated interface. + StringRef AsWrittenString; + + RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, + TypeRepr *FirstType, TypeRepr *SecondType) + : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), + FirstType(FirstType), SecondType(SecondType) { } + + RequirementRepr(SourceLoc SeparatorLoc, RequirementReprKind Kind, + TypeRepr *FirstType, LayoutConstraintLoc SecondLayout) + : SeparatorLoc(SeparatorLoc), Kind(Kind), Invalid(false), + FirstType(FirstType), SecondLayout(SecondLayout) { } + + void printImpl(ASTPrinter &OS) const; + +public: + /// Construct a new type-constraint requirement. + /// + /// \param Subject The type that must conform to the given protocol or + /// composition, or be a subclass of the given class type. + /// \param ColonLoc The location of the ':', or an invalid location if + /// this requirement was implied. + /// \param Constraint The protocol or protocol composition to which the + /// subject must conform, or superclass from which the subject must inherit. + static RequirementRepr getTypeConstraint(TypeRepr *Subject, + SourceLoc ColonLoc, + TypeRepr *Constraint) { + return { ColonLoc, RequirementReprKind::TypeConstraint, Subject, Constraint }; + } + + /// Construct a new same-type requirement. + /// + /// \param FirstType The first type. + /// \param EqualLoc The location of the '==' in the same-type constraint, or + /// an invalid location if this requirement was implied. + /// \param SecondType The second type. + static RequirementRepr getSameType(TypeRepr *FirstType, + SourceLoc EqualLoc, + TypeRepr *SecondType) { + return { EqualLoc, RequirementReprKind::SameType, FirstType, SecondType }; + } + + /// Construct a new layout-constraint requirement. + /// + /// \param Subject The type that must conform to the given layout + /// requirement. + /// \param ColonLoc The location of the ':', or an invalid location if + /// this requirement was implied. + /// \param Layout The layout requirement to which the + /// subject must conform. + static RequirementRepr getLayoutConstraint(TypeRepr *Subject, + SourceLoc ColonLoc, + LayoutConstraintLoc Layout) { + return {ColonLoc, RequirementReprKind::LayoutConstraint, Subject, + Layout}; + } + + /// Determine the kind of requirement + RequirementReprKind getKind() const { return Kind; } + + /// Determine whether this requirement is invalid. + bool isInvalid() const { return Invalid; } + + /// Mark this requirement invalid. + void setInvalid() { Invalid = true; } + + /// For a type-bound requirement, return the subject of the + /// conformance relationship. + TypeRepr *getSubjectRepr() const { + assert(getKind() == RequirementReprKind::TypeConstraint || + getKind() == RequirementReprKind::LayoutConstraint); + return FirstType; + } + + /// For a type-bound requirement, return the protocol or to which + /// the subject conforms or superclass it inherits. + TypeRepr *getConstraintRepr() const { + assert(getKind() == RequirementReprKind::TypeConstraint); + return SecondType; + } + + LayoutConstraint getLayoutConstraint() const { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout.getLayoutConstraint(); + } + + LayoutConstraintLoc &getLayoutConstraintLoc() { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout; + } + + const LayoutConstraintLoc &getLayoutConstraintLoc() const { + assert(getKind() == RequirementReprKind::LayoutConstraint); + return SecondLayout; + } + + /// Retrieve the first type of a same-type requirement. + TypeRepr *getFirstTypeRepr() const { + assert(getKind() == RequirementReprKind::SameType); + return FirstType; + } + + /// Retrieve the second type of a same-type requirement. + TypeRepr *getSecondTypeRepr() const { + assert(getKind() == RequirementReprKind::SameType); + return SecondType; + } + + /// Retrieve the location of the ':' or '==' in an explicitly-written + /// conformance or same-type requirement respectively. + SourceLoc getSeparatorLoc() const { + return SeparatorLoc; + } + + SourceRange getSourceRange() const; + + /// Retrieve the first or subject type representation from the \c repr, + /// or \c nullptr if \c repr is null. + static TypeRepr *getFirstTypeRepr(const RequirementRepr *repr) { + if (!repr) return nullptr; + return repr->FirstType; + } + + /// Retrieve the second or constraint type representation from the \c repr, + /// or \c nullptr if \c repr is null. + static TypeRepr *getSecondTypeRepr(const RequirementRepr *repr) { + if (!repr) return nullptr; + assert(repr->getKind() == RequirementReprKind::TypeConstraint || + repr->getKind() == RequirementReprKind::SameType); + return repr->SecondType; + } + + SWIFT_DEBUG_DUMP; + void print(raw_ostream &OS) const; + void print(ASTPrinter &Printer) const; +}; + +using GenericParamSource = PointerUnion; + +/// GenericParamList - A list of generic parameters that is part of a generic +/// function or type, along with extra requirements placed on those generic +/// parameters and types derived from them. +class GenericParamList final : + private llvm::TrailingObjects { + friend TrailingObjects; + + SourceRange Brackets; + unsigned NumParams; + SourceLoc WhereLoc; + MutableArrayRef Requirements; + + GenericParamList *OuterParameters; + + GenericParamList(SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + MutableArrayRef Requirements, + SourceLoc RAngleLoc); + + // Don't copy. + GenericParamList(const GenericParamList &) = delete; + GenericParamList &operator=(const GenericParamList &) = delete; + +public: + /// create - Create a new generic parameter list within the given AST context. + /// + /// \param Context The ASTContext in which the generic parameter list will + /// be allocated. + /// \param LAngleLoc The location of the opening angle bracket ('<') + /// \param Params The list of generic parameters, which will be copied into + /// ASTContext-allocated memory. + /// \param RAngleLoc The location of the closing angle bracket ('>') + static GenericParamList *create(ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc RAngleLoc); + + /// create - Create a new generic parameter list and "where" clause within + /// the given AST context. + /// + /// \param Context The ASTContext in which the generic parameter list will + /// be allocated. + /// \param LAngleLoc The location of the opening angle bracket ('<') + /// \param Params The list of generic parameters, which will be copied into + /// ASTContext-allocated memory. + /// \param WhereLoc The location of the 'where' keyword, if any. + /// \param Requirements The list of requirements, which will be copied into + /// ASTContext-allocated memory. + /// \param RAngleLoc The location of the closing angle bracket ('>') + static GenericParamList *create(const ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + ArrayRef Requirements, + SourceLoc RAngleLoc); + + MutableArrayRef getParams() { + return {getTrailingObjects(), NumParams}; + } + + ArrayRef getParams() const { + return {getTrailingObjects(), NumParams}; + } + + using iterator = GenericTypeParamDecl **; + using const_iterator = const GenericTypeParamDecl * const *; + + unsigned size() const { return NumParams; } + iterator begin() { return getParams().begin(); } + iterator end() { return getParams().end(); } + const_iterator begin() const { return getParams().begin(); } + const_iterator end() const { return getParams().end(); } + + /// Retrieve the location of the 'where' keyword, or an invalid + /// location if 'where' was not present. + SourceLoc getWhereLoc() const { return WhereLoc; } + + /// Retrieve the set of additional requirements placed on these + /// generic parameters and types derived from them. + /// + /// This list may contain both explicitly-written requirements as well as + /// implicitly-generated requirements, and may be non-empty even if no + /// 'where' keyword is present. + MutableArrayRef getRequirements() { return Requirements; } + + /// Retrieve the set of additional requirements placed on these + /// generic parameters and types derived from them. + /// + /// This list may contain both explicitly-written requirements as well as + /// implicitly-generated requirements, and may be non-empty even if no + /// 'where' keyword is present. + ArrayRef getRequirements() const { return Requirements; } + + /// Retrieve the outer generic parameter list. + /// + /// This is used for extensions of nested types, and in SIL mode, where a + /// single lexical context can have multiple logical generic parameter + /// lists. + GenericParamList *getOuterParameters() const { return OuterParameters; } + + /// Set the outer generic parameter list. See \c getOuterParameters + /// for more information. + void setOuterParameters(GenericParamList *Outer) { OuterParameters = Outer; } + + void setDeclContext(DeclContext *dc); + + SourceLoc getLAngleLoc() const { return Brackets.Start; } + SourceLoc getRAngleLoc() const { return Brackets.End; } + + SourceRange getSourceRange() const { return Brackets; } + + /// Retrieve the source range covering the where clause. + SourceRange getWhereClauseSourceRange() const { + if (WhereLoc.isInvalid()) + return SourceRange(); + + auto endLoc = Requirements.back().getSourceRange().End; + return SourceRange(WhereLoc, endLoc); + } + + /// Configure the depth of the generic parameters in this list. + void setDepth(unsigned depth); + + /// Create a copy of the generic parameter list and all of its generic + /// parameter declarations. The copied generic parameters are re-parented + /// to the given DeclContext. + GenericParamList *clone(DeclContext *dc) const; + + void print(raw_ostream &OS) const; + SWIFT_DEBUG_DUMP; + + bool walk(ASTWalker &walker); + + /// Finds a generic parameter declaration by name. This should only + /// be used from the SIL parser. + GenericTypeParamDecl *lookUpGenericParam(Identifier name) const; +}; + +/// A trailing where clause. +class alignas(RequirementRepr) TrailingWhereClause final : + private llvm::TrailingObjects { + friend TrailingObjects; + + SourceLoc WhereLoc; + + /// The number of requirements. The actual requirements are tail-allocated. + unsigned NumRequirements; + + TrailingWhereClause(SourceLoc whereLoc, + ArrayRef requirements); + +public: + /// Create a new trailing where clause with the given set of requirements. + static TrailingWhereClause *create(ASTContext &ctx, SourceLoc whereLoc, + ArrayRef requirements); + + /// Retrieve the location of the 'where' keyword. + SourceLoc getWhereLoc() const { return WhereLoc; } + + /// Retrieve the set of requirements. + MutableArrayRef getRequirements() { + return {getTrailingObjects(), NumRequirements}; + } + + /// Retrieve the set of requirements. + ArrayRef getRequirements() const { + return {getTrailingObjects(), NumRequirements}; + } + + /// Compute the source range containing this trailing where clause. + SourceRange getSourceRange() const { + return SourceRange(WhereLoc, + getRequirements().back().getSourceRange().End); + } + + void print(llvm::raw_ostream &OS, bool printWhereKeyword) const; + +}; + +} // namespace swift + +#endif // SWIFT_GENERIC_PARAM_LIST_H \ No newline at end of file diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 3a29d21fbd890..fccbe06bde122 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -20,6 +20,7 @@ #include "swift/AST/PrintOptions.h" #include "swift/AST/Requirement.h" #include "swift/AST/Type.h" +#include "swift/AST/TypeAlignments.h" #include "swift/Basic/AnyValue.h" #include "swift/Basic/Debug.h" #include "llvm/ADT/ArrayRef.h" diff --git a/include/swift/AST/GenericSignatureBuilder.h b/include/swift/AST/GenericSignatureBuilder.h index e91543b476539..6b8c507569a69 100644 --- a/include/swift/AST/GenericSignatureBuilder.h +++ b/include/swift/AST/GenericSignatureBuilder.h @@ -22,6 +22,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Identifier.h" #include "swift/AST/ProtocolConformanceRef.h" diff --git a/include/swift/AST/TypeAlignments.h b/include/swift/AST/TypeAlignments.h index f0081a80dacf4..6097cf9a29fc5 100644 --- a/include/swift/AST/TypeAlignments.h +++ b/include/swift/AST/TypeAlignments.h @@ -35,9 +35,11 @@ namespace swift { class CaptureListExpr; class Decl; class DeclContext; + class DifferentiableAttr; class Expr; class ExtensionDecl; class GenericEnvironment; + class GenericParamList; class GenericTypeParamDecl; class NominalTypeDecl; class NormalProtocolConformance; @@ -46,9 +48,12 @@ namespace swift { class Pattern; class ProtocolDecl; class ProtocolConformance; + class RequirementRepr; class SILFunction; class SILFunctionType; + class SpecializeAttr; class Stmt; + class TrailingWhereClause; class TypeVariableType; class TypeBase; class TypeDecl; @@ -63,9 +68,15 @@ namespace swift { constexpr size_t StmtAlignInBits = 3; constexpr size_t TypeAlignInBits = 3; constexpr size_t PatternAlignInBits = 3; + constexpr size_t TypeReprAlignInBits = 3; + constexpr size_t SILFunctionAlignInBits = 2; + constexpr size_t ASTContextAlignInBits = 2; constexpr size_t TypeVariableAlignInBits = 4; - constexpr size_t TypeReprAlignInBits = 3; + + // Well, this is the *minimum* pointer alignment; it's going to be 3 on + // 64-bit targets, but that doesn't matter. + constexpr size_t PointerAlignInBits = 2; } namespace llvm { @@ -110,8 +121,9 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunctionType, LLVM_DECLARE_TYPE_ALIGNMENT(swift::Stmt, swift::StmtAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::BraceStmt, swift::StmtAlignInBits) -LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, 2); +LLVM_DECLARE_TYPE_ALIGNMENT(swift::ASTContext, swift::ASTContextAlignInBits); LLVM_DECLARE_TYPE_ALIGNMENT(swift::DeclContext, swift::DeclContextAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::DifferentiableAttr, swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Expr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::CaptureListExpr, swift::ExprAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AbstractClosureExpr, swift::ExprAlignInBits) @@ -121,10 +133,17 @@ LLVM_DECLARE_TYPE_ALIGNMENT(swift::NormalProtocolConformance, swift::DeclAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericEnvironment, swift::DeclAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::GenericParamList, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::Pattern, swift::PatternAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::RequirementRepr, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::SILFunction, swift::SILFunctionAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::SpecializeAttr, swift::PointerAlignInBits) +LLVM_DECLARE_TYPE_ALIGNMENT(swift::TrailingWhereClause, + swift::PointerAlignInBits) LLVM_DECLARE_TYPE_ALIGNMENT(swift::AttributeBase, swift::AttrAlignInBits) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index ed84c49bbfa8e..045efb7179e42 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -19,6 +19,7 @@ #include "swift/AST/ActorIsolation.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/ASTTypeIDs.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" #include "swift/AST/Evaluator.h" diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 57c18ef074c6b..3cae352ef73dd 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -50,6 +50,7 @@ namespace swift { class Lexer; class ParsedTypeSyntax; class PersistentParserState; + class RequirementRepr; class SILParserStateBase; class ScopeInfo; class SourceManager; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 91b809e881ed5..dd12a1570f09b 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 3a76406d8ea4e..0c6d22b4bca5c 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -20,6 +20,7 @@ #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index e848e6c12c52c..20eea9d67101c 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index f68e4f582628e..2dafad62d3946 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index fc86ba1c2f84b..48a59b2f3ac59 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index bc5998914d1b3..900afa1baf7aa 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -55,6 +55,7 @@ #include "swift/AST/ASTWalker.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PrettyStackTrace.h" using namespace swift; diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 62993edae03ea..6762297a9f098 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -49,6 +49,7 @@ add_swift_host_library(swiftAST STATIC FineGrainedDependencyFormat.cpp FrontendSourceFileDepGraphFactory.cpp GenericEnvironment.cpp + GenericParamList.cpp GenericSignature.cpp GenericSignatureBuilder.cpp Identifier.cpp diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 45a249601a7ac..547fa1bd4b4d0 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -18,6 +18,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a330b63faedcb..6a153ef671abd 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -891,115 +891,6 @@ bool Decl::isWeakImported(ModuleDecl *fromModule) const { return !fromContext.isContainedIn(containingContext); } - -SourceRange RequirementRepr::getSourceRange() const { - if (getKind() == RequirementReprKind::LayoutConstraint) - return SourceRange(FirstType->getSourceRange().Start, - SecondLayout.getSourceRange().End); - return SourceRange(FirstType->getSourceRange().Start, - SecondType->getSourceRange().End); -} - -GenericParamList::GenericParamList(SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - MutableArrayRef Requirements, - SourceLoc RAngleLoc) - : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), - WhereLoc(WhereLoc), Requirements(Requirements), - OuterParameters(nullptr) -{ - std::uninitialized_copy(Params.begin(), Params.end(), - getTrailingObjects()); -} - -GenericParamList * -GenericParamList::create(ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc RAngleLoc) { - unsigned Size = totalSizeToAlloc(Params.size()); - void *Mem = Context.Allocate(Size, alignof(GenericParamList)); - return new (Mem) GenericParamList(LAngleLoc, Params, SourceLoc(), - MutableArrayRef(), - RAngleLoc); -} - -GenericParamList * -GenericParamList::create(const ASTContext &Context, - SourceLoc LAngleLoc, - ArrayRef Params, - SourceLoc WhereLoc, - ArrayRef Requirements, - SourceLoc RAngleLoc) { - unsigned Size = totalSizeToAlloc(Params.size()); - void *Mem = Context.Allocate(Size, alignof(GenericParamList)); - return new (Mem) GenericParamList(LAngleLoc, Params, - WhereLoc, - Context.AllocateCopy(Requirements), - RAngleLoc); -} - -GenericParamList * -GenericParamList::clone(DeclContext *dc) const { - auto &ctx = dc->getASTContext(); - SmallVector params; - for (auto param : getParams()) { - auto *newParam = new (ctx) GenericTypeParamDecl( - dc, param->getName(), SourceLoc(), - GenericTypeParamDecl::InvalidDepth, - param->getIndex()); - newParam->setImplicit(true); - params.push_back(newParam); - } - - return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); -} - -void GenericParamList::setDepth(unsigned depth) { - for (auto param : *this) - param->setDepth(depth); -} - -void GenericParamList::setDeclContext(DeclContext *dc) { - for (auto param : *this) - param->setDeclContext(dc); -} - -GenericTypeParamDecl *GenericParamList::lookUpGenericParam( - Identifier name) const { - for (const auto *innerParams = this; - innerParams != nullptr; - innerParams = innerParams->getOuterParameters()) { - for (auto *paramDecl : *innerParams) { - if (name == paramDecl->getName()) { - return const_cast(paramDecl); - } - } - } - - return nullptr; -} - -TrailingWhereClause::TrailingWhereClause( - SourceLoc whereLoc, - ArrayRef requirements) - : WhereLoc(whereLoc), - NumRequirements(requirements.size()) -{ - std::uninitialized_copy(requirements.begin(), requirements.end(), - getTrailingObjects()); -} - -TrailingWhereClause *TrailingWhereClause::create( - ASTContext &ctx, - SourceLoc whereLoc, - ArrayRef requirements) { - unsigned size = totalSizeToAlloc(requirements.size()); - void *mem = ctx.Allocate(size, alignof(TrailingWhereClause)); - return new (mem) TrailingWhereClause(whereLoc, requirements); -} - GenericContext::GenericContext(DeclContextKind Kind, DeclContext *Parent, GenericParamList *Params) : _GenericContext(), DeclContext(Kind, Parent) { diff --git a/lib/AST/GenericParamList.cpp b/lib/AST/GenericParamList.cpp new file mode 100644 index 0000000000000..426833e151997 --- /dev/null +++ b/lib/AST/GenericParamList.cpp @@ -0,0 +1,129 @@ +//===--- GenericParamList.cpp - Swift Language Decl ASTs ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements the GenericParamList class and related classes. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/GenericParamList.h" + +#include "swift/AST/ASTContext.h" +#include "swift/AST/TypeRepr.h" + +using namespace swift; +SourceRange RequirementRepr::getSourceRange() const { + if (getKind() == RequirementReprKind::LayoutConstraint) + return SourceRange(FirstType->getSourceRange().Start, + SecondLayout.getSourceRange().End); + return SourceRange(FirstType->getSourceRange().Start, + SecondType->getSourceRange().End); +} + +GenericParamList::GenericParamList(SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + MutableArrayRef Requirements, + SourceLoc RAngleLoc) + : Brackets(LAngleLoc, RAngleLoc), NumParams(Params.size()), + WhereLoc(WhereLoc), Requirements(Requirements), + OuterParameters(nullptr) +{ + std::uninitialized_copy(Params.begin(), Params.end(), + getTrailingObjects()); +} + +GenericParamList * +GenericParamList::create(ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc RAngleLoc) { + unsigned Size = totalSizeToAlloc(Params.size()); + void *Mem = Context.Allocate(Size, alignof(GenericParamList)); + return new (Mem) GenericParamList(LAngleLoc, Params, SourceLoc(), + MutableArrayRef(), + RAngleLoc); +} + +GenericParamList * +GenericParamList::create(const ASTContext &Context, + SourceLoc LAngleLoc, + ArrayRef Params, + SourceLoc WhereLoc, + ArrayRef Requirements, + SourceLoc RAngleLoc) { + unsigned Size = totalSizeToAlloc(Params.size()); + void *Mem = Context.Allocate(Size, alignof(GenericParamList)); + return new (Mem) GenericParamList(LAngleLoc, Params, + WhereLoc, + Context.AllocateCopy(Requirements), + RAngleLoc); +} + +GenericParamList * +GenericParamList::clone(DeclContext *dc) const { + auto &ctx = dc->getASTContext(); + SmallVector params; + for (auto param : getParams()) { + auto *newParam = new (ctx) GenericTypeParamDecl( + dc, param->getName(), SourceLoc(), + GenericTypeParamDecl::InvalidDepth, + param->getIndex()); + newParam->setImplicit(true); + params.push_back(newParam); + } + + return GenericParamList::create(ctx, SourceLoc(), params, SourceLoc()); +} + +void GenericParamList::setDepth(unsigned depth) { + for (auto param : *this) + param->setDepth(depth); +} + +void GenericParamList::setDeclContext(DeclContext *dc) { + for (auto param : *this) + param->setDeclContext(dc); +} + +GenericTypeParamDecl *GenericParamList::lookUpGenericParam( + Identifier name) const { + for (const auto *innerParams = this; + innerParams != nullptr; + innerParams = innerParams->getOuterParameters()) { + for (auto *paramDecl : *innerParams) { + if (name == paramDecl->getName()) { + return const_cast(paramDecl); + } + } + } + + return nullptr; +} + +TrailingWhereClause::TrailingWhereClause( + SourceLoc whereLoc, + ArrayRef requirements) + : WhereLoc(whereLoc), + NumRequirements(requirements.size()) +{ + std::uninitialized_copy(requirements.begin(), requirements.end(), + getTrailingObjects()); +} + +TrailingWhereClause *TrailingWhereClause::create( + ASTContext &ctx, + SourceLoc whereLoc, + ArrayRef requirements) { + unsigned size = totalSizeToAlloc(requirements.size()); + void *mem = ctx.Allocate(size, alignof(TrailingWhereClause)); + return new (mem) TrailingWhereClause(whereLoc, requirements); +} diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index bff5a8688291d..09b02ec88d227 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -24,6 +24,7 @@ #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index fb7cbf7a19568..e3ce6eb170076 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -20,6 +20,7 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.h" diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index 379a2748fd9dc..62b6484f5c547 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -14,6 +14,7 @@ #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Evaluator.h" #include "swift/AST/Module.h" diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 6490258e3b922..24253def0d94f 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/Expr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" diff --git a/lib/AST/USRGeneration.cpp b/lib/AST/USRGeneration.cpp index 30716b9f498c8..c3fe2322c21c9 100644 --- a/lib/AST/USRGeneration.cpp +++ b/lib/AST/USRGeneration.cpp @@ -12,6 +12,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ClangModuleLoader.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/USRGeneration.h" #include "swift/AST/ASTMangler.h" diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 6100a21d41669..a35bb306e10cf 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/ASTWalker.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/IDE/SourceEntityWalker.h" #include "swift/Parse/Parser.h" diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index fa9ace4441e27..da749109d0fd9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -23,6 +23,7 @@ #include "swift/Subsystems.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/DiagnosticsParse.h" diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index f980de64d56c8..969635bdd3cb6 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -16,6 +16,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Parse/SyntaxParsingContext.h" diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 016281ab3d6b0..a1ceca394ac08 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -17,6 +17,7 @@ #include "swift/Parse/Parser.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Attr.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/TypeRepr.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/CodeCompletionCallbacks.h" diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index dbd5f63e86b05..fa8f01f211e7c 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -22,6 +22,7 @@ #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Availability.h" #include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/KnownProtocols.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/NameLookup.h" diff --git a/unittests/AST/TestContext.cpp b/unittests/AST/TestContext.cpp index 174cf90630097..1cee32305944f 100644 --- a/unittests/AST/TestContext.cpp +++ b/unittests/AST/TestContext.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TestContext.h" +#include "swift/AST/GenericParamList.h" #include "swift/AST/Module.h" #include "swift/AST/ParseRequests.h" #include "swift/Strings.h" From 82ead3e8b5d4433ae60a205f18a77ab0beee02c8 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 29 Sep 2020 17:17:27 -0700 Subject: [PATCH 08/20] [NFC] Add test for (incorrect) behavior of swift-demangle. --- test/Demangle/Inputs/manglings.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 7f410eec10faa..c0326ef954197 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -357,6 +357,8 @@ $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red. $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP ---> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) $sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () +$s4main1gyySiXCvp ---> main.g : (Swift.Int) -> () +$s4main1gyySiXBvp ---> main.g : (Swift.Int) -> () $sxq_Idgnr_D ---> @differentiable @callee_guaranteed (@in_guaranteed A) -> (@out B) $sxq_Ilgnr_D ---> @differentiable(linear) @callee_guaranteed (@in_guaranteed A) -> (@out B) $sS3fIedgyywd_D ---> @escaping @differentiable @callee_guaranteed (@unowned Swift.Float, @unowned @noDerivative Swift.Float) -> (@unowned Swift.Float) From 26d828395803aacee1a85dd40e221d51f225e507 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 29 Sep 2020 17:27:12 -0700 Subject: [PATCH 09/20] [Demangler] Print convention attributes consistently. --- lib/Demangling/NodePrinter.cpp | 85 +++++++++++++----------------- test/Demangle/Inputs/manglings.txt | 4 +- 2 files changed, 38 insertions(+), 51 deletions(-) diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 3a7fc824699b2..5287239caffc5 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -18,6 +18,7 @@ #include "swift/Demangling/Demangle.h" #include "swift/AST/Ownership.h" #include "swift/Strings.h" +#include #include #include @@ -753,6 +754,35 @@ class NodePrinter { setInvalid(); return; } + + switch (node->getKind()) { + case Node::Kind::FunctionType: + case Node::Kind::UncurriedFunctionType: + case Node::Kind::NoEscapeFunctionType: + break; + case Node::Kind::AutoClosureType: + case Node::Kind::EscapingAutoClosureType: + Printer << "@autoclosure "; break; + case Node::Kind::ThinFunctionType: + Printer << "@convention(thin) "; break; + case Node::Kind::CFunctionPointer: + Printer << "@convention(c) "; break; + case Node::Kind::ObjCBlock: + Printer << "@convention(block) "; break; + case Node::Kind::EscapingObjCBlock: + Printer << "@escaping @convention(block) "; break; + case Node::Kind::DifferentiableFunctionType: + Printer << "@differentiable "; break; + case Node::Kind::EscapingDifferentiableFunctionType: + Printer << "@escaping @differentiable "; break; + case Node::Kind::LinearFunctionType: + Printer << "@differentiable(linear) "; break; + case Node::Kind::EscapingLinearFunctionType: + Printer << "@escaping @differentiable(linear) "; break; + default: + assert(false && "Unhandled function type in printFunctionType!"); + } + unsigned startIndex = 0; bool isAsync = false, isThrows = false; if (node->getChild(startIndex)->getKind() == Node::Kind::ThrowsAnnotation) { @@ -1256,39 +1286,19 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::UnknownIndex: Printer << "unknown index"; return nullptr; + case Node::Kind::FunctionType: + case Node::Kind::UncurriedFunctionType: case Node::Kind::NoEscapeFunctionType: - printFunctionType(nullptr, Node); - return nullptr; - case Node::Kind::EscapingAutoClosureType: - Printer << "@autoclosure "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::AutoClosureType: - Printer << "@autoclosure "; - printFunctionType(nullptr, Node); - return nullptr; + case Node::Kind::EscapingAutoClosureType: case Node::Kind::ThinFunctionType: - Printer << "@convention(thin) "; - printFunctionType(nullptr, Node); - return nullptr; + case Node::Kind::CFunctionPointer: + case Node::Kind::ObjCBlock: + case Node::Kind::EscapingObjCBlock: case Node::Kind::DifferentiableFunctionType: - Printer << "@differentiable "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::EscapingDifferentiableFunctionType: - Printer << "@escaping @differentiable "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::LinearFunctionType: - Printer << "@differentiable(linear) "; - printFunctionType(nullptr, Node); - return nullptr; case Node::Kind::EscapingLinearFunctionType: - Printer << "@escaping @differentiable(linear) "; - printFunctionType(nullptr, Node); - return nullptr; - case Node::Kind::FunctionType: - case Node::Kind::UncurriedFunctionType: printFunctionType(nullptr, Node); return nullptr; case Node::Kind::ArgumentTuple: @@ -1881,21 +1891,6 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { case Node::Kind::DynamicSelf: Printer << "Self"; return nullptr; - case Node::Kind::CFunctionPointer: { - Printer << "@convention(c) "; - printFunctionType(nullptr, Node); - return nullptr; - } - case Node::Kind::ObjCBlock: { - Printer << "@convention(block) "; - printFunctionType(nullptr, Node); - return nullptr; - } - case Node::Kind::EscapingObjCBlock: { - Printer << "@escaping @convention(block) "; - printFunctionType(nullptr, Node); - return nullptr; - } case Node::Kind::SILBoxType: { Printer << "@box "; NodePointer type = Node->getChild(0); @@ -2648,14 +2643,6 @@ void NodePrinter::printEntityType(NodePointer Entity, NodePointer type, Printer << ' '; type = dependentType->getFirstChild(); } - if (type->getKind() == Node::Kind::DifferentiableFunctionType) - Printer << "@differentiable "; - else if (type->getKind() == Node::Kind::EscapingDifferentiableFunctionType) - Printer << "@escaping @differentiable "; - else if (type->getKind() == Node::Kind::LinearFunctionType) - Printer << "@differentiable(linear) "; - else if (type->getKind() == Node::Kind::EscapingLinearFunctionType) - Printer << "@escaping @differentiable(linear) "; printFunctionType(labelList, type); } else { print(type); diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index c0326ef954197..294e7a08a34c2 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -357,8 +357,8 @@ $s3red4testyAA3ResOyxSayq_GAEs5ErrorAAq_sAFHD1__HCg_GADyxq_GsAFR_r0_lF ---> red. $s3red4testyAA7OurTypeOy4them05TheirD0Vy5AssocQzGAjE0F8ProtocolAAxAA0c7DerivedH0HD1_AA0c4BaseH0HI1_AieKHA2__HCg_GxmAaLRzlF ---> red.test(A.Type) -> red.OurType> $s17property_wrappers10WithTuplesV9fractionsSd_S2dtvpfP ---> property wrapper backing initializer of property_wrappers.WithTuples.fractions : (Swift.Double, Swift.Double, Swift.Double) $sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTOTA ---> {T:$sSo17OS_dispatch_queueC4sync7executeyyyXE_tFTO} partial apply forwarder for @nonobjc __C.OS_dispatch_queue.sync(execute: () -> ()) -> () -$s4main1gyySiXCvp ---> main.g : (Swift.Int) -> () -$s4main1gyySiXBvp ---> main.g : (Swift.Int) -> () +$s4main1gyySiXCvp ---> main.g : @convention(c) (Swift.Int) -> () +$s4main1gyySiXBvp ---> main.g : @convention(block) (Swift.Int) -> () $sxq_Idgnr_D ---> @differentiable @callee_guaranteed (@in_guaranteed A) -> (@out B) $sxq_Ilgnr_D ---> @differentiable(linear) @callee_guaranteed (@in_guaranteed A) -> (@out B) $sS3fIedgyywd_D ---> @escaping @differentiable @callee_guaranteed (@unowned Swift.Float, @unowned @noDerivative Swift.Float) -> (@unowned Swift.Float) From 34996bf9a6b58127c3e4a706329c53be3ba6dd0f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 21:32:27 -0700 Subject: [PATCH 10/20] [Concurrency] Give Actor enqueue(partialTask:) its own ptrauth discriminator. For actor class's implementations of `enqueue(partialTask:)`, use a fixed ptrauth discriminator. Paired with the fixed vtable location, this allows one to invoke this operation on any native actor instance without knowing the specific type. --- include/swift/ABI/MetadataValues.h | 3 +++ lib/IRGen/GenPointerAuth.cpp | 11 +++++++++++ test/IRGen/ptrauth-actor.swift | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 test/IRGen/ptrauth-actor.swift diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 54232972ca9fd..e45629dce240a 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1177,6 +1177,9 @@ namespace SpecialPointerAuthDiscriminators { /// Resilient class stub initializer callback const uint16_t ResilientClassStubInitCallback = 0xC671; + + /// Actor enqueue(partialTask:). + const uint16_t ActorEnqueuePartialTask = 0x8f3d; } /// The number of arguments that will be passed directly to a generic diff --git a/lib/IRGen/GenPointerAuth.cpp b/lib/IRGen/GenPointerAuth.cpp index fa6821b267900..6f2a7d2c17fc7 100644 --- a/lib/IRGen/GenPointerAuth.cpp +++ b/lib/IRGen/GenPointerAuth.cpp @@ -400,6 +400,17 @@ PointerAuthEntity::getDeclDiscriminator(IRGenModule &IGM) const { assert(!constant.isForeign && "discriminator for foreign declaration not supported yet!"); + // Special case: methods that are witnesses to Actor.enqueue(partialTask:) + // have their own descriminator, which is shared across all actor classes. + if (constant.hasFuncDecl()) { + auto func = dyn_cast(constant.getFuncDecl()); + if (func->isActorEnqueuePartialTaskWitness()) { + cache = IGM.getSize( + Size(SpecialPointerAuthDiscriminators::ActorEnqueuePartialTask)); + return cache; + } + } + auto mangling = constant.mangle(); cache = getDiscriminatorForString(IGM, mangling); return cache; diff --git a/test/IRGen/ptrauth-actor.swift b/test/IRGen/ptrauth-actor.swift new file mode 100644 index 0000000000000..4cd1f44ccb567 --- /dev/null +++ b/test/IRGen/ptrauth-actor.swift @@ -0,0 +1,24 @@ +// RUN: %swift -swift-version 5 -target arm64e-apple-macos11.0 -parse-stdlib %s -emit-ir -disable-llvm-optzns -enable-experimental-concurrency -o - | %FileCheck %s + +// REQUIRES: CODEGENERATOR=ARM +// REQUIRES: concurrency +// REQUIRES: CPU=arm64e +// REQUIRES: OS=macosx + +import _Concurrency +import Swift + +public actor class A1 { + var x: Int = 17 +} + +open actor class A3: Actor { + open func f() { } +} + +// enqueue(partialTask:) has the same discriminator across all classes. +// CHECK: s4main2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncE0V_tF.ptrauth{{.*}}i64 36669 + +// CHECK: @"$s4main2A3CyxG12_Concurrency5ActorAaeFP7enqueue11partialTaskyAE012PartialAsyncG0V_tFTW" +// CHECK-NOT: ret void +// CHECK: call i64 @llvm.ptrauth.blend.i64(i64 %{{[0-9]+}}, i64 36669) From e5c1491c6a77a21c9a6c2465de782d4777e31903 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 22:11:59 -0700 Subject: [PATCH 11/20] [Concurrency] Ban actor-isolated operations from being @objc. Actor-isolated operations must not be directly accessible from anywhere that is not already guaranteed to be running within the actor context. Prevent such operations from being `@objc`, because that would allow Objective-C code to violate actor isolation. --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/TypeCheckDeclObjC.cpp | 39 +++++++++++++++++++++++++-- test/attr/attr_objc_async.swift | 29 ++++++++++++++++++-- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f353c230f813d..6bd1a32a6eb76 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4101,6 +4101,9 @@ ERROR(not_objc_function_async,none, "'async' function cannot be represented in Objective-C", ()) NOTE(not_objc_function_type_async,none, "'async' function types cannot be represented in Objective-C", ()) +ERROR(actor_isolated_objc,none, + "actor-isolated %0 %1 cannot be @objc", + (DescriptiveDeclKind, DeclName)) NOTE(protocol_witness_async_conflict,none, "candidate is %select{not |}0'async', but protocol requirement is%select{| not}0", (bool)) diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 1d99cc1b9cc91..2659e564d6d92 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "TypeCheckObjC.h" #include "TypeChecker.h" +#include "TypeCheckConcurrency.h" #include "TypeCheckProtocol.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" @@ -368,6 +369,32 @@ static bool checkObjCInForeignClassContext(const ValueDecl *VD, return true; } +/// Actor-isolated declarations cannot be @objc. +static bool checkObjCActorIsolation(const ValueDecl *VD, + ObjCReason Reason) { + // Check actor isolation. + bool Diagnose = shouldDiagnoseObjCReason(Reason, VD->getASTContext()); + + switch (getActorIsolation(const_cast(VD))) { + case ActorIsolation::ActorInstance: + // Actor-isolated functions cannot be @objc. + if (Diagnose) { + VD->diagnose( + diag::actor_isolated_objc, VD->getDescriptiveKind(), VD->getName()); + describeObjCReason(VD, Reason); + if (auto FD = dyn_cast(VD)) { + addAsyncNotes(const_cast(FD)); + } + } + return true; + + case ActorIsolation::ActorPrivileged: + case ActorIsolation::Independent: + case ActorIsolation::Unspecified: + return false; + } +} + static VersionRange getMinOSVersionForClassStubs(const llvm::Triple &target) { if (target.isMacOSX()) return VersionRange::allGTE(llvm::VersionTuple(10, 15, 0)); @@ -512,6 +539,8 @@ bool swift::isRepresentableInObjC( return false; if (checkObjCInExtensionContext(AFD, Diagnose)) return false; + if (checkObjCActorIsolation(AFD, Reason)) + return false; if (AFD->isOperator()) { AFD->diagnose((isa(AFD->getDeclContext()) @@ -686,10 +715,12 @@ bool swift::isRepresentableInObjC( Type resultType = FD->mapTypeIntoContext(FD->getResultInterfaceType()); if (auto tupleType = resultType->getAs()) { for (const auto &tupleElt : tupleType->getElements()) { - addCompletionHandlerParam(tupleElt.getType()); + if (addCompletionHandlerParam(tupleElt.getType())) + return false; } } else { - addCompletionHandlerParam(resultType); + if (addCompletionHandlerParam(resultType)) + return false; } // For a throwing asynchronous function, an Error? parameter is added @@ -939,6 +970,8 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { if (checkObjCInForeignClassContext(VD, Reason)) return false; + if (checkObjCActorIsolation(VD, Reason)) + return false; if (!Diagnose || Result) return Result; @@ -967,6 +1000,8 @@ bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { return false; if (checkObjCWithGenericParams(SD, Reason)) return false; + if (checkObjCActorIsolation(SD, Reason)) + return false; // ObjC doesn't support class subscripts. if (!SD->isInstanceMember()) { diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index a3a2aad0c459d..fb8e571dc9a09 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -3,10 +3,11 @@ // RUN: not %target-swift-frontend -typecheck -dump-ast -disable-objc-attr-requires-foundation-module %s -swift-version 5 -enable-source-import -I %S/Inputs -enable-experimental-concurrency > %t.ast // RUN: %FileCheck -check-prefix CHECK-DUMP %s < %t.ast // REQUIRES: objc_interop +// REQUIRES: concurrency import Foundation -// CHECK: class Concurrency -class Concurrency { +// CHECK: class MyClass +class MyClass { // CHECK: @objc func doBigJob() async -> Int // CHECK-DUMP: func_decl{{.*}}doBigJob{{.*}}foreign_async=@convention(block) (Int) -> (),completion_handler_param=0 @objc func doBigJob() async -> Int { return 0 } @@ -21,3 +22,27 @@ class Concurrency { @objc class func createAsynchronously() async -> Self? { nil } // expected-error@-1{{asynchronous method returning 'Self' cannot be '@objc'}} } + +// Actor class exporting Objective-C entry points. + +// CHECK: class MyActor +actor class MyActor { + // CHECK: @objc func doBigJob() async -> Int + // CHECK-DUMP: func_decl{{.*}}doBigJob{{.*}}foreign_async=@convention(block) (Int) -> (),completion_handler_param=0 + @objc func doBigJob() async -> Int { return 0 } + + // CHECK: @objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int) + // CHECK-DUMP: func_decl{{.*}}doBigJobOrFail{{.*}}foreign_async=@convention(block) (Optional, Int, Optional) -> (),completion_handler_param=1,error_param=2 + @objc func doBigJobOrFail(_: Int) async throws -> (AnyObject, Int) { return (self, 0) } + + // Actor-isolated entities cannot be exposed to Objective-C. + @objc func synchronousBad() { } // expected-error{{actor-isolated instance method 'synchronousBad()' cannot be @objc}} + // expected-note@-1{{add 'async' to function 'synchronousBad()' to make it asynchronous}} + // expected-note@-2{{add '@asyncHandler' to function 'synchronousBad()' to create an implicit asynchronous context}} + + @objc var badProp: AnyObject { self } // expected-error{{actor-isolated property 'badProp' cannot be @objc}} + @objc subscript(index: Int) -> AnyObject { self } // expected-error{{actor-isolated subscript 'subscript(_:)' cannot be @objc}} + + // CHECK: @objc @actorIndependent func synchronousGood() + @objc @actorIndependent func synchronousGood() { } +} From 1e040ac3235c7cb0d68a41460aafcf6ffd76ea6c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 22:34:15 -0700 Subject: [PATCH 12/20] [Concurrency] Note that 'actor' is a decl modifier, not an attribute. --- include/swift/AST/Attr.def | 2 +- test/decl/class/actor/noconcurrency.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 93d8308bb34cc..bd0af44538512 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -567,7 +567,7 @@ SIMPLE_DECL_ATTR(asyncHandler, AsyncHandler, 101) CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, - OnClass | ConcurrencyOnly | + DeclModifier | OnClass | ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, 102) diff --git a/test/decl/class/actor/noconcurrency.swift b/test/decl/class/actor/noconcurrency.swift index 48e4c7d018bc5..2773bafa44d6f 100644 --- a/test/decl/class/actor/noconcurrency.swift +++ b/test/decl/class/actor/noconcurrency.swift @@ -1,4 +1,4 @@ // RUN: %target-typecheck-verify-swift -actor class C { } // expected-error{{'actor' attribute is only valid when experimental concurrency is enabled}} +actor class C { } // expected-error{{'actor' modifier is only valid when experimental concurrency is enabled}} From 72bf83653a8d4af599538ac53af0e7dd85c24722 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 29 Sep 2020 22:34:27 -0700 Subject: [PATCH 13/20] [Concurrency] Allow actor classes to inherit from NSObject. NSObject is guaranteed to have no state and no Swift vtable, and is necessary for Swift classes to implement the NSObject protocol. Allow it (and only it) as the superclass of an actor class, so that actor classes can be exposed to Objective-C. --- lib/Sema/TypeCheckConcurrency.cpp | 8 ++++++++ test/attr/attr_objc_async.swift | 3 +++ 2 files changed, 11 insertions(+) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index b5c19ab93c570..a7543e8265f6e 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -180,6 +180,14 @@ bool IsActorRequest::evaluate( if (superclassDecl->isActor()) return true; + // The superclass is 'NSObject', which is known to have no state and no + // superclass. + if (superclassDecl->hasClangNode() && + superclassDecl->getName().is("NSObject") && + superclassDecl->getModuleContext()->getName().is("ObjectiveC") && + actorAttr != nullptr) + return true; + // This class cannot be an actor; complain if the 'actor' modifier was // provided. if (actorAttr) { diff --git a/test/attr/attr_objc_async.swift b/test/attr/attr_objc_async.swift index fb8e571dc9a09..59300a20cdb09 100644 --- a/test/attr/attr_objc_async.swift +++ b/test/attr/attr_objc_async.swift @@ -46,3 +46,6 @@ actor class MyActor { // CHECK: @objc @actorIndependent func synchronousGood() @objc @actorIndependent func synchronousGood() { } } + +// CHECK: @objc actor class MyObjCActor +@objc actor class MyObjCActor: NSObject { } From 28220ff259b1d796ac7d9fdd3a53654cab847fe9 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 28 Sep 2020 18:20:01 +0200 Subject: [PATCH 14/20] SILCombine: make the alloc_stack-enum optimization a bit more tolerant. When eliminating an enum from an alloc_stack, accept "dead" inject_enum_addr instructions, which inject different enum cases. --- .../SILCombiner/SILCombinerMiscVisitors.cpp | 51 +++++++++++++++--- ...cast_optimizer_conditional_conformance.sil | 2 - test/SILOptimizer/sil_combine_enums.sil | 54 +++++++++++++++++++ 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index d47dca47efee1..144c1d30a04e7 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -453,6 +453,10 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { return false; EnumElementDecl *element = nullptr; + unsigned numInits =0; + unsigned numTakes = 0; + SILBasicBlock *initBlock = nullptr; + SILBasicBlock *takeBlock = nullptr; SILType payloadType; // First step: check if the stack location is only used to hold one specific @@ -463,6 +467,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { case SILInstructionKind::DebugValueAddrInst: case SILInstructionKind::DestroyAddrInst: case SILInstructionKind::DeallocStackInst: + case SILInstructionKind::InjectEnumAddrInst: + // We'll check init_enum_addr below. break; case SILInstructionKind::InitEnumDataAddrInst: { auto *ieda = cast(user); @@ -472,13 +478,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { element = el; assert(!payloadType || payloadType == ieda->getType()); payloadType = ieda->getType(); - break; - } - case SILInstructionKind::InjectEnumAddrInst: { - auto *el = cast(user)->getElement(); - if (element && el != element) - return false; - element = el; + numInits++; + initBlock = user->getParent(); break; } case SILInstructionKind::UncheckedTakeEnumDataAddrInst: { @@ -486,6 +487,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { if (element && el != element) return false; element = el; + numTakes++; + takeBlock = user->getParent(); break; } default: @@ -495,6 +498,24 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { if (!element || !payloadType) return false; + // If the enum has a single init-take pair in a single block, we know that + // the enum cannot contain any valid payload outside that init-take pair. + // + // This also means that we can ignore any inject_enum_addr of another enum + // case, because this can only inject a case without a payload. + bool singleInitTakePair = + (numInits == 1 && numTakes == 1 && initBlock == takeBlock); + if (!singleInitTakePair) { + // No single init-take pair: We cannot ignore inject_enum_addrs with a + // mismatching case. + for (auto *use : AS->getUses()) { + if (auto *inject = dyn_cast(use->getUser())) { + if (inject->getElement() != element) + return false; + } + } + } + // Second step: replace the enum alloc_stack with a payload alloc_stack. auto *newAlloc = Builder.createAllocStack( AS->getLoc(), payloadType, AS->getVarInfo(), AS->hasDynamicLifetime()); @@ -508,6 +529,22 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) { eraseInstFromFunction(*user); break; case SILInstructionKind::DestroyAddrInst: + if (singleInitTakePair) { + // It's not possible that the enum has a payload at the destroy_addr, + // because it must have already been taken by the take of the + // single init-take pair. + // We _have_ to remove the destroy_addr, because we also remove all + // inject_enum_addrs which might inject a payload-less case before + // the destroy_addr. + eraseInstFromFunction(*user); + } else { + // The enum payload can still be valid at the destroy_addr, so we have + // to keep the destroy_addr. Just replace the enum with the payload + // (and because it's not a singleInitTakePair, we can be sure that the + // enum cannot have any other case than the payload case). + use->set(newAlloc); + } + break; case SILInstructionKind::DeallocStackInst: use->set(newAlloc); break; diff --git a/test/SILOptimizer/cast_optimizer_conditional_conformance.sil b/test/SILOptimizer/cast_optimizer_conditional_conformance.sil index 87688a532e593..355dc0d053251 100644 --- a/test/SILOptimizer/cast_optimizer_conditional_conformance.sil +++ b/test/SILOptimizer/cast_optimizer_conditional_conformance.sil @@ -98,8 +98,6 @@ bb6: // CHECK: [[IEDA:%.*]] = init_enum_data_addr [[OPT]] : $*Optional, #Optional.some!enumelt // CHECK: checked_cast_addr_br take_always S1 in [[VAL]] : $*S1 to HasFoo in [[IEDA]] : $*HasFoo, bb1, bb2 // bbs... -// CHECK: switch_enum_addr [[OPT]] : $*Optional, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5 -// bbs... // CHECK: [[UNWRAP:%.*]] = unchecked_take_enum_data_addr [[OPT]] : $*Optional, #Optional.some!enumelt // CHECK: copy_addr [take] [[UNWRAP]] to [initialization] [[EXIS]] : $*HasFoo // CHECK: [[OPEN:%.*]] = open_existential_addr immutable_access [[EXIS]] : $*HasFoo to $*@opened("4E16CBC0-FD9F-11E8-A311-D0817AD9F6DD") HasFoo diff --git a/test/SILOptimizer/sil_combine_enums.sil b/test/SILOptimizer/sil_combine_enums.sil index 03378b8025f95..89a82f48777d8 100644 --- a/test/SILOptimizer/sil_combine_enums.sil +++ b/test/SILOptimizer/sil_combine_enums.sil @@ -504,6 +504,7 @@ enum MP { } sil @take_s : $@convention(thin) (@in S) -> () +sil @take_t : $@convention(thin) (@in T) -> () sil @use_mp : $@convention(thin) (@in_guaranteed MP) -> () // CHECK-LABEL: sil @expand_alloc_stack_of_enum1 @@ -568,6 +569,37 @@ bb3: return %11 : $() } +// CHECK-LABEL: sil @expand_alloc_stack_of_enum_multiple_cases +// CHECK: [[A:%[0-9]+]] = alloc_stack $T +// CHECK: bb1: +// CHECK-NEXT: store %0 to [[A]] +// CHECK: apply {{%[0-9]+}}([[A]]) +// CHECK: bb2: +// CHECK-NEXT: br bb3 +// CHECK: bb3: +// CHECK: } // end sil function 'expand_alloc_stack_of_enum_multiple_cases' +sil @expand_alloc_stack_of_enum_multiple_cases : $@convention(method) (T) -> () { +bb0(%0 : $T): + %1 = alloc_stack $X + cond_br undef, bb1, bb2 +bb1: + %2 = init_enum_data_addr %1 : $*X, #X.some!enumelt + store %0 to %2 : $*T + inject_enum_addr %1 : $*X, #X.some!enumelt + %7 = unchecked_take_enum_data_addr %1 : $*X, #X.some!enumelt + %8 = function_ref @take_t : $@convention(thin) (@in T) -> () + %9 = apply %8(%7) : $@convention(thin) (@in T) -> () + br bb3 +bb2: + inject_enum_addr %1 : $*X, #X.none!enumelt + destroy_addr %1 : $*X + br bb3 +bb3: + dealloc_stack %1 : $*X + %11 = tuple () + return %11 : $() +} + // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases // CHECK: alloc_stack $MP // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases' @@ -592,6 +624,28 @@ bb3: return %11 : $() } +// CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases2 +// CHECK: alloc_stack $X +// CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases2' +sil @dont_expand_alloc_stack_of_enum_multiple_cases2 : $@convention(method) (T) -> () { +bb0(%0 : $T): + %1 = alloc_stack $X + cond_br undef, bb1, bb2 +bb1: + %2 = init_enum_data_addr %1 : $*X, #X.some!enumelt + store %0 to %2 : $*T + inject_enum_addr %1 : $*X, #X.some!enumelt + br bb3 +bb2: + inject_enum_addr %1 : $*X, #X.none!enumelt + br bb3 +bb3: + destroy_addr %1 : $*X + dealloc_stack %1 : $*X + %11 = tuple () + return %11 : $() +} + // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_unknown_use // CHECK: alloc_stack $MP // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_unknown_use' From 0a71d0fbea7cafd35d35bf2a4c0bd2be73a36e47 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 28 Sep 2020 18:23:12 +0200 Subject: [PATCH 15/20] SimplifyCFG: allow jump-threading for switch_enum_data_addr instructions. If the branch-block injects a certain enum case and the destination switches on that enum, it's worth jump threading. E.g. inject_enum_addr %enum : $*Optional, #Optional.some ... // no memory writes here br DestBB DestBB: ... // no memory writes here switch_enum_addr %enum : $*Optional, case #Optional.some ... This enables removing all code with optionals in a loop, which iterates over an array of address-only elements, e.g. func test(_ items: [T]) { for i in items { print(i) } } --- lib/SILOptimizer/Transforms/SimplifyCFG.cpp | 74 +++++++++++++++-- test/SILOptimizer/enum_jump_thread.sil | 80 +++++++++++++++++++ test/SILOptimizer/generic_loop.swift | 17 ++++ .../sil_combine_concrete_existential.swift | 7 +- 4 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 test/SILOptimizer/generic_loop.swift diff --git a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp index 44ee8e01f3998..176588416d0dd 100644 --- a/lib/SILOptimizer/Transforms/SimplifyCFG.cpp +++ b/lib/SILOptimizer/Transforms/SimplifyCFG.cpp @@ -916,16 +916,57 @@ static bool couldRemoveRelease(SILBasicBlock *SrcBB, SILValue SrcV, return IsReleaseOfDest; } +/// Returns true if any instruction in \p block may write memory. +static bool blockMayWriteMemory(SILBasicBlock *block) { + for (auto instAndIdx : llvm::enumerate(*block)) { + if (instAndIdx.value().mayWriteToMemory()) + return true; + // Only look at the first 20 instructions to avoid compile time problems for + // corner cases of very large blocks without memory writes. + // 20 instructions is more than enough. + if (instAndIdx.index() > 50) + return true; + } + return false; +} + +// Returns true if \p block contains an injected an enum case into \p enumAddr +// which is valid at the end of the block. +static bool hasInjectedEnumAtEndOfBlock(SILBasicBlock *block, SILValue enumAddr) { + for (auto instAndIdx : llvm::enumerate(llvm::reverse(*block))) { + SILInstruction &inst = instAndIdx.value(); + if (auto *injectInst = dyn_cast(&inst)) { + return injectInst->getOperand() == enumAddr; + } + if (inst.mayWriteToMemory()) + return false; + // Only look at the first 20 instructions to avoid compile time problems for + // corner cases of very large blocks without memory writes. + // 20 instructions is more than enough. + if (instAndIdx.index() > 50) + return false; + } + return false; +} + /// tryJumpThreading - Check to see if it looks profitable to duplicate the /// destination of an unconditional jump into the bottom of this block. bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { auto *DestBB = BI->getDestBB(); auto *SrcBB = BI->getParent(); + TermInst *destTerminator = DestBB->getTerminator(); // If the destination block ends with a return, we don't want to duplicate it. // We want to maintain the canonical form of a single return where possible. - if (DestBB->getTerminator()->isFunctionExiting()) + if (destTerminator->isFunctionExiting()) return false; + // Jump threading only makes sense if there is an argument on the branch + // (which is reacted on in the DestBB), or if this goes through a memory + // location (switch_enum_addr is the only adress-instruction which we + // currently handle). + if (BI->getArgs().empty() && !isa(destTerminator)) + return false; + // We don't have a great cost model at the SIL level, so we don't want to // blissly duplicate tons of code with a goal of improved performance (we'll // leave that to LLVM). However, doing limited code duplication can lead to @@ -956,11 +997,29 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { } } - if (ThreadingBudget == 0 && isa(DestBB->getTerminator())) { - for (auto V : BI->getArgs()) { - if (isa(V) || isa(V)) { + if (ThreadingBudget == 0) { + if (isa(destTerminator)) { + for (auto V : BI->getArgs()) { + if (isa(V) || isa(V)) { + ThreadingBudget = 4; + break; + } + } + } else if (auto *SEA = dyn_cast(destTerminator)) { + // If the branch-block injects a certain enum case and the destination + // switches on that enum, it's worth jump threading. E.g. + // + // inject_enum_addr %enum : $*Optional, #Optional.some + // ... // no memory writes here + // br DestBB + // DestBB: + // ... // no memory writes here + // switch_enum_addr %enum : $*Optional, case #Optional.some ... + // + SILValue enumAddr = SEA->getOperand(); + if (!blockMayWriteMemory(DestBB) && + hasInjectedEnumAtEndOfBlock(SrcBB, enumAddr)) { ThreadingBudget = 4; - break; } } } @@ -976,7 +1035,7 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) { // control flow. Still, we make an exception for switch_enum. bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0); if (DestIsLoopHeader) { - if (!isa(DestBB->getTerminator())) + if (!isa(destTerminator)) return false; } @@ -1286,8 +1345,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) { // If this unconditional branch has BBArgs, check to see if duplicating the // destination would allow it to be simplified. This is a simple form of jump // threading. - if (!isVeryLargeFunction && !BI->getArgs().empty() && - tryJumpThreading(BI)) + if (!isVeryLargeFunction && tryJumpThreading(BI)) return true; return Simplified; diff --git a/test/SILOptimizer/enum_jump_thread.sil b/test/SILOptimizer/enum_jump_thread.sil index 22d76c7a9142f..3ce09bbe2112a 100644 --- a/test/SILOptimizer/enum_jump_thread.sil +++ b/test/SILOptimizer/enum_jump_thread.sil @@ -49,3 +49,83 @@ bb5(%7 : $E2): return %7 : $E2 } +// CHECK-LABEL: sil @test_enum_addr +sil @test_enum_addr : $@convention(thin) () -> Builtin.Int32 { +bb0: + %2 = alloc_stack $E + cond_br undef, bb1, bb2 + +// CHECK: bb1: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: switch_enum_addr +bb1: + inject_enum_addr %2 : $*E, #E.A!enumelt + br bb3 + +// CHECK: bb2: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: switch_enum_addr +bb2: + inject_enum_addr %2 : $*E, #E.B!enumelt + br bb3 + +bb3: + switch_enum_addr %2 : $*E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5 + +bb4: + %10 = integer_literal $Builtin.Int32, 1 + br bb6(%10 : $Builtin.Int32) + +bb5: + %11 = integer_literal $Builtin.Int32, 2 + br bb6(%11 : $Builtin.Int32) + +bb6(%12 : $Builtin.Int32): + dealloc_stack %2 : $*E + return %12 : $Builtin.Int32 +// CHECK: } // end sil function 'test_enum_addr' +} + +// CHECK-LABEL: sil @dont_jumpthread_enum_addr +sil @dont_jumpthread_enum_addr : $@convention(thin) (E) -> Builtin.Int32 { +bb0(%0 : $E): + %2 = alloc_stack $E + %3 = alloc_stack $E + cond_br undef, bb1, bb2 + +// CHECK: bb1: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: store +// CHECK-NEXT: br bb3 +bb1: + inject_enum_addr %2 : $*E, #E.A!enumelt + store %0 to %2 : $*E + br bb3 + +// CHECK: bb2: +// CHECK-NEXT: inject_enum_addr +// CHECK-NEXT: br bb3 +bb2: + inject_enum_addr %3 : $*E, #E.A!enumelt + br bb3 + +// CHECK: bb3: +// CHECK-NEXT: switch_enum_addr +bb3: + switch_enum_addr %2 : $*E, case #E.A!enumelt: bb4, case #E.B!enumelt: bb5 + +bb4: + %10 = integer_literal $Builtin.Int32, 1 + br bb6(%10 : $Builtin.Int32) + +bb5: + %11 = integer_literal $Builtin.Int32, 2 + br bb6(%11 : $Builtin.Int32) + +bb6(%12 : $Builtin.Int32): + dealloc_stack %3 : $*E + dealloc_stack %2 : $*E + return %12 : $Builtin.Int32 +// CHECK: } // end sil function 'dont_jumpthread_enum_addr' +} + diff --git a/test/SILOptimizer/generic_loop.swift b/test/SILOptimizer/generic_loop.swift new file mode 100644 index 0000000000000..55facdb5ab61f --- /dev/null +++ b/test/SILOptimizer/generic_loop.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -primary-file %s -O -sil-verify-all -module-name=test -emit-sil | %FileCheck %s + +// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib + + +// Check that we can eliminate all optionals from a loop which is iterating +// over an array of address-only elements. + +// CHECK-LABEL: sil @$s4test0A18_no_optionals_usedyySayxGlF : $@convention(thin) (@guaranteed Array) -> () { +// CHECK-NOT: Optional +// CHECK: } // end sil function '$s4test0A18_no_optionals_usedyySayxGlF' +public func test_no_optionals_used(_ items: [T]) { + for i in items { + print(i) + } +} + diff --git a/test/SILOptimizer/sil_combine_concrete_existential.swift b/test/SILOptimizer/sil_combine_concrete_existential.swift index 3fe0399faa9d7..5a840d7ffbc67 100644 --- a/test/SILOptimizer/sil_combine_concrete_existential.swift +++ b/test/SILOptimizer/sil_combine_concrete_existential.swift @@ -95,10 +95,11 @@ struct SS: PPP { // The first apply has been devirtualized and inlined. The second remains unspecialized. // CHECK-LABEL: sil @$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF : $@convention(thin) () -> () { +// CHECK: [[O1:%.*]] = open_existential_addr immutable_access %{{.*}} : $*PPP to $*@opened("{{.*}}") PPP // CHECK: switch_enum_addr %{{.*}} : $*Optional<@opened("{{.*}}") PPP>, case #Optional.some!enumelt: bb{{.*}}, case #Optional.none!enumelt: bb{{.*}} -// CHECK: [[O:%.*]] = open_existential_addr immutable_access %{{.*}} : $*PPP to $*@opened("{{.*}}") PPP -// CHECK: [[W:%.*]] = witness_method $@opened("{{.*}}") PPP, #PPP.returnsOptionalIndirect : (Self) -> () -> Self?, [[O]] : $*@opened("{{.*}}") PPP : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> -// CHECK: apply [[W]]<@opened("{{.*}}") PPP>(%{{.*}}, [[O]]) : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> +// CHECK: [[O2:%.*]] = open_existential_addr immutable_access %{{.*}} : $*PPP to $*@opened("{{.*}}") PPP +// CHECK: [[W:%.*]] = witness_method $@opened("{{.*}}") PPP, #PPP.returnsOptionalIndirect : (Self) -> () -> Self?, [[O1]] : $*@opened("{{.*}}") PPP : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> +// CHECK: apply [[W]]<@opened("{{.*}}") PPP>(%{{.*}}, [[O2]]) : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> // CHECK-LABEL: } // end sil function '$s32sil_combine_concrete_existential37testWitnessReturnOptionalIndirectSelfyyF' public func testWitnessReturnOptionalIndirectSelf() { let p: PPP = S() From 592a5e1ce3e202c0d3d17aab8e5e2ce4c4e7c628 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 30 Sep 2020 08:07:28 -0700 Subject: [PATCH 16/20] [Concurrency] Actor is not an attribute --- test/IDE/complete_decl_attribute.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 752ba9e08b5f1..23606dacfa21e 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -3,7 +3,6 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD2 | %FileCheck %s -check-prefix=KEYWORD2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3 | %FileCheck %s -check-prefix=KEYWORD3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3_2 | %FileCheck %s -check-prefix=KEYWORD3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD3 -enable-experimental-concurrency | %FileCheck %s -check-prefix=KEYWORD3_ASYNC // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD4 | %FileCheck %s -check-prefix=KEYWORD4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=KEYWORD5 | %FileCheck %s -check-prefix=KEYWORD5 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ON_GLOBALVAR | %FileCheck %s -check-prefix=ON_GLOBALVAR @@ -92,8 +91,6 @@ struct MyStruct {} // KEYWORD3-NEXT: Keyword/None: propertyWrapper[#Class Attribute#]; name=propertyWrapper // KEYWORD3-NEXT: End completions -// KEYWORD3_ASYNC: Keyword/None: actor[#Class Attribute#]; name=actor - @#^KEYWORD3_2^#IB class C2 {} // Same as KEYWORD3. From 9b9e46b63a0bb47c926b5e7ff86366d0f4c3e993 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 30 Sep 2020 11:41:49 -0500 Subject: [PATCH 17/20] [templvalueopt] Fix up tests to be more robust. --- test/SILOptimizer/templvalueopt.sil | 20 ++++++++++---------- test/SILOptimizer/templvalueopt_ossa.sil | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/test/SILOptimizer/templvalueopt.sil b/test/SILOptimizer/templvalueopt.sil index 81f57e2d4da75..42b6d63ed236a 100644 --- a/test/SILOptimizer/templvalueopt.sil +++ b/test/SILOptimizer/templvalueopt.sil @@ -3,7 +3,7 @@ import Swift import Builtin -// CHECK-LABEL: sil @test_enum_with_initialize +// CHECK-LABEL: sil @test_enum_with_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -22,7 +22,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil @test_enum_without_initialize +// CHECK-LABEL: sil @test_enum_without_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr %0 // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 @@ -48,7 +48,7 @@ struct ConformingStruct : Proto { @_hasStorage let a: Any } -// CHECK-LABEL: sil @test_existential +// CHECK-LABEL: sil @test_existential : // CHECK: bb0(%0 : $*Proto, %1 : $*ConformingStruct): // CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -80,7 +80,7 @@ struct AddressOnlyPayload { // There should only be a single copy_addr after optimization. // -// CHECK-LABEL: sil @test_initialize_struct +// CHECK-LABEL: sil @test_initialize_struct : // CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 // CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] @@ -114,7 +114,7 @@ bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): return %20 : $() } -// CHECK-LABEL: sil @bail_on_write_to_dest +// CHECK-LABEL: sil @bail_on_write_to_dest : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -132,7 +132,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil @write_to_dest_ok_if_before_liferange +// CHECK-LABEL: sil @write_to_dest_ok_if_before_liferange : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr // CHECK-NEXT: init_enum_data_addr @@ -161,7 +161,7 @@ struct StructWithEnum : Proto { @_hasStorage let e: Enum } -// CHECK-LABEL: sil @move_projections +// CHECK-LABEL: sil @move_projections : // CHECK: bb0(%0 : $*Proto, %1 : $*Any): // CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*Proto, $StructWithEnum // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e @@ -188,7 +188,7 @@ bb0(%0 : $*Proto, %1 : $*Any): return %10 : $() } -// CHECK-LABEL: sil @cant_move_projections +// CHECK-LABEL: sil @cant_move_projections : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: load @@ -210,7 +210,7 @@ bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): sil @init_optional : $@convention(thin) () -> @out Optional -// CHECK-LABEL: sil @instructions_after_copy_addr +// CHECK-LABEL: sil @instructions_after_copy_addr : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -231,7 +231,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil @dont_optimize_swap +// CHECK-LABEL: sil @dont_optimize_swap : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr diff --git a/test/SILOptimizer/templvalueopt_ossa.sil b/test/SILOptimizer/templvalueopt_ossa.sil index 9e0a5562c5103..e37a777091b80 100644 --- a/test/SILOptimizer/templvalueopt_ossa.sil +++ b/test/SILOptimizer/templvalueopt_ossa.sil @@ -3,7 +3,7 @@ import Swift import Builtin -// CHECK-LABEL: sil [ossa] @test_enum_with_initialize +// CHECK-LABEL: sil [ossa] @test_enum_with_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -22,7 +22,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil [ossa] @test_enum_without_initialize +// CHECK-LABEL: sil [ossa] @test_enum_without_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr %0 // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 @@ -48,7 +48,7 @@ struct ConformingStruct : Proto { @_hasStorage let a: Any } -// CHECK-LABEL: sil [ossa] @test_existential +// CHECK-LABEL: sil [ossa] @test_existential : // CHECK: bb0(%0 : $*Proto, %1 : $*ConformingStruct): // CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [initialization] [[E]] @@ -80,7 +80,7 @@ struct AddressOnlyPayload { // There should only be a single copy_addr after optimization. // -// CHECK-LABEL: sil [ossa] @test_initialize_struct +// CHECK-LABEL: sil [ossa] @test_initialize_struct : // CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 // CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] @@ -114,7 +114,7 @@ bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): return %20 : $() } -// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest +// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -132,7 +132,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liferange +// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liferange : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr // CHECK-NEXT: init_enum_data_addr @@ -161,7 +161,7 @@ struct StructWithEnum : Proto { @_hasStorage let e: Enum } -// CHECK-LABEL: sil [ossa] @move_projections +// CHECK-LABEL: sil [ossa] @move_projections : // CHECK: bb0(%0 : $*Proto, %1 : $*Any): // CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*Proto, $StructWithEnum // CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e @@ -188,7 +188,7 @@ bb0(%0 : $*Proto, %1 : $*Any): return %10 : $() } -// CHECK-LABEL: sil [ossa] @cant_move_projections +// CHECK-LABEL: sil [ossa] @cant_move_projections : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: load @@ -210,7 +210,7 @@ bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): sil [ossa] @init_optional : $@convention(thin) () -> @out Optional -// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr +// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr @@ -231,7 +231,7 @@ bb0(%0 : $*Optional, %1 : $*Any): return %6 : $() } -// CHECK-LABEL: sil [ossa] @dont_optimize_swap +// CHECK-LABEL: sil [ossa] @dont_optimize_swap : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr From c3bc8e8ef93f4ad5d7eb39bb0334bc00bef8de74 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 29 Sep 2020 16:37:59 -0500 Subject: [PATCH 18/20] [ownership] Move ownership elimination on the stdlib passed lower aggregate instrs. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 8f6fcc0a52e8b..4a17701499818 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -286,13 +286,13 @@ void addFunctionPasses(SILPassPipelinePlan &P, // Optimize copies from a temporary (an "l-value") to a destination. P.addTempLValueOpt(); + // Split up opaque operations (copy_addr, retain_value, etc.). + P.addLowerAggregateInstrs(); + // We earlier eliminated ownership if we are not compiling the stdlib. Now // handle the stdlib functions. P.addNonTransparentFunctionOwnershipModelEliminator(); - // Split up opaque operations (copy_addr, retain_value, etc.). - P.addLowerAggregateInstrs(); - // Split up operations on stack-allocated aggregates (struct, tuple). if (OpLevel == OptimizationLevelKind::HighLevel) { P.addEarlySROA(); From b972bef2ce1908f40a651f4a45e95e63b4e05454 Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Wed, 30 Sep 2020 12:14:35 -0700 Subject: [PATCH 19/20] Add an implementation of Differentiation without tgmath (#34116) Addresses rdar://68471851 --- stdlib/CMakeLists.txt | 4 ++++ stdlib/public/CMakeLists.txt | 10 +++++++++- .../Differentiation_NoTgMath/CMakeLists.txt | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 stdlib/public/Differentiation_NoTgMath/CMakeLists.txt diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index 6a39ac804c01a..5f8202b406ab2 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -66,6 +66,10 @@ option(SWIFT_ENABLE_MODULE_INTERFACES "Generate .swiftinterface files alongside .swiftmodule files" "${SWIFT_STDLIB_STABLE_ABI}") +option(SWIFT_COMPILE_DIFFERENTIATION_WITHOUT_TGMATH + "Build Differentation without tgmath (and dependency on platform runtime libraries)" + FALSE) + # # End of user-configurable options. # diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 9141a0db2d466..978322b95a1b0 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -87,7 +87,15 @@ if(SWIFT_BUILD_STDLIB) add_subdirectory(SwiftOnoneSupport) if(SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING) - add_subdirectory(Differentiation) + if(SWIFT_COMPILE_DIFFERENTIATION_WITHOUT_TGMATH) + # Use a different CMakeLists.txt for this configuration + # while sharing the bulk of the code + # This way we will reduce any side effect on the main configuration + # and increase the readability of the CMake code + add_subdirectory(Differentiation_NoTgMath) + else() + add_subdirectory(Differentiation) + endif() endif() if(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) diff --git a/stdlib/public/Differentiation_NoTgMath/CMakeLists.txt b/stdlib/public/Differentiation_NoTgMath/CMakeLists.txt new file mode 100644 index 0000000000000..b27e8a207be96 --- /dev/null +++ b/stdlib/public/Differentiation_NoTgMath/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SOURCES_FOLDER ../Differentiation) + +add_swift_target_library(swift_Differentiation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + ${SOURCES_FOLDER}/Differentiable.swift + ${SOURCES_FOLDER}/DifferentialOperators.swift + ${SOURCES_FOLDER}/DifferentiationUtilities.swift + ${SOURCES_FOLDER}/AnyDifferentiable.swift + ${SOURCES_FOLDER}/ArrayDifferentiation.swift + ${SOURCES_FOLDER}/OptionalDifferentiation.swift + + GYB_SOURCES + ${SOURCES_FOLDER}/FloatingPointDifferentiation.swift.gyb + ${SOURCES_FOLDER}/SIMDDifferentiation.swift.gyb + + SWIFT_COMPILE_FLAGS + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -parse-stdlib + LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}" + DARWIN_INSTALL_NAME_DIR "@rpath" + INSTALL_IN_COMPONENT stdlib) From 4c8d09feb3e527569ea9c5ba026bb7ea59138c14 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 30 Sep 2020 16:08:06 -0500 Subject: [PATCH 20/20] [ownership] Move ownership lowering past SROA. I already updated SROA for this and we already have tests/etc. We have just been waiting on some other passes to be moved afterwards. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 4a17701499818..2c81d1bbcb646 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -289,10 +289,6 @@ void addFunctionPasses(SILPassPipelinePlan &P, // Split up opaque operations (copy_addr, retain_value, etc.). P.addLowerAggregateInstrs(); - // We earlier eliminated ownership if we are not compiling the stdlib. Now - // handle the stdlib functions. - P.addNonTransparentFunctionOwnershipModelEliminator(); - // Split up operations on stack-allocated aggregates (struct, tuple). if (OpLevel == OptimizationLevelKind::HighLevel) { P.addEarlySROA(); @@ -300,6 +296,10 @@ void addFunctionPasses(SILPassPipelinePlan &P, P.addSROA(); } + // We earlier eliminated ownership if we are not compiling the stdlib. Now + // handle the stdlib functions. + P.addNonTransparentFunctionOwnershipModelEliminator(); + // Promote stack allocations to values. P.addMem2Reg();