From 5c6721625c25bd3851121a441a526e448211c4c8 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 6 Apr 2022 16:34:31 +0900 Subject: [PATCH 01/80] [Changelog] fix formatting of recent items Without these spaces the items break out of the listing. With them they look correct and are aligned with the `*` --- CHANGELOG.md | 104 +++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b31024270c547..513f304535fa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,54 +85,54 @@ _**Note:** This is in reverse chronological order, so newer entries are added to * [SE-0343][]: -Top-level scripts support asynchronous calls. + Top-level scripts support asynchronous calls. -Using an `await` by calling an asynchronous function or accessing an isolated -variable transitions the top-level to an asynchronous context. As an -asynchronous context, top-level variables are `@MainActor`-isolated and the -top-level is run on the `@MainActor`. + Using an `await` by calling an asynchronous function or accessing an isolated + variable transitions the top-level to an asynchronous context. As an + asynchronous context, top-level variables are `@MainActor`-isolated and the + top-level is run on the `@MainActor`. -Note that the transition affects function overload resolution and starts an -implicit run loop to drive the concurrency machinery. + Note that the transition affects function overload resolution and starts an + implicit run loop to drive the concurrency machinery. -Unmodified scripts are not affected by this change unless `-warn-concurrency` is -passed to the compiler invocation. With `-warn-concurrency`, variables in the -top-level are isolated to the main actor and the top-level context is isolated -to the main actor, but is not an asynchronous context. + Unmodified scripts are not affected by this change unless `-warn-concurrency` is + passed to the compiler invocation. With `-warn-concurrency`, variables in the + top-level are isolated to the main actor and the top-level context is isolated + to the main actor, but is not an asynchronous context. * [SE-0336][]: -It is now possible to declare `distributed actor` and `distributed func`s inside of them. + It is now possible to declare `distributed actor` and `distributed func`s inside of them. -Distributed actors provide stronger isolation guarantees than "local" actors, and enable additional checks to be made on return types and parameters of distributed methods, e.g. checking if they conform to `Codable`. Distributed methods can be called on "remote" references of distributed actors, turning those invocations into remote procedure calls, by means of pluggable and user extensible distributed actor system implementations. - -Swift does not provide any specific distributed actor system by itself, however, packages in the ecosystem fulfil the role of providing those implementations. - -```swift -distributed actor Greeter { - var greetingsSent = 0 + Distributed actors provide stronger isolation guarantees than "local" actors, and enable additional checks to be made on return types and parameters of distributed methods, e.g. checking if they conform to `Codable`. Distributed methods can be called on "remote" references of distributed actors, turning those invocations into remote procedure calls, by means of pluggable and user extensible distributed actor system implementations. + + Swift does not provide any specific distributed actor system by itself, however, packages in the ecosystem fulfil the role of providing those implementations. - distributed func greet(name: String) -> String { - greetingsSent += 1 - return "Hello, \(name)!" + ```swift + distributed actor Greeter { + var greetingsSent = 0 + + distributed func greet(name: String) -> String { + greetingsSent += 1 + return "Hello, \(name)!" + } } -} - -func talkTo(greeter: Greeter) async throws { - // isolation of distributed actors is stronger, it is impossible to refer to - // any stored properties of distributed actors from outside of them: - greeter.greetingsSent // distributed actor-isolated property 'name' can not be accessed from a non-isolated context - // remote calls are implicitly throwing and async, - // to account for the potential networking involved: - let greeting = try await greeter.greet(name: "Alice") - print(greeting) // Hello, Alice! -} -``` + func talkTo(greeter: Greeter) async throws { + // isolation of distributed actors is stronger, it is impossible to refer to + // any stored properties of distributed actors from outside of them: + greeter.greetingsSent // distributed actor-isolated property 'name' can not be accessed from a non-isolated context + + // remote calls are implicitly throwing and async, + // to account for the potential networking involved: + let greeting = try await greeter.greet(name: "Alice") + print(greeting) // Hello, Alice! + } + ``` * The compiler now emits a warning when a non-final class conforms to a protocol that imposes a same-type requirement between `Self` and an associated type. This is because such a requirement makes the conformance unsound for subclasses. -For example, Swift 5.6 would allow the following code, which at runtime would construct an instance of `C` and not `SubC` as expected: + For example, Swift 5.6 would allow the following code, which at runtime would construct an instance of `C` and not `SubC` as expected: ```swift protocol P { @@ -240,24 +240,24 @@ Swift 5.6 * [SE-0327][]: -In Swift 5 mode, a warning is now emitted if the default-value expression of an -instance-member property requires global-actor isolation. For example: - -```swift -@MainActor -func partyGenerator() -> [PartyMember] { fatalError("todo") } + In Swift 5 mode, a warning is now emitted if the default-value expression of an + instance-member property requires global-actor isolation. For example: -class Party { - @MainActor var members: [PartyMember] = partyGenerator() - // ^~~~~~~~~~~~~~~~ - // warning: expression requiring global actor 'MainActor' cannot - // appear in default-value expression of property 'members' -} -``` - -Previously, the isolation granted by the type checker matched the isolation of -the property itself, but at runtime that is not guaranteed. In Swift 6, -such default-value expressions will become an error if they require isolation. + ```swift + @MainActor + func partyGenerator() -> [PartyMember] { fatalError("todo") } + + class Party { + @MainActor var members: [PartyMember] = partyGenerator() + // ^~~~~~~~~~~~~~~~ + // warning: expression requiring global actor 'MainActor' cannot + // appear in default-value expression of property 'members' + } + ``` + + Previously, the isolation granted by the type checker matched the isolation of + the property itself, but at runtime that is not guaranteed. In Swift 6, + such default-value expressions will become an error if they require isolation. * Actor isolation checking now understands that `defer` bodies share the isolation of their enclosing function. From 43d5ddd8dfb9bd3ac902ec0dc246e6be4389639c Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 5 Apr 2022 15:14:09 -0700 Subject: [PATCH 02/80] [NFC][CodeCompletion] Prepend 'should' to 'addItemWithoutDefaultArgs' Rename 'addItemWithoutDefaultArgs' to 'shouldAddItemWithoutDefaultArg' because this function only returns bool indicating "should or not". (cherry picked from commit 0dad5ff24e384fccebed0e49a280c59d7d334e97) --- include/swift/IDE/CompletionLookup.h | 2 +- lib/IDE/CompletionLookup.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 5f0945fa1aa7d..eca436fec53f9 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -348,7 +348,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { static bool hasInterestingDefaultValue(const ParamDecl *param); - bool addItemWithoutDefaultArgs(const AbstractFunctionDecl *func); + bool shouldAddItemWithoutDefaultArgs(const AbstractFunctionDecl *func); /// Build argument patterns for calling. Returns \c true if any content was /// added to \p Builder. If \p declParams is non-empty, the size must match diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index c015d7b2a9be7..90de9516db1f1 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -894,7 +894,7 @@ bool CompletionLookup::hasInterestingDefaultValue(const ParamDecl *param) { } } -bool CompletionLookup::addItemWithoutDefaultArgs( +bool CompletionLookup::shouldAddItemWithoutDefaultArgs( const AbstractFunctionDecl *func) { if (!func || !Sink.addCallWithNoDefaultArgs) return false; @@ -1189,7 +1189,7 @@ void CompletionLookup::addFunctionCallPattern( if (isImplicitlyCurriedInstanceMethod) { addPattern({AFD->getImplicitSelfDecl()}, /*includeDefaultArgs=*/true); } else { - if (addItemWithoutDefaultArgs(AFD)) + if (shouldAddItemWithoutDefaultArgs(AFD)) addPattern(AFD->getParameters()->getArray(), /*includeDefaultArgs=*/false); addPattern(AFD->getParameters()->getArray(), @@ -1385,7 +1385,7 @@ void CompletionLookup::addMethodCall(const FuncDecl *FD, if (trivialTrailingClosure) addMethodImpl(/*includeDefaultArgs=*/false, /*trivialTrailingClosure=*/true); - if (addItemWithoutDefaultArgs(FD)) + if (shouldAddItemWithoutDefaultArgs(FD)) addMethodImpl(/*includeDefaultArgs=*/false); addMethodImpl(/*includeDefaultArgs=*/true); } @@ -1475,7 +1475,7 @@ void CompletionLookup::addConstructorCall(const ConstructorDecl *CD, } }; - if (ConstructorType && addItemWithoutDefaultArgs(CD)) + if (ConstructorType && shouldAddItemWithoutDefaultArgs(CD)) addConstructorImpl(/*includeDefaultArgs=*/false); addConstructorImpl(); } From db097aa183ecb9d86f5dffbb8e8172dc3f46bc92 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 5 Apr 2022 15:15:46 -0700 Subject: [PATCH 03/80] [CodeCompletion] Don't mark some undesirable imported default values For ObjC NS_OPTIONS/NSDictionary parameters with some specific names, ClangImporter implicitly adds default values. However, that behavior is sometimes not really desirable. For example, for: -(void)addAttributes:(NSDictionary *)attrs; 'attrs' is defaulted, but calling 'addAttributes()' doesn't make any sense. In code-completion, consider such paramters non-defaulted. rdar://89051832 (cherry picked from commit 2b9ee76677cdf27726aa79bf67c62b3b5dd28b9b) --- lib/IDE/CompletionLookup.cpp | 76 ++++++++++++++++++- ...plete_default_arguments_rdar89051832.swift | 69 +++++++++++++++++ 2 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 test/IDE/complete_default_arguments_rdar89051832.swift diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 90de9516db1f1..fe45b1e8da260 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -873,6 +873,71 @@ void CompletionLookup::addVarDeclRef(const VarDecl *VD, Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); } +/// Return whether \p param has a non-desirable default value for code +/// completion. +/// +/// 'ClangImporter::Implementation::inferDefaultArgument()' automatically adds +/// default values for some parameters; +/// * NS_OPTIONS enum type with the name '...Options'. +/// * NSDictionary and labeled 'options', 'attributes', or 'userInfo'. +/// +/// But sometimes, this behavior isn't really desirable. This function add a +/// heuristic where if a parameter matches all the following condition, we +/// consider the imported default value is _not_ desirable: +/// * it is the first parameter, +/// * it doesn't have an argument label, and +/// * the imported function base name ends with those words +/// For example, ClangImporter imports: +/// +/// -(void)addAttributes:(NSDictionary *)attrs, options:(NSDictionary *)opts; +/// +/// as: +/// +/// func addAttributes(_ attrs: [AnyHashable:Any] = [:], +/// options opts: [AnyHashable:Any] = [:]) +/// +/// In this case, we don't want 'attrs' defaulted because the function name have +/// 'Attribute' in its name so calling 'value.addAttribute()' doesn't make +/// sense, but we _do_ want to keep 'opts' defaulted. +/// +/// Note that: +/// +/// -(void)performWithOptions:(NSDictionary *) opts; +/// +/// This doesn't match the condition because the base name of the function in +/// Swift is 'peform': +/// +/// func perform(options opts: [AnyHashable:Any] = [:]) +/// +bool isNonDesirableImportedDefaultArg(const ParamDecl *param) { + auto kind = param->getDefaultArgumentKind(); + if (kind != DefaultArgumentKind::EmptyArray && + kind != DefaultArgumentKind::EmptyDictionary) + return false; + + if (!param->getArgumentName().empty()) + return false; + + auto *func = dyn_cast(param->getDeclContext()); + if (!func->hasClangNode()) + return false; + if (func->getParameters()->front() != param) + return false; + if (func->getBaseName().isSpecial()) + return false; + + auto baseName = func->getBaseName().getIdentifier().str(); + switch (kind) { + case DefaultArgumentKind::EmptyArray: + return (baseName.endswith("Options")); + case DefaultArgumentKind::EmptyDictionary: + return (baseName.endswith("Options") || baseName.endswith("Attributes") || + baseName.endswith("UserInfo")); + default: + llvm_unreachable("unhandled DefaultArgumentKind"); + } +} + bool CompletionLookup::hasInterestingDefaultValue(const ParamDecl *param) { if (!param) return false; @@ -880,12 +945,16 @@ bool CompletionLookup::hasInterestingDefaultValue(const ParamDecl *param) { switch (param->getDefaultArgumentKind()) { case DefaultArgumentKind::Normal: case DefaultArgumentKind::NilLiteral: - case DefaultArgumentKind::EmptyArray: - case DefaultArgumentKind::EmptyDictionary: case DefaultArgumentKind::StoredProperty: case DefaultArgumentKind::Inherited: return true; + case DefaultArgumentKind::EmptyArray: + case DefaultArgumentKind::EmptyDictionary: + if (isNonDesirableImportedDefaultArg(param)) + return false; + return true; + case DefaultArgumentKind::None: #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ case DefaultArgumentKind::NAME: @@ -924,7 +993,8 @@ bool CompletionLookup::addCallArgumentPatterns( bool hasDefault = false; if (!declParams.empty()) { const ParamDecl *PD = declParams[i]; - hasDefault = PD->isDefaultArgument(); + hasDefault = + PD->isDefaultArgument() && !isNonDesirableImportedDefaultArg(PD); // Skip default arguments if we're either not including them or they // aren't interesting if (hasDefault && diff --git a/test/IDE/complete_default_arguments_rdar89051832.swift b/test/IDE/complete_default_arguments_rdar89051832.swift new file mode 100644 index 0000000000000..e0e3298ffaa4a --- /dev/null +++ b/test/IDE/complete_default_arguments_rdar89051832.swift @@ -0,0 +1,69 @@ + +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -batch-code-completion -source-filename %t/test.swift -filecheck %raw-FileCheck -completion-output-dir %t/out -code-completion-annotate-results -import-objc-header %t/ObjC.h -enable-objc-interop %t/Lib.swift +// REQUIRES: objc_interop + + +//--- ObjC.h +@import Foundation; + +typedef NS_OPTIONS(NSInteger, MyOptions) { + MyOptOne = 1 << 0, + MyOptTwo = 1 << 1, +}; + +@interface MyObj : NSObject +// 'opt' should not be defaulted. +// FIXME: Currently this is considered defaulted because the base name is 'store'. +- (void)storeOptions:(MyOptions)opts; + +// 'opts' should not be defaulted. +- (void)addOptions:(NSDictionary*)opts; + +// 'attrs' should not be defaulted. +- (void)addAttributes:(NSDictionary *)attrs; + +// 'info' should not be defaulted but 'opts' should be. +- (void)addUserInfo:(NSDictionary *)info options:(MyOptions)opts; + +// 'opts' should be defaulted because the base name is 'run'. +- (void)runWithOptions:(MyOptions)opts; + +// 'attrs' should be defaulted because the base name is 'execute'. +- (void)executeWithAttributes:(NSDictionary *)attrs; +@end + +//--- Lib.swift +extension MyObj { + // 'attrs' should not be defaulted because this is explicitly written in Swift. + func swift_addAttributes(_ attrs : [AnyHashable:Any]! = [:]) {} +} + +//--- test.swift +func test(value: MyObj) { + value.#^COMPLETE^# +// COMPLETE: Begin completions +// COMPLETE-NOT: name=addOptions() +// COMPLETE-NOT: name=addAttributes() + +// FIXME: we don't want to suggest 'store()'. +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: store(); typename=Void; name=store() +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: store(_ opts: MyOptions); typename=Void; name=store(:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: addOptions(_ opts: [AnyHashable : Any]!); typename=Void; name=addOptions(:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: addAttributes(_ attrs: [AnyHashable : Any]!); typename=Void; name=addAttributes(:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: addUserInfo(_ info: [AnyHashable : Any]!); typename=Void; name=addUserInfo(:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: addUserInfo(_ info: [AnyHashable : Any]!, options opts: MyOptions); typename=Void; name=addUserInfo(:options:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: run(); typename=Void; name=run() +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: run(options opts: MyOptions); typename=Void; name=run(options:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: execute(); typename=Void; name=execute() +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: execute(attributes attrs: [AnyHashable : Any]!); typename=Void; name=execute(attributes:) +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: swift_addAttributes(); typename=Void; name=swift_addAttributes() +// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: swift_addAttributes(_ attrs: [AnyHashable : Any]!); typename=Void; name=swift_addAttributes(:) + +// COMPLETE-NOT: name=addOptions() +// COMPLETE-NOT: name=addAttributes() +// COMPLETE: End completions +} + From 979035a0ebbe09edece00442893071474fd19de4 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 8 Mar 2022 11:30:08 -0800 Subject: [PATCH 04/80] [CodeCompletion] Ensure synthesized members are available before lookup In ExprContextAnalyzer, when looking up members, some implicit members weren't populated. Ensure all implicit members available by force synthesizing them. rdar://89773376 (cherry picked from commit 4e621408aa4555f90e1910154906517f71873150) --- include/swift/AST/NameLookup.h | 3 +++ lib/AST/NameLookup.cpp | 26 ++++++++++++++++++++++++++ lib/IDE/ExprContextAnalysis.cpp | 4 ++++ lib/Sema/TypeCheckNameLookup.cpp | 30 ++---------------------------- test/IDE/complete_call_arg.swift | 15 +++++++++++++++ 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index dfe01d8f9eef8..069c26d24878f 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -491,6 +491,9 @@ void lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer, namespace namelookup { +/// Add semantic members to \p type before attempting a semantic lookup. +void installSemanticMembersIfNeeded(Type type, DeclNameRef name); + void extractDirectlyReferencedNominalTypes( Type type, SmallVectorImpl &decls); diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 166b817eecc09..d6bf23587369d 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1592,6 +1592,32 @@ void namelookup::pruneLookupResultSet(const DeclContext *dc, NLOptions options, filterForDiscriminator(decls, M->getDebugClient()); } +// An unfortunate hack to kick the decl checker into adding semantic members to +// the current type before we attempt a semantic lookup. The places this method +// looks needs to be in sync with \c extractDirectlyReferencedNominalTypes. +// See the note in \c synthesizeSemanticMembersIfNeeded about a better, more +// just, and peaceful world. +void namelookup::installSemanticMembersIfNeeded(Type type, DeclNameRef name) { + // Look-through class-bound archetypes to ensure we synthesize e.g. + // inherited constructors. + if (auto archetypeTy = type->getAs()) { + if (auto super = archetypeTy->getSuperclass()) { + type = super; + } + } + + if (type->isExistentialType()) { + auto layout = type->getExistentialLayout(); + if (auto super = layout.explicitSuperclass) { + type = super; + } + } + + if (auto *current = type->getAnyNominal()) { + current->synthesizeSemanticMembersIfNeeded(name.getFullName()); + } +} + /// Inspect the given type to determine which nominal type declarations it /// directly references, to facilitate name lookup into those types. void namelookup::extractDirectlyReferencedNominalTypes( diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 58c96bb5211de..00c982873b02b 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -21,6 +21,7 @@ #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" +#include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" #include "swift/AST/SourceFile.h" @@ -364,6 +365,9 @@ static void collectPossibleCalleesByQualifiedLookup( if (!baseInstanceTy->mayHaveMembers()) return; + // Make sure we've resolved implicit members. + namelookup::installSemanticMembersIfNeeded(baseInstanceTy, name); + bool isOnMetaType = baseTy->is(); SmallVector decls; diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 26d0e8ec85a8a..a957da09715e1 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -273,32 +273,6 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclNameRef name, return result; } -// An unfortunate hack to kick the decl checker into adding semantic members to -// the current type before we attempt a semantic lookup. The places this method -// looks needs to be in sync with \c extractDirectlyReferencedNominalTypes. -// See the note in \c synthesizeSemanticMembersIfNeeded about a better, more -// just, and peaceful world. -static void installSemanticMembersIfNeeded(Type type, DeclNameRef name) { - // Look-through class-bound archetypes to ensure we synthesize e.g. - // inherited constructors. - if (auto archetypeTy = type->getAs()) { - if (auto super = archetypeTy->getSuperclass()) { - type = super; - } - } - - if (type->isExistentialType()) { - auto layout = type->getExistentialLayout(); - if (auto super = layout.explicitSuperclass) { - type = super; - } - } - - if (auto *current = type->getAnyNominal()) { - current->synthesizeSemanticMembersIfNeeded(name.getFullName()); - } -} - LookupResult TypeChecker::lookupUnqualifiedType(DeclContext *dc, DeclNameRef name, SourceLoc loc, @@ -346,7 +320,7 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, subOptions &= ~NL_RemoveNonVisible; // Make sure we've resolved implicit members, if we need them. - installSemanticMembersIfNeeded(type, name); + namelookup::installSemanticMembersIfNeeded(type, name); LookupResultBuilder builder(result, dc, options); SmallVector lookupResults; @@ -438,7 +412,7 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, subOptions |= NL_IncludeUsableFromInline; // Make sure we've resolved implicit members, if we need them. - installSemanticMembersIfNeeded(type, name); + namelookup::installSemanticMembersIfNeeded(type, name); if (!dc->lookupQualified(type, name, subOptions, decls)) return result; diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index e1c065ef09a3b..7ce601fffdefa 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -1271,6 +1271,7 @@ func testSkippedCallArgInInvalidResultBuilderBody() { Other(action: 2) { MyImage(systemName: "", #^INVALID_RESULTBUILDER_ARG^# struct Invalid + } } // INVALID_RESULTBUILDER_ARG: Begin completions, 1 item @@ -1399,3 +1400,17 @@ func testVarInitializedByCallingClosure() { // VAR_INITIALIZED_BY_CALLING_CLOSURE-DAG: Pattern/Local/Flair[ArgLabels]: {#withExtension: String?#}[#String?#]; // VAR_INITIALIZED_BY_CALLING_CLOSURE: End completions } + +struct Rdar89773376 { + var intVal: Int +} +extension Rdar89773376 { + init(string: String) { self.intVal = 1 } +} +func testRdar89773376(arry: [Int]) { + arry.map { Rdar89773376(#^RDAR89773376^#) } +// RDAR89773376: Begin completions, 2 items +// RDAR89773376-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#string: String#}[')'][#Rdar89773376#]; +// RDAR89773376-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#intVal: Int#}[')'][#Rdar89773376#]; +// RDAR89773376: End completions +} From e559de86ea79426238b796d20f786cb5cd362e8e Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 14 Apr 2022 06:37:26 +0300 Subject: [PATCH 05/80] Add executable tests for the edge case of unbound reference to optional method on `AnyObject` --- test/Interpreter/dynamic_lookup.swift | 30 +++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/Interpreter/dynamic_lookup.swift b/test/Interpreter/dynamic_lookup.swift index 7a7125f9de822..3aa87bd964dab 100644 --- a/test/Interpreter/dynamic_lookup.swift +++ b/test/Interpreter/dynamic_lookup.swift @@ -5,6 +5,10 @@ import Foundation +@objc protocol P { + @objc optional func e() +} + class X { init() {} @@ -15,11 +19,15 @@ class X { return 17 } } +extension X: P { + @objc func e() { print("X.e()") } +} class Y { init() {} @objc class func g() { print("Y.g()") } } +extension Y: P {} class Z { init() {} @@ -44,7 +52,16 @@ func test_dynamic_lookup_f_unbound(_ obj: AnyObject) { if of != nil { of!() } else { - print("Object does not respond to the selector \"f\".\n", terminator: "") + print("\(type(of: obj)) does not respond to the selector \"f\"") + } +} + +func test_dynamic_lookup_e_unbound(_ obj: AnyObject) { + var oe = AnyObject.e(obj) + if oe != nil { + oe!() + } else { + print("\(type(of: obj)) does not respond to the selector \"e\"") } } @@ -77,11 +94,20 @@ test_dynamic_lookup_f(Z()) print(type(of: AnyObject.f)) // CHECK-NEXT: X.f() test_dynamic_lookup_f_unbound(X()) -// CHECK-NEXT: Object does not respond to the selector "f" +// CHECK-NEXT: Y does not respond to the selector "f" test_dynamic_lookup_f_unbound(Y()) // CHECK-NEXT: Z.f() test_dynamic_lookup_f_unbound(Z()) +// CHECK-NEXT: (AnyObject) -> Optional<() -> ()> +print(type(of: AnyObject.e)) +// CHECK-NEXT: X.e() +test_dynamic_lookup_e_unbound(X()) +// CHECK-NEXT: Y does not respond to the selector "e" +test_dynamic_lookup_e_unbound(Y()) +// CHECK-NEXT: Z does not respond to the selector "e" +test_dynamic_lookup_e_unbound(Z()) + // CHECK: Class does not respond to the selector "g" test_dynamic_lookup_g(X()) // CHECK: Y.g() From 5db44c3e4735769d69059f4fb23177e8933a8def Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 14 Apr 2022 06:42:54 +0300 Subject: [PATCH 06/80] CHANGELOG: Add entry for #41849 and #42332 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4040f144bf239..87f8464d4aa3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ _**Note:** This is in reverse chronological order, so newer entries are added to ## Swift 5.7 +* References to `optional` methods on a protocol metatype, as well as references to dynamically looked up methods on the `AnyObject` metatype are now supported. These references always have the type of a function that accepts a single argument and returns an optional value of function type: + + ```swift + class Object { + @objc func getTag() -> Int + } + + @objc protocol P { + @objc optional func didUpdateObject(withTag tag: Int) + } + + let getTag: (AnyObject) -> (() -> Int)? = AnyObject.getTag + + let didUpdateObject: (any P) -> ((Int) -> Void)? = P.didUpdateObject + ``` + * [SE-0352][]: It's now possible to call a generic function with a value of protocol type From 5deab08513be6b6570c59f2cd9592a50de909613 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 21 Apr 2022 22:26:06 +0300 Subject: [PATCH 07/80] CHANGELOG: Add missing language mode for code block --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87f8464d4aa3d..d8b6905a4abca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ _**Note:** This is in reverse chronological order, so newer entries are added to It's now possible to use a default value expression with a generic parameter type to default the argument and its type: - ``` + ```swift func compute(_ values: C = [0, 1, 2]) { ... } From 490b4ec0c1191e3ff4197aa14a6930a490c216fe Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 25 Mar 2022 17:52:29 -0600 Subject: [PATCH 08/80] [test] avoid using array conversion for String-from-C-string --- test/Prototypes/PatternMatching.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Prototypes/PatternMatching.swift b/test/Prototypes/PatternMatching.swift index 33065d4a3729e..7383ba8466687 100644 --- a/test/Prototypes/PatternMatching.swift +++ b/test/Prototypes/PatternMatching.swift @@ -302,7 +302,9 @@ extension Collection where Iterator.Element == UTF8.CodeUnit { a.reserveCapacity(count + 1) a.append(contentsOf: self) a.append(0) - return String(reflecting: String(cString: a)) + return a.withUnsafeBufferPointer { + String(reflecting: String(cString: $0.baseAddress!)) + } } } From cb04de10b72d7c24aa5e41da21aa01306dba79bd Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 29 Mar 2022 14:12:25 -0600 Subject: [PATCH 09/80] [stdlib] harmonize parameter labels with StringProtocol --- stdlib/public/core/CString.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index 930d4cb7b8598..0fa9046c0881f 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -42,11 +42,11 @@ extension String { /// } /// // Prints "Caf�" /// - /// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence. - public init(cString: UnsafePointer) { - let len = UTF8._nullCodeUnitOffset(in: cString) + /// - Parameter nullTerminatedUTF8: A pointer to a null-terminated UTF-8 code sequence. + public init(cString nullTerminatedUTF8: UnsafePointer) { + let len = UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) self = String._fromUTF8Repairing( - UnsafeBufferPointer(start: cString._asUInt8, count: len)).0 + UnsafeBufferPointer(start: nullTerminatedUTF8._asUInt8, count: len)).0 } /// Creates a new string by copying the null-terminated UTF-8 data referenced @@ -54,10 +54,10 @@ extension String { /// /// This is identical to `init(cString: UnsafePointer)` but operates on /// an unsigned sequence of bytes. - public init(cString: UnsafePointer) { - let len = UTF8._nullCodeUnitOffset(in: cString) + public init(cString nullTerminatedUTF8: UnsafePointer) { + let len = UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) self = String._fromUTF8Repairing( - UnsafeBufferPointer(start: cString, count: len)).0 + UnsafeBufferPointer(start: nullTerminatedUTF8, count: len)).0 } /// Creates a new string by copying and validating the null-terminated UTF-8 @@ -179,10 +179,10 @@ extension String { @_specialize(where Encoding == Unicode.UTF16) @inlinable // Fold away specializations public init( - decodingCString ptr: UnsafePointer, + decodingCString nullTerminatedCodeUnits: UnsafePointer, as sourceEncoding: Encoding.Type ) { - self = String.decodeCString(ptr, as: sourceEncoding)!.0 + self = String.decodeCString(nullTerminatedCodeUnits, as: sourceEncoding)!.0 } } From f641ca8084670da45486fcfdc9afda025bf91644 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 25 Mar 2022 19:14:33 -0600 Subject: [PATCH 10/80] [stdlib] remove incorrect uses of pointer conversion --- .../StdlibUnittest/StdlibUnittest.swift | 7 ++++--- .../SwiftReflectionTest.swift | 20 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/stdlib/private/StdlibUnittest/StdlibUnittest.swift b/stdlib/private/StdlibUnittest/StdlibUnittest.swift index 7b28d564e46d5..d6e3f90bae28a 100644 --- a/stdlib/private/StdlibUnittest/StdlibUnittest.swift +++ b/stdlib/private/StdlibUnittest/StdlibUnittest.swift @@ -2182,9 +2182,10 @@ func _getSystemVersionPlistProperty(_ propertyName: String) -> String? { func _getSystemVersionPlistProperty(_ propertyName: String) -> String? { var count = 0 sysctlbyname("kern.osproductversion", nil, &count, nil, 0) - var s = [CChar](repeating: 0, count: count) - sysctlbyname("kern.osproductversion", &s, &count, nil, 0) - return String(cString: &s) + return withUnsafeTemporaryAllocation(of: CChar.self, capacity: count) { + sysctlbyname("kern.osproductversion", $0.baseAddress, &count, nil, 0) + return String(cString: $0.baseAddress!) + } } #endif #endif diff --git a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift index db1919bb81be5..cb153b8daf618 100644 --- a/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift +++ b/stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift @@ -373,25 +373,25 @@ internal func reflect(instanceAddress: UInt, shouldUnwrapClassExistential: Bool = false) { while let command = readLine(strippingNewline: true) { switch command { - case String(validatingUTF8: RequestInstanceKind)!: + case RequestInstanceKind: sendValue(kind.rawValue) - case String(validatingUTF8: RequestShouldUnwrapClassExistential)!: + case RequestShouldUnwrapClassExistential: sendValue(shouldUnwrapClassExistential) - case String(validatingUTF8: RequestInstanceAddress)!: + case RequestInstanceAddress: sendValue(instanceAddress) - case String(validatingUTF8: RequestReflectionInfos)!: + case RequestReflectionInfos: sendReflectionInfos() - case String(validatingUTF8: RequestImages)!: + case RequestImages: sendImages() - case String(validatingUTF8: RequestReadBytes)!: + case RequestReadBytes: sendBytes() - case String(validatingUTF8: RequestSymbolAddress)!: + case RequestSymbolAddress: sendSymbolAddress() - case String(validatingUTF8: RequestStringLength)!: + case RequestStringLength: sendStringLength() - case String(validatingUTF8: RequestPointerSize)!: + case RequestPointerSize: sendPointerSize() - case String(validatingUTF8: RequestDone)!: + case RequestDone: return default: fatalError("Unknown request received: '\(Array(command.utf8))'!") From 2ede9a7567e72979e8a1850fd5bae6246a2c71ce Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 24 Mar 2022 17:24:16 -0600 Subject: [PATCH 11/80] [stdlib] overload for arrays passed to `String.init(cString:)` --- stdlib/public/core/CString.swift | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index 0fa9046c0881f..db765845820f0 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -49,6 +49,29 @@ extension String { UnsafeBufferPointer(start: nullTerminatedUTF8._asUInt8, count: len)).0 } + @inlinable + @_alwaysEmitIntoClient + public init(cString nullTerminatedUTF8: [CChar]) { + self = nullTerminatedUTF8.withUnsafeBytes { + String(_checkingCString: $0.assumingMemoryBound(to: UInt8.self)) + } + } + + @_alwaysEmitIntoClient + private init(_checkingCString bytes: UnsafeBufferPointer) { + guard let length = bytes.firstIndex(of: 0) else { + _preconditionFailure( + "input of String.init(cString:) must be null-terminated" + ) + } + self = String._fromUTF8Repairing( + UnsafeBufferPointer( + start: bytes.baseAddress._unsafelyUnwrappedUnchecked, + count: length + ) + ).0 + } + /// Creates a new string by copying the null-terminated UTF-8 data referenced /// by the given pointer. /// @@ -60,6 +83,14 @@ extension String { UnsafeBufferPointer(start: nullTerminatedUTF8, count: len)).0 } + @inlinable + @_alwaysEmitIntoClient + public init(cString nullTerminatedUTF8: [UInt8]) { + self = nullTerminatedUTF8.withUnsafeBufferPointer { + String(_checkingCString: $0) + } + } + /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given pointer. /// From d80964072ec6f66cbed4384e199a3542fe2e3407 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 24 Mar 2022 19:07:12 -0600 Subject: [PATCH 12/80] [stdlib] overload for arrays passed to String.init?(validatingUTF8:) --- stdlib/public/core/CString.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index db765845820f0..522dca1c4696c 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -126,6 +126,21 @@ extension String { self = str } + @inlinable + @_alwaysEmitIntoClient + public init?(validatingUTF8 cString: [CChar]) { + guard let length = cString.firstIndex(of: 0) else { + _preconditionFailure( + "input of String.init(validatingUTF8:) must be null-terminated" + ) + } + guard let string = cString.prefix(length).withUnsafeBytes({ + String._tryFromUTF8($0.assumingMemoryBound(to: UInt8.self)) + }) else { return nil } + + self = string + } + /// Creates a new string by copying the null-terminated data referenced by /// the given pointer using the specified encoding. /// From 63ba39f43079682f9b027bdd7c5139d066e20db8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 25 Mar 2022 15:11:53 -0600 Subject: [PATCH 13/80] [stdlib] overload for arrays passed to String(decodeCString:as:) --- stdlib/public/core/CString.swift | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index 522dca1c4696c..04077641f8c25 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -212,6 +212,42 @@ extension String { return String._fromCodeUnits( codeUnits, encoding: encoding, repair: isRepairing) } + + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) + @inlinable // Fold away specializations + @_alwaysEmitIntoClient + public static func decodeCString( + _ cString: [Encoding.CodeUnit], + as encoding: Encoding.Type, + repairingInvalidCodeUnits isRepairing: Bool = true + ) -> (result: String, repairsMade: Bool)? { + guard let length = cString.firstIndex(of: 0) else { + _preconditionFailure( + "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + ) + } + + if _fastPath(encoding == Unicode.UTF8.self) { + return cString.prefix(length).withUnsafeBytes { + buf -> (result: String, repairsMade: Bool)? in + let codeUnits = buf.assumingMemoryBound(to: UInt8.self) + if isRepairing { + return String._fromUTF8Repairing(codeUnits) + } + else if let str = String._tryFromUTF8(codeUnits) { + return (str, false) + } + return nil + } + } + + return cString.prefix(length).withUnsafeBufferPointer { + buf -> (result: String, repairsMade: Bool)? in + String._fromCodeUnits(buf, encoding: encoding, repair: isRepairing) + } + } + /// Creates a string from the null-terminated sequence of bytes at the given /// pointer. /// @@ -230,6 +266,17 @@ extension String { ) { self = String.decodeCString(nullTerminatedCodeUnits, as: sourceEncoding)!.0 } + + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) + @inlinable // Fold away specializations + @_alwaysEmitIntoClient + public init( + decodingCString nullTerminatedCodeUnits: [Encoding.CodeUnit], + as sourceEncoding: Encoding.Type + ) { + self = String.decodeCString(nullTerminatedCodeUnits, as: sourceEncoding)!.0 + } } extension UnsafePointer where Pointee == UInt8 { From 1eee2430013b10238547ef5aebdba38600e7ac2d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 1 Apr 2022 15:29:50 -0600 Subject: [PATCH 14/80] [stdlib] String overloads for String-from-C-string inits --- stdlib/public/core/CString.swift | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index 04077641f8c25..082068bdd22d8 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -91,6 +91,13 @@ extension String { } } + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated, message: "Operate directly on the String") + public init(cString nullTerminatedUTF8: String) { + self = nullTerminatedUTF8.withCString(String.init(cString:)) + } + /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given pointer. /// @@ -141,6 +148,13 @@ extension String { self = string } + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated, message: "Operate directly on the String") + public init?(validatingUTF8 cString: String) { + self = cString.withCString(String.init(cString:)) + } + /// Creates a new string by copying the null-terminated data referenced by /// the given pointer using the specified encoding. /// @@ -248,6 +262,23 @@ extension String { } } + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated, message: "Operate directly on the String") + public static func decodeCString( + _ cString: String, + as encoding: Encoding.Type, + repairingInvalidCodeUnits isRepairing: Bool = true + ) -> (result: String, repairsMade: Bool)? { + return cString.withCString(encodedAs: encoding) { + String.decodeCString( + $0, as: encoding, repairingInvalidCodeUnits: isRepairing + ) + } + } + /// Creates a string from the null-terminated sequence of bytes at the given /// pointer. /// @@ -277,6 +308,20 @@ extension String { ) { self = String.decodeCString(nullTerminatedCodeUnits, as: sourceEncoding)!.0 } + + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated, message: "Operate directly on the String") + public init( + decodingCString nullTerminatedCodeUnits: String, + as sourceEncoding: Encoding.Type + ) { + self = nullTerminatedCodeUnits.withCString(encodedAs: sourceEncoding) { + String(decodingCString: $0, as: sourceEncoding.self) + } + } } extension UnsafePointer where Pointee == UInt8 { From 7d2151f3ef5ea3e535e8363d2fa89bbb804bf3a9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 4 Apr 2022 13:42:09 -0600 Subject: [PATCH 15/80] [test] add tests of String-from-C-string Array overloads --- test/stdlib/StringAPICString.swift | 108 +++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index be062847c6455..68423fa8f780b 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -225,5 +225,113 @@ CStringTests.test("Substring.withCString") { } } +CStringTests.test("String.cString.with.Array.UInt8.input") { + do { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + let cstr = UnsafePointer(u8p) + let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) + let str = String(cString: Array(buffer)) + str.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { + expectEqualCString(u8p, $0) + } + } + } + // no need to test every case; that is covered in other tests + expectCrashLater( + withMessage: "input of String.init(cString:) must be null-terminated" + ) + _ = String(cString: [] as [UInt8]) + expectUnreachable() +} + +CStringTests.test("String.cString.with.Array.CChar.input") { + do { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: CChar.self) + let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) + let str = String(cString: Array(buffer)) + str.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { + expectEqualCString(u8p, $0) + } + } + } + // no need to test every case; that is covered in other tests + expectCrashLater( + withMessage: "input of String.init(cString:) must be null-terminated" + ) + _ = String(cString: [] as [CChar]) + expectUnreachable() +} + +CStringTests.test("String.validatingUTF8.with.Array.input") { + do { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: CChar.self) + let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) + let str = String(validatingUTF8: Array(buffer)) + expectNotNil(str) + str?.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { + expectEqualCString(u8p, $0) + } + } + } + // no need to test every case; that is covered in other tests + expectCrashLater( + withMessage: "input of String.init(validatingUTF8:) must be null-terminated" + ) + _ = String(validatingUTF8: []) + expectUnreachable() +} + +CStringTests.test("String.decodeCString.with.Array.input") { + do { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: Unicode.UTF8.CodeUnit.self) + let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) + let result = String.decodeCString(Array(buffer), as: Unicode.UTF8.self) + expectNotNil(result) + expectEqual(result?.repairsMade, false) + result?.result.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { + expectEqualCString(u8p, $0) + } + } + } + // no need to test every case; that is covered in other tests + expectCrashLater( + withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + ) + _ = String.decodeCString([], as: Unicode.UTF8.self) + expectUnreachable() +} + +CStringTests.test("String.decodingCString.with.Array.input") { + do { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: Unicode.UTF8.CodeUnit.self) + let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) + let str = String(decodingCString: Array(buffer), as: Unicode.UTF8.self) + str.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { + expectEqualCString(u8p, $0) + } + } + } + // no need to test every case; that is covered in other tests + expectCrashLater( + withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + ) + _ = String(decodingCString: [], as: Unicode.UTF8.self) + expectUnreachable() +} + runAllTests() From 1e5d6902d171810dc76db9aba92a91fd113dc660 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 4 Apr 2022 15:40:32 -0600 Subject: [PATCH 16/80] [test] add tests of String-from-C-string String overloads --- test/stdlib/StringAPICString.swift | 60 ++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index 68423fa8f780b..1b7ff5a36db59 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -267,6 +267,19 @@ CStringTests.test("String.cString.with.Array.CChar.input") { expectUnreachable() } +CStringTests.test("String.cString.with.String.input") { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + var str = String(cString: "ab") + str.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { + expectEqualCString(u8p, $0) + } + } + str = String(cString: "") + expectTrue(str.isEmpty) +} + CStringTests.test("String.validatingUTF8.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() @@ -289,6 +302,21 @@ CStringTests.test("String.validatingUTF8.with.Array.input") { expectUnreachable() } +CStringTests.test("String.validatingUTF8.with.String.input") { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + var str = String(validatingUTF8: "ab") + expectNotNil(str) + str?.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { + expectEqualCString(u8p, $0) + } + } + str = String(validatingUTF8: "") + expectNotNil(str) + expectEqual(str?.isEmpty, true) +} + CStringTests.test("String.decodeCString.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() @@ -312,6 +340,25 @@ CStringTests.test("String.decodeCString.with.Array.input") { expectUnreachable() } +CStringTests.test("String.decodeCString.with.String.input") { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + var result = String.decodeCString( + "ab", as: Unicode.UTF8.self, repairingInvalidCodeUnits: true + ) + expectNotNil(result) + expectEqual(result?.repairsMade, false) + result?.result.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { + expectEqualCString(u8p, $0) + } + } + result = String.decodeCString("", as: Unicode.UTF8.self) + expectNotNil(result) + expectEqual(result?.repairsMade, false) + expectEqual(result?.result.isEmpty, true) +} + CStringTests.test("String.decodingCString.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() @@ -333,5 +380,18 @@ CStringTests.test("String.decodingCString.with.Array.input") { expectUnreachable() } +CStringTests.test("String.init.decodingCString.with.String.input") { + let (u8p, dealloc) = getASCIIUTF8() + defer { dealloc() } + var str = String(decodingCString: "ab", as: Unicode.UTF8.self) + str.withCString { + $0.withMemoryRebound(to: UInt8.self, capacity: getUTF8Length(u8p)+1) { + expectEqualCString(u8p, $0) + } + } + str = String(decodingCString: "", as: Unicode.UTF8.self) + expectTrue(str.isEmpty) +} + runAllTests() From 0285602a4bc8704119734d4c48be6b84dfaa8e05 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 4 Apr 2022 16:33:41 -0600 Subject: [PATCH 17/80] [stdlib] overload String-from-C-string initializers in the case of inout conversion --- stdlib/public/core/CString.swift | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index 082068bdd22d8..afd75f862ca81 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -72,6 +72,18 @@ extension String { ).0 } + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated) + public init(cString nullTerminatedUTF8: inout CChar) { + guard nullTerminatedUTF8 == 0 else { + _preconditionFailure( + "input of String.init(cString:) must be null-terminated" + ) + } + self = "" + } + /// Creates a new string by copying the null-terminated UTF-8 data referenced /// by the given pointer. /// @@ -98,6 +110,18 @@ extension String { self = nullTerminatedUTF8.withCString(String.init(cString:)) } + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated) + public init(cString nullTerminatedUTF8: inout UInt8) { + guard nullTerminatedUTF8 == 0 else { + _preconditionFailure( + "input of String.init(cString:) must be null-terminated" + ) + } + self = "" + } + /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given pointer. /// @@ -155,6 +179,18 @@ extension String { self = cString.withCString(String.init(cString:)) } + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated) + public init?(validatingUTF8 cString: inout CChar) { + guard cString == 0 else { + _preconditionFailure( + "input of String.init(validatingUTF8:) must be null-terminated" + ) + } + self = "" + } + /// Creates a new string by copying the null-terminated data referenced by /// the given pointer using the specified encoding. /// @@ -279,6 +315,24 @@ extension String { } } + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) + @inlinable + @_alwaysEmitIntoClient + @available(*, deprecated) + public static func decodeCString( + _ cString: inout Encoding.CodeUnit, + as encoding: Encoding.Type, + repairingInvalidCodeUnits isRepairing: Bool = true + ) -> (result: String, repairsMade: Bool)? { + guard cString == 0 else { + _preconditionFailure( + "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + ) + } + return ("", false) + } + /// Creates a string from the null-terminated sequence of bytes at the given /// pointer. /// @@ -322,6 +376,22 @@ extension String { String(decodingCString: $0, as: sourceEncoding.self) } } + + @_specialize(where Encoding == Unicode.UTF8) + @_specialize(where Encoding == Unicode.UTF16) + @inlinable // Fold away specializations + @_alwaysEmitIntoClient + public init( + decodingCString nullTerminatedCodeUnits: inout Encoding.CodeUnit, + as sourceEncoding: Encoding.Type + ) { + guard nullTerminatedCodeUnits == 0 else { + _preconditionFailure( + "input of String.init(decodingCString:as:) must be null-terminated" + ) + } + self = "" + } } extension UnsafePointer where Pointee == UInt8 { From c594482fcad7133e839bb1880f89e39f91def044 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 4 Apr 2022 17:18:25 -0600 Subject: [PATCH 18/80] [test] add tests of String-from-C-string inout conversion overloads --- test/stdlib/StringAPICString.swift | 67 +++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index 1b7ff5a36db59..f95e61469c8e1 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -280,6 +280,30 @@ CStringTests.test("String.cString.with.String.input") { expectTrue(str.isEmpty) } +CStringTests.test("String.cString.with.inout.UInt8.conversion") { + var c = UInt8.zero + var str = String(cString: &c) + expectTrue(str.isEmpty) + c = 100 + expectCrashLater( + withMessage: "input of String.init(cString:) must be null-terminated" + ) + str = String(cString: &c) + expectUnreachable() +} + +CStringTests.test("String.cString.with.inout.CChar.conversion") { + var c = CChar.zero + var str = String(cString: &c) + expectTrue(str.isEmpty) + c = 100 + expectCrashLater( + withMessage: "input of String.init(cString:) must be null-terminated" + ) + str = String(cString: &c) + expectUnreachable() +} + CStringTests.test("String.validatingUTF8.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() @@ -317,6 +341,19 @@ CStringTests.test("String.validatingUTF8.with.String.input") { expectEqual(str?.isEmpty, true) } +CStringTests.test("String.validatingUTF8.with.inout.conversion") { + var c = CChar.zero + var str = String(validatingUTF8: &c) + expectNotNil(str) + expectEqual(str?.isEmpty, true) + c = 100 + expectCrashLater( + withMessage: "input of String.init(validatingUTF8:) must be null-terminated" + ) + str = String(validatingUTF8: &c) + expectUnreachable() +} + CStringTests.test("String.decodeCString.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() @@ -359,7 +396,23 @@ CStringTests.test("String.decodeCString.with.String.input") { expectEqual(result?.result.isEmpty, true) } -CStringTests.test("String.decodingCString.with.Array.input") { +CStringTests.test("String.decodeCString.with.inout.conversion") { + var c = Unicode.UTF8.CodeUnit.zero + var result = String.decodeCString( + &c, as: Unicode.UTF8.self, repairingInvalidCodeUnits: true + ) + expectNotNil(result) + expectEqual(result?.result.isEmpty, true) + expectEqual(result?.repairsMade, false) + c = 100 + expectCrashLater( + withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + ) + result = String.decodeCString(&c, as: Unicode.UTF8.self) + expectUnreachable() +} + +CStringTests.test("String.init.decodingCString.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -393,5 +446,17 @@ CStringTests.test("String.init.decodingCString.with.String.input") { expectTrue(str.isEmpty) } +CStringTests.test("String.init.decodingCString.with.inout.conversion") { + var c = Unicode.UTF8.CodeUnit.zero + var str = String(decodingCString: &c, as: Unicode.UTF8.self) + expectEqual(str.isEmpty, true) + c = 100 + expectCrashLater( + withMessage: "input of String.init(decodingCString:as:) must be null-terminated" + ) + str = String(decodingCString: &c, as: Unicode.UTF8.self) + expectUnreachable() +} + runAllTests() From 59c1f4266fba7efeeb5314c1b25d6a3e8e736a0a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 4 Apr 2022 20:26:40 -0600 Subject: [PATCH 19/80] [test] check deprecation warnings for undesirable overloads --- stdlib/public/core/CString.swift | 1 + test/stdlib/StringAPICStringDiagnostics.swift | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/stdlib/StringAPICStringDiagnostics.swift diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index afd75f862ca81..3f6987442433c 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -381,6 +381,7 @@ extension String { @_specialize(where Encoding == Unicode.UTF16) @inlinable // Fold away specializations @_alwaysEmitIntoClient + @available(*, deprecated) public init( decodingCString nullTerminatedCodeUnits: inout Encoding.CodeUnit, as sourceEncoding: Encoding.Type diff --git a/test/stdlib/StringAPICStringDiagnostics.swift b/test/stdlib/StringAPICStringDiagnostics.swift new file mode 100644 index 0000000000000..f9badbb8dcb49 --- /dev/null +++ b/test/stdlib/StringAPICStringDiagnostics.swift @@ -0,0 +1,33 @@ +// RUN: %target-typecheck-verify-swift -verify-ignore-unknown + +import StdlibUnittest + +func checkStringOverloadCompilationDiagnostics() { + + _ = String(cString: "string") // expected-warning {{'init(cString:)' is deprecated: Operate directly on the String}} + + _ = String(validatingUTF8: "string") // expected-warning {{init(validatingUTF8:)' is deprecated: Operate directly on the String}} + + _ = String.decodeCString("string", as: Unicode.UTF8.self) // expected-warning {{'decodeCString(_:as:repairingInvalidCodeUnits:)' is deprecated: Operate directly on the String}} + + _ = String(decodingCString: "string", as: Unicode.UTF8.self) // expected-warning {{'init(decodingCString:as:)' is deprecated: Operate directly on the String}} +} + +func checkInoutConversionOverloadCompilationDiagnostics() { + + var i = UInt8.zero + + _ = String(cString: &i) // expected-warning {{'init(cString:)' is deprecated}} + + var c = CChar.zero + + _ = String(cString: &c) // expected-warning {{'init(cString:)' is deprecated}} + + _ = String(validatingUTF8: &c) // expected-warning {{init(validatingUTF8:)' is deprecated}} + + var u = Unicode.UTF8.CodeUnit.zero + + _ = String.decodeCString(&u, as: Unicode.UTF8.self) // expected-warning {{'decodeCString(_:as:repairingInvalidCodeUnits:)' is deprecated}} + + _ = String(decodingCString: &u, as: Unicode.UTF8.self) // expected-warning {{'init(decodingCString:as:)' is deprecated}} +} From d83d0607e46c2ba1677bc422460f9d7a75c26d54 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 5 Apr 2022 14:43:03 -0600 Subject: [PATCH 20/80] [stdlib] improve the new deprecation messages --- stdlib/public/core/CString.swift | 18 +++++++++--------- test/stdlib/StringAPICStringDiagnostics.swift | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index 3f6987442433c..c517e599d4568 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -74,7 +74,7 @@ extension String { @inlinable @_alwaysEmitIntoClient - @available(*, deprecated) + @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init(cString nullTerminatedUTF8: inout CChar) { guard nullTerminatedUTF8 == 0 else { _preconditionFailure( @@ -105,14 +105,14 @@ extension String { @inlinable @_alwaysEmitIntoClient - @available(*, deprecated, message: "Operate directly on the String") + @available(*, deprecated, message: "Use a copy of the String argument") public init(cString nullTerminatedUTF8: String) { self = nullTerminatedUTF8.withCString(String.init(cString:)) } @inlinable @_alwaysEmitIntoClient - @available(*, deprecated) + @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init(cString nullTerminatedUTF8: inout UInt8) { guard nullTerminatedUTF8 == 0 else { _preconditionFailure( @@ -174,14 +174,14 @@ extension String { @inlinable @_alwaysEmitIntoClient - @available(*, deprecated, message: "Operate directly on the String") + @available(*, deprecated, message: "Use a copy of the String argument") public init?(validatingUTF8 cString: String) { self = cString.withCString(String.init(cString:)) } @inlinable @_alwaysEmitIntoClient - @available(*, deprecated) + @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init?(validatingUTF8 cString: inout CChar) { guard cString == 0 else { _preconditionFailure( @@ -302,7 +302,7 @@ extension String { @_specialize(where Encoding == Unicode.UTF16) @inlinable @_alwaysEmitIntoClient - @available(*, deprecated, message: "Operate directly on the String") + @available(*, deprecated, message: "Use a copy of the String argument") public static func decodeCString( _ cString: String, as encoding: Encoding.Type, @@ -319,7 +319,7 @@ extension String { @_specialize(where Encoding == Unicode.UTF16) @inlinable @_alwaysEmitIntoClient - @available(*, deprecated) + @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public static func decodeCString( _ cString: inout Encoding.CodeUnit, as encoding: Encoding.Type, @@ -367,7 +367,7 @@ extension String { @_specialize(where Encoding == Unicode.UTF16) @inlinable @_alwaysEmitIntoClient - @available(*, deprecated, message: "Operate directly on the String") + @available(*, deprecated, message: "Use a copy of the String argument") public init( decodingCString nullTerminatedCodeUnits: String, as sourceEncoding: Encoding.Type @@ -381,7 +381,7 @@ extension String { @_specialize(where Encoding == Unicode.UTF16) @inlinable // Fold away specializations @_alwaysEmitIntoClient - @available(*, deprecated) + @available(*, deprecated, message: "Use String(_ scalar: Unicode.Scalar)") public init( decodingCString nullTerminatedCodeUnits: inout Encoding.CodeUnit, as sourceEncoding: Encoding.Type diff --git a/test/stdlib/StringAPICStringDiagnostics.swift b/test/stdlib/StringAPICStringDiagnostics.swift index f9badbb8dcb49..434fbab1c4685 100644 --- a/test/stdlib/StringAPICStringDiagnostics.swift +++ b/test/stdlib/StringAPICStringDiagnostics.swift @@ -4,30 +4,30 @@ import StdlibUnittest func checkStringOverloadCompilationDiagnostics() { - _ = String(cString: "string") // expected-warning {{'init(cString:)' is deprecated: Operate directly on the String}} + _ = String(cString: "string") // expected-warning {{'init(cString:)' is deprecated: Use a copy of the String argument}} - _ = String(validatingUTF8: "string") // expected-warning {{init(validatingUTF8:)' is deprecated: Operate directly on the String}} + _ = String(validatingUTF8: "string") // expected-warning {{init(validatingUTF8:)' is deprecated: Use a copy of the String argument}} - _ = String.decodeCString("string", as: Unicode.UTF8.self) // expected-warning {{'decodeCString(_:as:repairingInvalidCodeUnits:)' is deprecated: Operate directly on the String}} + _ = String.decodeCString("string", as: Unicode.UTF8.self) // expected-warning {{'decodeCString(_:as:repairingInvalidCodeUnits:)' is deprecated: Use a copy of the String argument}} - _ = String(decodingCString: "string", as: Unicode.UTF8.self) // expected-warning {{'init(decodingCString:as:)' is deprecated: Operate directly on the String}} + _ = String(decodingCString: "string", as: Unicode.UTF8.self) // expected-warning {{'init(decodingCString:as:)' is deprecated: Use a copy of the String argument}} } func checkInoutConversionOverloadCompilationDiagnostics() { var i = UInt8.zero - _ = String(cString: &i) // expected-warning {{'init(cString:)' is deprecated}} + _ = String(cString: &i) // expected-warning {{'init(cString:)' is deprecated: Use String(_ scalar: Unicode.Scalar)}} var c = CChar.zero - _ = String(cString: &c) // expected-warning {{'init(cString:)' is deprecated}} + _ = String(cString: &c) // expected-warning {{'init(cString:)' is deprecated: Use String(_ scalar: Unicode.Scalar)}} - _ = String(validatingUTF8: &c) // expected-warning {{init(validatingUTF8:)' is deprecated}} + _ = String(validatingUTF8: &c) // expected-warning {{init(validatingUTF8:)' is deprecated: Use String(_ scalar: Unicode.Scalar)}} var u = Unicode.UTF8.CodeUnit.zero - _ = String.decodeCString(&u, as: Unicode.UTF8.self) // expected-warning {{'decodeCString(_:as:repairingInvalidCodeUnits:)' is deprecated}} + _ = String.decodeCString(&u, as: Unicode.UTF8.self) // expected-warning {{'decodeCString(_:as:repairingInvalidCodeUnits:)' is deprecated: Use String(_ scalar: Unicode.Scalar)}} - _ = String(decodingCString: &u, as: Unicode.UTF8.self) // expected-warning {{'init(decodingCString:as:)' is deprecated}} + _ = String(decodingCString: &u, as: Unicode.UTF8.self) // expected-warning {{'init(decodingCString:as:)' is deprecated: Use String(_ scalar: Unicode.Scalar)}} } From c3256a3cbcf67298b01830d177c9e5f7f5b2e5ee Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 6 Apr 2022 04:18:17 -0600 Subject: [PATCH 21/80] [test] work around a linux testing issue --- test/stdlib/StringAPICString.swift | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index f95e61469c8e1..2d63d78fb299b 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -239,9 +239,13 @@ CStringTests.test("String.cString.with.Array.UInt8.input") { } } // no need to test every case; that is covered in other tests + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(cString:) must be null-terminated" ) + #endif _ = String(cString: [] as [UInt8]) expectUnreachable() } @@ -260,9 +264,13 @@ CStringTests.test("String.cString.with.Array.CChar.input") { } } // no need to test every case; that is covered in other tests + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(cString:) must be null-terminated" ) + #endif _ = String(cString: [] as [CChar]) expectUnreachable() } @@ -285,9 +293,13 @@ CStringTests.test("String.cString.with.inout.UInt8.conversion") { var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(cString:) must be null-terminated" ) + #endif str = String(cString: &c) expectUnreachable() } @@ -297,9 +309,13 @@ CStringTests.test("String.cString.with.inout.CChar.conversion") { var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(cString:) must be null-terminated" ) + #endif str = String(cString: &c) expectUnreachable() } @@ -319,9 +335,13 @@ CStringTests.test("String.validatingUTF8.with.Array.input") { } } // no need to test every case; that is covered in other tests + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(validatingUTF8:) must be null-terminated" ) + #endif _ = String(validatingUTF8: []) expectUnreachable() } @@ -347,9 +367,13 @@ CStringTests.test("String.validatingUTF8.with.inout.conversion") { expectNotNil(str) expectEqual(str?.isEmpty, true) c = 100 + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(validatingUTF8:) must be null-terminated" ) + #endif str = String(validatingUTF8: &c) expectUnreachable() } @@ -370,9 +394,13 @@ CStringTests.test("String.decodeCString.with.Array.input") { } } // no need to test every case; that is covered in other tests + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) + #endif _ = String.decodeCString([], as: Unicode.UTF8.self) expectUnreachable() } @@ -405,9 +433,13 @@ CStringTests.test("String.decodeCString.with.inout.conversion") { expectEqual(result?.result.isEmpty, true) expectEqual(result?.repairsMade, false) c = 100 + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) + #endif result = String.decodeCString(&c, as: Unicode.UTF8.self) expectUnreachable() } @@ -426,9 +458,13 @@ CStringTests.test("String.init.decodingCString.with.Array.input") { } } // no need to test every case; that is covered in other tests + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) + #endif _ = String(decodingCString: [], as: Unicode.UTF8.self) expectUnreachable() } @@ -451,9 +487,13 @@ CStringTests.test("String.init.decodingCString.with.inout.conversion") { var str = String(decodingCString: &c, as: Unicode.UTF8.self) expectEqual(str.isEmpty, true) c = 100 + #if os(Linux) + expectCrashLater() + #else expectCrashLater( withMessage: "input of String.init(decodingCString:as:) must be null-terminated" ) + #endif str = String(decodingCString: &c, as: Unicode.UTF8.self) expectUnreachable() } From 044f4365f9b276939d396965908ca5ae786c59a5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 6 Apr 2022 16:53:23 -0600 Subject: [PATCH 22/80] =?UTF-8?q?[test]=20just=20don=E2=80=99t=20check=20f?= =?UTF-8?q?or=20the=20crash=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/stdlib/StringAPICString.swift | 70 +++++++++--------------------- 1 file changed, 20 insertions(+), 50 deletions(-) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index 2d63d78fb299b..b395a1ae5c87a 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -239,13 +239,10 @@ CStringTests.test("String.cString.with.Array.UInt8.input") { } } // no need to test every case; that is covered in other tests - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(cString:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(cString:) must be null-terminated" ) - #endif _ = String(cString: [] as [UInt8]) expectUnreachable() } @@ -264,13 +261,10 @@ CStringTests.test("String.cString.with.Array.CChar.input") { } } // no need to test every case; that is covered in other tests - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(cString:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(cString:) must be null-terminated" ) - #endif _ = String(cString: [] as [CChar]) expectUnreachable() } @@ -293,13 +287,10 @@ CStringTests.test("String.cString.with.inout.UInt8.conversion") { var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(cString:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(cString:) must be null-terminated" ) - #endif str = String(cString: &c) expectUnreachable() } @@ -309,13 +300,10 @@ CStringTests.test("String.cString.with.inout.CChar.conversion") { var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(cString:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(cString:) must be null-terminated" ) - #endif str = String(cString: &c) expectUnreachable() } @@ -335,13 +323,10 @@ CStringTests.test("String.validatingUTF8.with.Array.input") { } } // no need to test every case; that is covered in other tests - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(validatingUTF8:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(validatingUTF8:) must be null-terminated" ) - #endif _ = String(validatingUTF8: []) expectUnreachable() } @@ -367,13 +352,10 @@ CStringTests.test("String.validatingUTF8.with.inout.conversion") { expectNotNil(str) expectEqual(str?.isEmpty, true) c = 100 - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(validatingUTF8:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(validatingUTF8:) must be null-terminated" ) - #endif str = String(validatingUTF8: &c) expectUnreachable() } @@ -394,13 +376,10 @@ CStringTests.test("String.decodeCString.with.Array.input") { } } // no need to test every case; that is covered in other tests - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) - #endif _ = String.decodeCString([], as: Unicode.UTF8.self) expectUnreachable() } @@ -433,13 +412,10 @@ CStringTests.test("String.decodeCString.with.inout.conversion") { expectEqual(result?.result.isEmpty, true) expectEqual(result?.repairsMade, false) c = 100 - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) - #endif result = String.decodeCString(&c, as: Unicode.UTF8.self) expectUnreachable() } @@ -458,13 +434,10 @@ CStringTests.test("String.init.decodingCString.with.Array.input") { } } // no need to test every case; that is covered in other tests - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" ) - #endif _ = String(decodingCString: [], as: Unicode.UTF8.self) expectUnreachable() } @@ -487,13 +460,10 @@ CStringTests.test("String.init.decodingCString.with.inout.conversion") { var str = String(decodingCString: &c, as: Unicode.UTF8.self) expectEqual(str.isEmpty, true) c = 100 - #if os(Linux) - expectCrashLater() - #else expectCrashLater( - withMessage: "input of String.init(decodingCString:as:) must be null-terminated" + // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) + // withMessage: "input of String.init(decodingCString:as:) must be null-terminated" ) - #endif str = String(decodingCString: &c, as: Unicode.UTF8.self) expectUnreachable() } From ece2965d59a3140150e0210b01f5ec54de9fafe4 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Sun, 10 Apr 2022 13:55:14 -0600 Subject: [PATCH 23/80] [test] check for availability before testing - These tests verify updated behaviour of existing API, and should check for availability first. --- test/stdlib/StringAPICString.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index b395a1ae5c87a..0cbbf48e5db9d 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -226,6 +226,7 @@ CStringTests.test("Substring.withCString") { } CStringTests.test("String.cString.with.Array.UInt8.input") { + guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -248,6 +249,7 @@ CStringTests.test("String.cString.with.Array.UInt8.input") { } CStringTests.test("String.cString.with.Array.CChar.input") { + guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -270,6 +272,7 @@ CStringTests.test("String.cString.with.Array.CChar.input") { } CStringTests.test("String.cString.with.String.input") { + guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var str = String(cString: "ab") @@ -283,6 +286,7 @@ CStringTests.test("String.cString.with.String.input") { } CStringTests.test("String.cString.with.inout.UInt8.conversion") { + guard #available(SwiftStdlib 5.7, *) else { return } var c = UInt8.zero var str = String(cString: &c) expectTrue(str.isEmpty) @@ -296,6 +300,7 @@ CStringTests.test("String.cString.with.inout.UInt8.conversion") { } CStringTests.test("String.cString.with.inout.CChar.conversion") { + guard #available(SwiftStdlib 5.7, *) else { return } var c = CChar.zero var str = String(cString: &c) expectTrue(str.isEmpty) @@ -309,6 +314,7 @@ CStringTests.test("String.cString.with.inout.CChar.conversion") { } CStringTests.test("String.validatingUTF8.with.Array.input") { + guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -332,6 +338,7 @@ CStringTests.test("String.validatingUTF8.with.Array.input") { } CStringTests.test("String.validatingUTF8.with.String.input") { + guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var str = String(validatingUTF8: "ab") @@ -347,6 +354,7 @@ CStringTests.test("String.validatingUTF8.with.String.input") { } CStringTests.test("String.validatingUTF8.with.inout.conversion") { + guard #available(SwiftStdlib 5.7, *) else { return } var c = CChar.zero var str = String(validatingUTF8: &c) expectNotNil(str) @@ -361,6 +369,7 @@ CStringTests.test("String.validatingUTF8.with.inout.conversion") { } CStringTests.test("String.decodeCString.with.Array.input") { + guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -385,6 +394,7 @@ CStringTests.test("String.decodeCString.with.Array.input") { } CStringTests.test("String.decodeCString.with.String.input") { + guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var result = String.decodeCString( @@ -404,6 +414,7 @@ CStringTests.test("String.decodeCString.with.String.input") { } CStringTests.test("String.decodeCString.with.inout.conversion") { + guard #available(SwiftStdlib 5.7, *) else { return } var c = Unicode.UTF8.CodeUnit.zero var result = String.decodeCString( &c, as: Unicode.UTF8.self, repairingInvalidCodeUnits: true @@ -421,6 +432,7 @@ CStringTests.test("String.decodeCString.with.inout.conversion") { } CStringTests.test("String.init.decodingCString.with.Array.input") { + guard #available(SwiftStdlib 5.7, *) else { return } do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -443,6 +455,7 @@ CStringTests.test("String.init.decodingCString.with.Array.input") { } CStringTests.test("String.init.decodingCString.with.String.input") { + guard #available(SwiftStdlib 5.7, *) else { return } let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } var str = String(decodingCString: "ab", as: Unicode.UTF8.self) @@ -456,6 +469,7 @@ CStringTests.test("String.init.decodingCString.with.String.input") { } CStringTests.test("String.init.decodingCString.with.inout.conversion") { + guard #available(SwiftStdlib 5.7, *) else { return } var c = Unicode.UTF8.CodeUnit.zero var str = String(decodingCString: &c, as: Unicode.UTF8.self) expectEqual(str.isEmpty, true) From b9dfe0cedc61d7ca1f15e12c6923cca2876fa682 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Apr 2022 10:26:14 -0700 Subject: [PATCH 24/80] Runtime: correct invalid C++ workaround The placement new operator is only available when `` has been included. Add the missing include to the header to allow us to get the definition of the placement new allocator. This allows us to remove the workaround that was there for Windows, and should hopefully repair the Android build as well. Thanks to @grynspan for helping identify the underlying issue! (cherry picked from commit 312f25874a7a8717422700a171f4520b009a5ce9) --- include/swift/Runtime/Atomic.h | 8 -------- stdlib/public/runtime/MetadataImpl.h | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/include/swift/Runtime/Atomic.h b/include/swift/Runtime/Atomic.h index 436be6b839f51..a5c6cb84bc682 100644 --- a/include/swift/Runtime/Atomic.h +++ b/include/swift/Runtime/Atomic.h @@ -95,14 +95,6 @@ struct aligned_alloc { free(ptr); #endif } - -#if defined(_WIN32) - // FIXME: why is this even needed? This is not permitted as per the C++ - // standrd new.delete.placement (§17.6.3.4). - [[nodiscard]] void *operator new(std::size_t size, void *where) noexcept { - return ::operator new(size, where); - } -#endif }; /// The default implementation for swift::atomic, which just wraps diff --git a/stdlib/public/runtime/MetadataImpl.h b/stdlib/public/runtime/MetadataImpl.h index ac455a8cc9938..c285f203070d6 100644 --- a/stdlib/public/runtime/MetadataImpl.h +++ b/stdlib/public/runtime/MetadataImpl.h @@ -51,6 +51,7 @@ #include "EnumImpl.h" #include +#include #include namespace swift { From d1eaa1615d9108ed8e7633eb71b6bd01c1ac9141 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Apr 2022 17:59:54 -0700 Subject: [PATCH 25/80] runtime: use explicit name for placement new The android build seems to find a conflict in the overload set for the placement new operator due to the custom new overload. Provide the indicator that we want the placement new allocator by using the explicitly namespaced spelling. Thanks to @buttaface for reporting the build failure. (cherry picked from commit ce6d97de38947c53997ca6e4a19de425ccb79f02) --- stdlib/public/runtime/MetadataImpl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/runtime/MetadataImpl.h b/stdlib/public/runtime/MetadataImpl.h index c285f203070d6..211d2b93512e8 100644 --- a/stdlib/public/runtime/MetadataImpl.h +++ b/stdlib/public/runtime/MetadataImpl.h @@ -100,11 +100,11 @@ struct NativeBox { } static T *initializeWithCopy(T *dest, T *src) { - return new (dest) T(*src); + return ::new (dest) T(*src); } static T *initializeWithTake(T *dest, T *src) { - T *result = new (dest) T(std::move(*src)); + T *result = ::new (dest) T(std::move(*src)); src->T::~T(); return result; } From 4ba9bcd226d0b16327694f4af7d7475a03149639 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 14 Apr 2022 06:52:25 -0700 Subject: [PATCH 26/80] Concurrency: include missing header Include the new header for placement new. (cherry picked from commit c5e99e453f1ac0a7140517773bbe760078cc2f79) --- stdlib/public/Concurrency/AsyncLet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/public/Concurrency/AsyncLet.cpp b/stdlib/public/Concurrency/AsyncLet.cpp index e1542cb18df91..70e2fd833de47 100644 --- a/stdlib/public/Concurrency/AsyncLet.cpp +++ b/stdlib/public/Concurrency/AsyncLet.cpp @@ -31,6 +31,8 @@ #include #endif +#include + using namespace swift; namespace { From d6ab6c69421ca3da70bd8cf41d5044eab06062bc Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 14 Apr 2022 14:20:03 -0700 Subject: [PATCH 27/80] runtime: blanket application of namespacing and inclusion of `new` Apply a blanket pass of including `new` for the placement new allocation and namespacing the call to the global placement new allocator. This should repair the Android ARMv7 builds. (cherry picked from commit a8b0ee24dcb2a8758b4854df226f17a203821d08) --- stdlib/public/Concurrency/Actor.cpp | 1 + stdlib/public/Concurrency/AsyncLet.cpp | 2 +- stdlib/public/Concurrency/Task.cpp | 11 ++++++----- stdlib/public/Concurrency/TaskGroup.cpp | 3 ++- stdlib/public/Concurrency/TaskLocal.cpp | 3 ++- stdlib/public/Concurrency/TaskPrivate.h | 5 +++-- stdlib/public/runtime/AccessibleFunction.cpp | 3 ++- stdlib/public/runtime/AnyHashableSupport.cpp | 4 +++- stdlib/public/runtime/AutoDiffSupport.cpp | 4 +++- stdlib/public/runtime/HeapObject.cpp | 3 ++- stdlib/public/runtime/KeyPaths.cpp | 3 ++- stdlib/public/runtime/Metadata.cpp | 4 ++-- stdlib/public/runtime/MetadataLookup.cpp | 5 +++-- stdlib/public/runtime/ProtocolConformance.cpp | 3 ++- stdlib/public/runtime/StackAllocator.h | 7 ++++--- stdlib/public/runtime/SwiftRT-COFF.cpp | 2 +- stdlib/public/runtime/SwiftRT-ELF.cpp | 2 +- stdlib/public/runtime/SwiftValue.mm | 4 +++- 18 files changed, 43 insertions(+), 26 deletions(-) diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 97bbd43c27d0c..350d82cf1d9b9 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -17,6 +17,7 @@ #include "swift/Runtime/Concurrency.h" #include +#include #ifdef _WIN32 // On Windows, an include below triggers an indirect include of minwindef.h diff --git a/stdlib/public/Concurrency/AsyncLet.cpp b/stdlib/public/Concurrency/AsyncLet.cpp index 70e2fd833de47..6ac84a265caf3 100644 --- a/stdlib/public/Concurrency/AsyncLet.cpp +++ b/stdlib/public/Concurrency/AsyncLet.cpp @@ -141,7 +141,7 @@ static AsyncLetImpl *asImpl(const AsyncLet *alet) { void swift::asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet, bool didAllocateInParentTask) { - AsyncLetImpl *impl = new (asyncLet) AsyncLetImpl(task); + AsyncLetImpl *impl = ::new (asyncLet) AsyncLetImpl(task); impl->setDidAllocateFromParentTask(didAllocateInParentTask); auto record = impl->getTaskRecord(); diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 1bdbea0bc1b6b..1d232e1ca2245 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -28,6 +28,7 @@ #include "Debug.h" #include "Error.h" #include +#include #if SWIFT_CONCURRENCY_ENABLE_DISPATCH #include @@ -770,20 +771,20 @@ static AsyncTaskAndContext swift_task_create_commonImpl( // Initialize the child fragment if applicable. if (parent) { auto childFragment = task->childFragment(); - new (childFragment) AsyncTask::ChildFragment(parent); + ::new (childFragment) AsyncTask::ChildFragment(parent); } // Initialize the group child fragment if applicable. if (group) { auto groupChildFragment = task->groupChildFragment(); - new (groupChildFragment) AsyncTask::GroupChildFragment(group); + ::new (groupChildFragment) AsyncTask::GroupChildFragment(group); } // Initialize the future fragment if applicable. if (futureResultType) { assert(task->isFuture()); auto futureFragment = task->futureFragment(); - new (futureFragment) FutureFragment(futureResultType); + ::new (futureFragment) FutureFragment(futureResultType); // Set up the context for the future so there is no error, and a successful // result will be written into the future fragment's storage. @@ -1202,7 +1203,7 @@ swift_task_addCancellationHandlerImpl( void *allocation = swift_task_alloc(sizeof(CancellationNotificationStatusRecord)); auto unsigned_handler = swift_auth_code(handler, 3848); - auto *record = new (allocation) + auto *record = ::new (allocation) CancellationNotificationStatusRecord(unsigned_handler, context); bool fireHandlerNow = false; @@ -1237,7 +1238,7 @@ swift_task_createNullaryContinuationJobImpl( void *allocation = swift_task_alloc(sizeof(NullaryContinuationJob)); auto *job = - new (allocation) NullaryContinuationJob( + ::new (allocation) NullaryContinuationJob( swift_task_getCurrent(), static_cast(priority), continuation); diff --git a/stdlib/public/Concurrency/TaskGroup.cpp b/stdlib/public/Concurrency/TaskGroup.cpp index fa29de226f1e7..60ff5d8670c21 100644 --- a/stdlib/public/Concurrency/TaskGroup.cpp +++ b/stdlib/public/Concurrency/TaskGroup.cpp @@ -33,6 +33,7 @@ #include "string" #include "queue" // TODO: remove and replace with usage of our mpsc queue #include +#include #include #if SWIFT_CONCURRENCY_ENABLE_DISPATCH #include @@ -469,7 +470,7 @@ SWIFT_CC(swift) static void swift_taskGroup_initializeImpl(TaskGroup *group, const Metadata *T) { SWIFT_TASK_DEBUG_LOG("creating task group = %p", group); - TaskGroupImpl *impl = new (group) TaskGroupImpl(T); + TaskGroupImpl *impl = ::new (group) TaskGroupImpl(T); auto record = impl->getTaskRecord(); assert(impl == record && "the group IS the task record"); diff --git a/stdlib/public/Concurrency/TaskLocal.cpp b/stdlib/public/Concurrency/TaskLocal.cpp index 2961403988ce8..dec28b157a012 100644 --- a/stdlib/public/Concurrency/TaskLocal.cpp +++ b/stdlib/public/Concurrency/TaskLocal.cpp @@ -24,6 +24,7 @@ #include "swift/ABI/Metadata.h" #include "llvm/ADT/PointerIntPair.h" #include "TaskPrivate.h" +#include #include #if SWIFT_STDLIB_HAS_ASL @@ -207,7 +208,7 @@ TaskLocal::Item::createLink(AsyncTask *task, size_t amountToAllocate = Item::itemSize(valueType); void *allocation = task ? _swift_task_alloc_specific(task, amountToAllocate) : malloc(amountToAllocate); - Item *item = new (allocation) Item(key, valueType); + Item *item = ::new (allocation) Item(key, valueType); auto next = task ? task->_private().Local.head : FallbackTaskLocalStorage::get()->head; diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index ed7daaaf0c81b..f5429db28330b 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -29,6 +29,7 @@ #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" #include +#include #define SWIFT_FATAL_ERROR swift_Concurrency_fatalError #include "../runtime/StackAllocator.h" @@ -655,11 +656,11 @@ AsyncTask::OpaquePrivateStorage::get() const { return reinterpret_cast(*this); } inline void AsyncTask::OpaquePrivateStorage::initialize(JobPriority basePri) { - new (this) PrivateStorage(basePri); + ::new (this) PrivateStorage(basePri); } inline void AsyncTask::OpaquePrivateStorage::initializeWithSlab( JobPriority basePri, void *slab, size_t slabCapacity) { - new (this) PrivateStorage(basePri, slab, slabCapacity); + ::new (this) PrivateStorage(basePri, slab, slabCapacity); } inline void AsyncTask::OpaquePrivateStorage::complete(AsyncTask *task) { get().complete(task); diff --git a/stdlib/public/runtime/AccessibleFunction.cpp b/stdlib/public/runtime/AccessibleFunction.cpp index 95e9678323bf6..e71cf09206af8 100644 --- a/stdlib/public/runtime/AccessibleFunction.cpp +++ b/stdlib/public/runtime/AccessibleFunction.cpp @@ -23,6 +23,7 @@ #include "swift/Runtime/Metadata.h" #include +#include using namespace swift; @@ -153,7 +154,7 @@ swift::runtime::swift_findAccessibleFunction(const char *targetNameStart, S.Cache.getOrInsert( name, [&](AccessibleFunctionCacheEntry *entry, bool created) { if (created) - new (entry) AccessibleFunctionCacheEntry{name, record}; + ::new (entry) AccessibleFunctionCacheEntry{name, record}; return true; }); } diff --git a/stdlib/public/runtime/AnyHashableSupport.cpp b/stdlib/public/runtime/AnyHashableSupport.cpp index 65d9e810bdbcf..3002ccca53b9f 100644 --- a/stdlib/public/runtime/AnyHashableSupport.cpp +++ b/stdlib/public/runtime/AnyHashableSupport.cpp @@ -21,6 +21,8 @@ #include "swift/Runtime/Debug.h" #include "swift/Runtime/HeapObject.h" +#include + using namespace swift; using namespace swift::hashable_support; @@ -103,7 +105,7 @@ findHashableBaseTypeImpl(const Metadata *type) { HashableConformances.getOrInsert(key, [&](HashableConformanceEntry *entry, bool created) { if (created) - new (entry) HashableConformanceEntry(key, baseTypeThatConformsToHashable); + ::new (entry) HashableConformanceEntry(key, baseTypeThatConformsToHashable); return true; // Keep the new entry. }); return baseTypeThatConformsToHashable; diff --git a/stdlib/public/runtime/AutoDiffSupport.cpp b/stdlib/public/runtime/AutoDiffSupport.cpp index 467ae2b3e37ce..6e8ec4d1d8190 100644 --- a/stdlib/public/runtime/AutoDiffSupport.cpp +++ b/stdlib/public/runtime/AutoDiffSupport.cpp @@ -14,6 +14,8 @@ #include "swift/ABI/Metadata.h" #include "swift/Runtime/HeapObject.h" +#include + using namespace swift; using namespace llvm; @@ -59,7 +61,7 @@ AutoDiffLinearMapContext *swift::swift_autoDiffCreateLinearMapContext( sizeof(AutoDiffLinearMapContext), alignof(AutoDiffLinearMapContext)) + topLevelLinearMapStructSize; auto *buffer = (AutoDiffLinearMapContext *)malloc(allocationSize); - return new (buffer) AutoDiffLinearMapContext; + return ::new (buffer) AutoDiffLinearMapContext; } void *swift::swift_autoDiffProjectTopLevelSubcontext( diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index 74c13899e52db..32f5b6aaf3021 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "../SwiftShims/GlobalObjects.h" #include "../SwiftShims/RuntimeShims.h" @@ -124,7 +125,7 @@ static HeapObject *_swift_allocObject_(HeapMetadata const *metadata, // NOTE: this relies on the C++17 guaranteed semantics of no null-pointer // check on the placement new allocator which we have observed on Windows, // Linux, and macOS. - new (object) HeapObject(metadata); + ::new (object) HeapObject(metadata); // If leak tracking is enabled, start tracking this object. SWIFT_LEAKS_START_TRACKING_OBJECT(object); diff --git a/stdlib/public/runtime/KeyPaths.cpp b/stdlib/public/runtime/KeyPaths.cpp index 4afed3e71ee1a..d504b2374b0ff 100644 --- a/stdlib/public/runtime/KeyPaths.cpp +++ b/stdlib/public/runtime/KeyPaths.cpp @@ -14,6 +14,7 @@ #include "swift/Runtime/Metadata.h" #include #include +#include using namespace swift; @@ -98,7 +99,7 @@ namespace { static OpaqueValue *allocateIn(const Metadata *type, YieldOnceBuffer *buffer) { auto *temp = - new (reinterpret_cast(buffer)) YieldOnceTemporary(type); + ::new (reinterpret_cast(buffer)) YieldOnceTemporary(type); return type->allocateBufferIn(&temp->Buffer); } diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index fda3470bc8e46..bca2539c68684 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2385,7 +2385,7 @@ static ValueWitnessTable *getMutableVWTableForInit(StructMetadata *self, // Otherwise, allocate permanent memory for it and copy the existing table. void *memory = allocateMetadata(sizeof(ValueWitnessTable), alignof(ValueWitnessTable)); - auto newTable = new (memory) ValueWitnessTable(*oldTable); + auto newTable = ::new (memory) ValueWitnessTable(*oldTable); // If we ever need to check layout-completeness asynchronously from // initialization, we'll need this to be a store-release (and rely on @@ -4650,7 +4650,7 @@ static const WitnessTable *_getForeignWitnessTable( ForeignWitnessTables.getOrInsert( key, [&](ForeignWitnessTableCacheEntry *entryPtr, bool created) { if (created) - new (entryPtr) + ::new (entryPtr) ForeignWitnessTableCacheEntry(key, witnessTableCandidate); result = entryPtr->data; return true; diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index d0815b7e04736..fde4220c496ee 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -38,6 +38,7 @@ #include #include #include +#include using namespace swift; using namespace Demangle; @@ -774,7 +775,7 @@ _findContextDescriptor(Demangle::NodePointer node, *entry, bool created) { if (created) - new (entry) NominalTypeDescriptorCacheEntry{mangledName, foundContext}; + ::new (entry) NominalTypeDescriptorCacheEntry{mangledName, foundContext}; return true; }); @@ -931,7 +932,7 @@ _findProtocolDescriptor(NodePointer node, *entry, bool created) { if (created) - new (entry) ProtocolDescriptorCacheEntry{mangledName, foundProtocol}; + ::new (entry) ProtocolDescriptorCacheEntry{mangledName, foundProtocol}; return true; }); } diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index c3ebd11cbf44b..856132b92452d 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -31,6 +31,7 @@ #include "ImageInspection.h" #include "Private.h" +#include #include #if __has_include() @@ -510,7 +511,7 @@ struct ConformanceState { SectionsToScan.snapshot().count() != sectionsCount) return false; // abandon the new entry - new (entry) ConformanceCacheEntry( + ::new (entry) ConformanceCacheEntry( ConformanceCacheKey(type, proto), witness); return true; // keep the new entry }); diff --git a/stdlib/public/runtime/StackAllocator.h b/stdlib/public/runtime/StackAllocator.h index 2f23ff41cf1c5..9ab0c84030722 100644 --- a/stdlib/public/runtime/StackAllocator.h +++ b/stdlib/public/runtime/StackAllocator.h @@ -25,6 +25,7 @@ #include "swift/Runtime/Debug.h" #include "llvm/Support/Alignment.h" #include +#include // Notes: swift::fatalError is not shared between libswiftCore and libswift_Concurrency // and libswift_Concurrency uses swift_Concurrency_fatalError instead. @@ -170,7 +171,7 @@ class StackAllocator { assert(llvm::isAligned(llvm::Align(alignment), alignedSize)); assert(canAllocate(alignedSize)); void *buffer = getAddr(currentOffset); - auto *allocation = new (buffer) Allocation(lastAllocation, this); + auto *allocation = ::new (buffer) Allocation(lastAllocation, this); currentOffset += Allocation::includingHeader(alignedSize); if (guardAllocations) { uintptr_t *endOfCurrentAllocation = (uintptr_t *)getAddr(currentOffset); @@ -251,7 +252,7 @@ class StackAllocator { size_t capacity = std::max(SlabCapacity, Allocation::includingHeader(size)); void *slabBuffer = malloc(Slab::includingHeader(capacity)); - Slab *newSlab = new (slabBuffer) Slab(capacity); + Slab *newSlab = ::new (slabBuffer) Slab(capacity); if (slab) slab->next = newSlab; else @@ -292,7 +293,7 @@ class StackAllocator { char *end = (char *)firstSlabBuffer + bufferCapacity; assert(start + Slab::headerSize() <= end && "buffer for first slab too small"); - firstSlab = new (start) Slab(end - start - Slab::headerSize()); + firstSlab = ::new (start) Slab(end - start - Slab::headerSize()); firstSlabIsPreallocated = true; numAllocatedSlabs = 0; } diff --git a/stdlib/public/runtime/SwiftRT-COFF.cpp b/stdlib/public/runtime/SwiftRT-COFF.cpp index 61fc81b3c6fc7..5f94fcbe25bb3 100644 --- a/stdlib/public/runtime/SwiftRT-COFF.cpp +++ b/stdlib/public/runtime/SwiftRT-COFF.cpp @@ -65,7 +65,7 @@ static void swift_image_constructor() { { reinterpret_cast(&__start_##name) + sizeof(__start_##name), \ reinterpret_cast(&__stop_##name) - reinterpret_cast(&__start_##name) - sizeof(__start_##name) } - new (§ions) swift::MetadataSections { + ::new (§ions) swift::MetadataSections { swift::CurrentSectionMetadataVersion, { __ImageBase }, diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp index 577843e449714..0c2253d50f93b 100644 --- a/stdlib/public/runtime/SwiftRT-ELF.cpp +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -56,7 +56,7 @@ static void swift_image_constructor() { { reinterpret_cast(&__start_##name), \ static_cast(&__stop_##name - &__start_##name) } - new (§ions) swift::MetadataSections { + ::new (§ions) swift::MetadataSections { swift::CurrentSectionMetadataVersion, { __dso_handle }, diff --git a/stdlib/public/runtime/SwiftValue.mm b/stdlib/public/runtime/SwiftValue.mm index b7ea34b864a61..31b8fc3561083 100644 --- a/stdlib/public/runtime/SwiftValue.mm +++ b/stdlib/public/runtime/SwiftValue.mm @@ -34,6 +34,8 @@ #include #include +#include + using namespace swift; using namespace swift::hashable_support; @@ -196,7 +198,7 @@ static size_t getSwiftValuePayloadAlignMask(const Metadata *type) { */ auto header = getSwiftValueHeader(instance); - new (header) SwiftValueHeader(); + ::new (header) SwiftValueHeader(); header->type = srcType; auto payload = getSwiftValuePayload(instance, alignMask); From ff69aae21bb548a01f741f800c784dd51d9e2ad2 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 13 Apr 2022 23:15:38 -0400 Subject: [PATCH 28/80] Ensure AtomicWaitQueue allocates its inner queues in an aligned fashion even when the compiler does not support the C++17 over-aligned new feature (and avoid using new anyway since it might be overridden by something else in the process.) (cherry picked from commit 3f24533da154acddeb9b2c186a2f95a8558ee259) --- include/swift/Runtime/AtomicWaitQueue.h | 12 +++---- include/swift/Runtime/HeapObject.h | 45 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/include/swift/Runtime/AtomicWaitQueue.h b/include/swift/Runtime/AtomicWaitQueue.h index cc0d99890d571..0985067828b25 100644 --- a/include/swift/Runtime/AtomicWaitQueue.h +++ b/include/swift/Runtime/AtomicWaitQueue.h @@ -20,6 +20,7 @@ #define SWIFT_RUNTIME_ATOMICWAITQUEUE_H #include "swift/Runtime/Heap.h" +#include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Mutex.h" #include @@ -84,7 +85,7 @@ class AtomicWaitQueue { /// global lock and while *not* holding the wait queue lock. void release_locked() { if (referenceCount == 1) { - delete &asImpl(); + swift_cxx_deleteObject(&asImpl()); } else { referenceCount--; } @@ -211,7 +212,7 @@ class AtomicWaitQueue { // If we created the queue but never published it, destroy it. if (CurrentQueue) { CurrentQueue->WaitQueueLock.unlock(); - delete CurrentQueue; + swift_cxx_deleteObject(CurrentQueue); } } @@ -425,12 +426,7 @@ class AtomicWaitQueue { private: template static Impl *createNewQueue(Args &&...args) { -#if !defined(__cpp_aligned_new) - static_assert(!swift::requires_aligned_alloc::value>::value || - is_aligned_alloc_aware::value, - "type is over-aligned for non-alignment aware operator new"); -#endif - auto queue = new Impl(std::forward(args)...); + auto queue = swift_cxx_newObject(std::forward(args)...); queue->WaitQueueLock.lock(); return queue; } diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index efb50b3f93c57..7825484670df1 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -19,6 +19,8 @@ #include #include +#include +#include #include "swift/Runtime/Config.h" #if SWIFT_OBJC_INTEROP @@ -131,6 +133,49 @@ void *swift_slowAlloc(size_t bytes, size_t alignMask); SWIFT_RUNTIME_EXPORT void swift_slowDealloc(void *ptr, size_t bytes, size_t alignMask); +/// Allocate and construct an instance of type \c T. +/// +/// \param args The arguments to pass to the constructor for \c T. +/// +/// \returns A pointer to a new, fully constructed instance of \c T. This +/// function never returns \c nullptr. The caller is responsible for +/// eventually destroying the resulting object by passing it to +/// \c swift_cxx_deleteObject(). +/// +/// This function avoids the use of the global \c operator \c new (which may be +/// overridden by other code in a process) in favor of calling +/// \c swift_slowAlloc() and constructing the new object with placement new. +/// +/// This function is capable of returning well-aligned memory even on platforms +/// that do not implement the C++17 "over-aligned new" feature. +template +static inline T *swift_cxx_newObject(Args &&... args) { + auto result = reinterpret_cast(swift_slowAlloc(sizeof(T), + alignof(T) - 1)); + ::new (result) T(std::forward(args)...); + return result; +} + +/// Destruct and deallocate an instance of type \c T. +/// +/// \param ptr A pointer to an instance of type \c T previously created with a +/// call to \c swift_cxx_newObject(). +/// +/// This function avoids the use of the global \c operator \c delete (which may +/// be overridden by other code in a process) in favor of directly calling the +/// destructor for \a *ptr and then freeing its memory by calling +/// \c swift_slowDealloc(). +/// +/// The effect of passing a pointer to this function that was \em not returned +/// from \c swift_cxx_newObject() is undefined. +template +static inline void swift_cxx_deleteObject(T *ptr) { + if (ptr) { + ptr->~T(); + swift_slowDealloc(ptr, sizeof(T), alignof(T) - 1); + } +} + /// Atomically increments the retain count of an object. /// /// \param object - may be null, in which case this is a no-op From a06bddb6e1fd6ef74fe01ac8e19fba7b8dfd5325 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Apr 2022 15:39:57 -0700 Subject: [PATCH 29/80] Make sure we don't provide duplicate synthesized conformance table entries. This was benign with `Sendable`, but is not benign for the `Encodable` and `Decodable` synthesis for distributed actors, which results in a crash in TBD generation. Fixes rdar://92008955. --- lib/AST/Module.cpp | 7 ------- lib/AST/ProtocolConformance.cpp | 10 +++++++++- test/Distributed/distributed_actor_layout.swift | 10 ++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index c47dd18591f62..29bb70e972c5c 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1051,13 +1051,6 @@ static bool shouldCreateMissingConformances(Type type, ProtocolDecl *proto) { return true; } - // A 'distributed actor' may have to create missing Codable conformances. - if (auto nominal = dyn_cast_or_null(type->getAnyNominal())) { - return nominal->isDistributedActor() && - (proto->isSpecificProtocol(swift::KnownProtocolKind::Decodable) || - proto->isSpecificProtocol(swift::KnownProtocolKind::Encodable)); - } - return false; } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 06376d0cbae22..a2aeb6595907e 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1532,8 +1532,16 @@ IterableDeclContext::getLocalConformances(ConformanceLookupKind lookupKind) // Look for a Sendable conformance globally. If it is synthesized // and matches this declaration context, use it. auto dc = getAsGenericContext(); + + SmallPtrSet known; for (auto conformance : findSynthesizedConformances(dc)) { - result.push_back(conformance); + // Compute the known set of conformances for the first time. + if (known.empty()) { + known.insert(result.begin(), result.end()); + } + + if (known.insert(conformance).second) + result.push_back(conformance); } break; } diff --git a/test/Distributed/distributed_actor_layout.swift b/test/Distributed/distributed_actor_layout.swift index 140189278811a..68f6e9e4ad71d 100644 --- a/test/Distributed/distributed_actor_layout.swift +++ b/test/Distributed/distributed_actor_layout.swift @@ -17,8 +17,18 @@ class MyClass { } // Ensure that the actor layout is (metadata pointer, default actor, id, system, // ) +protocol HasActorSystem { + var actorSystem: FakeActorSystem { get } +} + +extension MyActor: HasActorSystem { } + // CHECK: %T27distributed_actor_accessors7MyActorC = type <{ %swift.refcounted, %swift.defaultactor, %T27FakeDistributedActorSystems0C7AddressV, %T27FakeDistributedActorSystems0aC6SystemV, %T27distributed_actor_accessors7MyClassC* }> @available(SwiftStdlib 5.7, *) public distributed actor MyActor { var field: MyClass = MyClass() + + init(actorSystem: FakeActorSystem) { + self.actorSystem = actorSystem + } } From bc78aa93e2ddc845aee9125c8c9fee4486bfbb17 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Apr 2022 17:35:04 -0700 Subject: [PATCH 30/80] [Clang importer] Consider attributes on the typedef name for an anonymous tag. In C, one can provide a typedef name for an anonymous tag declaration in one shot, e.g., typedef struct { double x, y } Point; In this case, there are effectively two declarations at the C level: the typedef and the struct. The Clang importer was only taking attributes from the anonymous struct (i.e., the tag) and not from the typedef. However, any attributes put before the `typedef` should apply as well... so consider those, too. For now, only do this for `swift_attr` attributes, because we're seeing this primarily with `Sendable` annotations. In the future, we can look to generalizing it, but that could have source-breaking consequences. Fixes rdar://91632960. --- lib/ClangImporter/ImportDecl.cpp | 128 ++++++++++-------- test/ClangImporter/objc_async.swift | 10 +- .../usr/include/ObjCConcurrency.h | 5 + 3 files changed, 84 insertions(+), 59 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index d568a08b8fbf0..9da3a9e5d2d37 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -8787,74 +8787,86 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { Optional SeenMainActorAttr; PatternBindingInitializer *initContext = nullptr; - // - // __attribute__((swift_attr("attribute"))) - // - for (auto swiftAttr : ClangDecl->specific_attrs()) { - // FIXME: Hard-code @MainActor and @UIActor, because we don't have a - // point at which to do name lookup for imported entities. - if (isMainActorAttr(swiftAttr)) { - if (SeenMainActorAttr) { - // Cannot add main actor annotation twice. We'll keep the first - // one and raise a warning about the duplicate. - HeaderLoc attrLoc(swiftAttr->getLocation()); - diagnose(attrLoc, diag::import_multiple_mainactor_attr, - swiftAttr->getAttribute(), - SeenMainActorAttr.getValue()->getAttribute()); + auto importAttrsFromDecl = [&](const clang::NamedDecl *ClangDecl) { + // + // __attribute__((swift_attr("attribute"))) + // + for (auto swiftAttr : ClangDecl->specific_attrs()) { + // FIXME: Hard-code @MainActor and @UIActor, because we don't have a + // point at which to do name lookup for imported entities. + if (isMainActorAttr(swiftAttr)) { + if (SeenMainActorAttr) { + // Cannot add main actor annotation twice. We'll keep the first + // one and raise a warning about the duplicate. + HeaderLoc attrLoc(swiftAttr->getLocation()); + diagnose(attrLoc, diag::import_multiple_mainactor_attr, + swiftAttr->getAttribute(), + SeenMainActorAttr.getValue()->getAttribute()); + continue; + } + + if (Type mainActorType = SwiftContext.getMainActorType()) { + auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext); + auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr); + MappedDecl->getAttrs().add(attr); + SeenMainActorAttr = swiftAttr; + } + continue; } - if (Type mainActorType = SwiftContext.getMainActorType()) { - auto typeExpr = TypeExpr::createImplicit(mainActorType, SwiftContext); - auto attr = CustomAttr::create(SwiftContext, SourceLoc(), typeExpr); + // Hard-code @actorIndependent, until Objective-C clients start + // using nonisolated. + if (swiftAttr->getAttribute() == "@actorIndependent") { + auto attr = new (SwiftContext) NonisolatedAttr(/*isImplicit=*/true); MappedDecl->getAttrs().add(attr); - SeenMainActorAttr = swiftAttr; + continue; } - continue; - } - - // Hard-code @actorIndependent, until Objective-C clients start - // using nonisolated. - if (swiftAttr->getAttribute() == "@actorIndependent") { - auto attr = new (SwiftContext) NonisolatedAttr(/*isImplicit=*/true); - MappedDecl->getAttrs().add(attr); - continue; - } + // Dig out a buffer with the attribute text. + unsigned bufferID = getClangSwiftAttrSourceBuffer( + swiftAttr->getAttribute()); - // Dig out a buffer with the attribute text. - unsigned bufferID = getClangSwiftAttrSourceBuffer( - swiftAttr->getAttribute()); + // Dig out a source file we can use for parsing. + auto &sourceFile = getClangSwiftAttrSourceFile( + *MappedDecl->getDeclContext()->getParentModule()); - // Dig out a source file we can use for parsing. - auto &sourceFile = getClangSwiftAttrSourceFile( - *MappedDecl->getDeclContext()->getParentModule()); + // Spin up a parser. + swift::Parser parser( + bufferID, sourceFile, &SwiftContext.Diags, nullptr, nullptr); + // Prime the lexer. + parser.consumeTokenWithoutFeedingReceiver(); - // Spin up a parser. - swift::Parser parser( - bufferID, sourceFile, &SwiftContext.Diags, nullptr, nullptr); - // Prime the lexer. - parser.consumeTokenWithoutFeedingReceiver(); + bool hadError = false; + SourceLoc atLoc; + if (parser.consumeIf(tok::at_sign, atLoc)) { + hadError = parser.parseDeclAttribute( + MappedDecl->getAttrs(), atLoc, initContext, + /*isFromClangAttribute=*/true).isError(); + } else { + SourceLoc staticLoc; + StaticSpellingKind staticSpelling; + hadError = parser.parseDeclModifierList( + MappedDecl->getAttrs(), staticLoc, staticSpelling, + /*isFromClangAttribute=*/true); + } - bool hadError = false; - SourceLoc atLoc; - if (parser.consumeIf(tok::at_sign, atLoc)) { - hadError = parser.parseDeclAttribute( - MappedDecl->getAttrs(), atLoc, initContext, - /*isFromClangAttribute=*/true).isError(); - } else { - SourceLoc staticLoc; - StaticSpellingKind staticSpelling; - hadError = parser.parseDeclModifierList( - MappedDecl->getAttrs(), staticLoc, staticSpelling, - /*isFromClangAttribute=*/true); - } - - if (hadError) { - // Complain about the unhandled attribute or modifier. - HeaderLoc attrLoc(swiftAttr->getLocation()); - diagnose(attrLoc, diag::clang_swift_attr_unhandled, - swiftAttr->getAttribute()); + if (hadError) { + // Complain about the unhandled attribute or modifier. + HeaderLoc attrLoc(swiftAttr->getLocation()); + diagnose(attrLoc, diag::clang_swift_attr_unhandled, + swiftAttr->getAttribute()); + } + } + }; + importAttrsFromDecl(ClangDecl); + + // If the Clang declaration is from an anonymous tag that was given a + // name via a typedef, look for attributes on the typedef as well. + if (auto tag = dyn_cast(ClangDecl)) { + if (tag->getName().empty()) { + if (auto typedefDecl = tag->getTypedefNameForAnonDecl()) + importAttrsFromDecl(typedefDecl); } } diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 04528006c2675..4c2eaabc79f98 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -177,8 +177,16 @@ actor MySubclassCheckingSwiftAttributes : ProtocolWithSwiftAttributes { // Sendable conformance inference for imported types. func acceptCV(_: T) { } -func testCV(r: NSRange) { + +struct MyStruct: Sendable { + var range: NSRange + var inner: SendableStructWithNonSendable +} + +@available(SwiftStdlib 5.5, *) +func testCV(r: NSRange, someStruct: SendableStructWithNonSendable) async { acceptCV(r) + acceptCV(someStruct) } // Global actor (unsafe) isolation. diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index 10713b46eebc1..ab524142ef336 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -265,6 +265,11 @@ typedef NSString *NonSendableStringEnum NS_STRING_ENUM NONSENDABLE; typedef NSString *SendableStringStruct NS_EXTENSIBLE_STRING_ENUM; typedef NSString *NonSendableStringStruct NS_EXTENSIBLE_STRING_ENUM NONSENDABLE; +SENDABLE +typedef struct { + void *ptr; +} SendableStructWithNonSendable; + ASSUME_NONSENDABLE_END typedef id ObjectTypedef; From 72777aec0ad4a1de888b7ce8b75b9f9820020f51 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Apr 2022 23:54:35 -0700 Subject: [PATCH 31/80] Introduce missing Sendable conformances for existential conversions When performing conversions to an existential that involves Sendable, introducing missing conformances as needed to allow the type-check to succeed and then (later) they'll be diagnosed appropriately. Fixes rdar://89992095. --- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSSimplify.cpp | 3 +- lib/Sema/TypeCheckProtocol.cpp | 21 ++++++-- lib/Sema/TypeChecker.h | 3 +- test/Concurrency/sendable_existentials.swift | 54 ++++++++++++++++++++ 5 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 test/Concurrency/sendable_existentials.swift diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 90b61949fdb05..d7d3d914f3f3a 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5433,7 +5433,7 @@ collectExistentialConformances(Type fromType, Type toType, SmallVector conformances; for (auto proto : layout.getProtocols()) { conformances.push_back(TypeChecker::containsProtocol( - fromType, proto, module)); + fromType, proto, module, false, /*allowMissing=*/true)); } return toType->getASTContext().AllocateCopy(conformances); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 360fc136317d7..097d88520a14e 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7108,7 +7108,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( case ConstraintKind::SelfObjectOfProtocol: { auto conformance = TypeChecker::containsProtocol( type, protocol, DC->getParentModule(), - /*skipConditionalRequirements=*/true); + /*skipConditionalRequirements=*/true, + /*allowMissing=*/true); if (conformance) { return recordConformance(conformance); } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index c5860e5ed7d62..263d5255c1552 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5672,7 +5672,8 @@ void ConformanceChecker::emitDelayedDiags() { ProtocolConformanceRef TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M, - bool skipConditionalRequirements) { + bool skipConditionalRequirements, + bool allowMissing) { // Existential types don't need to conform, i.e., they only need to // contain the protocol. if (T->isExistentialType()) { @@ -5694,8 +5695,9 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M, if (auto superclass = layout.getSuperclass()) { auto result = (skipConditionalRequirements - ? M->lookupConformance(superclass, Proto) - : TypeChecker::conformsToProtocol(superclass, Proto, M)); + ? M->lookupConformance(superclass, Proto, allowMissing) + : TypeChecker::conformsToProtocol( + superclass, Proto, M, allowMissing)); if (result) { return result; } @@ -5713,13 +5715,22 @@ TypeChecker::containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M, return ProtocolConformanceRef(Proto); } + // FIXME: Unify with shouldCreateMissingConformances + if (allowMissing && + Proto->isSpecificProtocol(KnownProtocolKind::Sendable)) { + return ProtocolConformanceRef( + M->getASTContext().getBuiltinConformance( + T, Proto, GenericSignature(), { }, + BuiltinConformanceKind::Missing)); + } + return ProtocolConformanceRef::forInvalid(); } // For non-existential types, this is equivalent to checking conformance. return (skipConditionalRequirements - ? M->lookupConformance(T, Proto) - : TypeChecker::conformsToProtocol(T, Proto, M)); + ? M->lookupConformance(T, Proto, allowMissing) + : TypeChecker::conformsToProtocol(T, Proto, M, allowMissing)); } ProtocolConformanceRef diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index bb4011804d512..677bf149bf6f1 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -791,7 +791,8 @@ Expr *addImplicitLoadExpr( /// an empty optional. ProtocolConformanceRef containsProtocol(Type T, ProtocolDecl *Proto, ModuleDecl *M, - bool skipConditionalRequirements=false); + bool skipConditionalRequirements=false, + bool allowMissing=false); /// Determine whether the given type conforms to the given protocol. /// diff --git a/test/Concurrency/sendable_existentials.swift b/test/Concurrency/sendable_existentials.swift new file mode 100644 index 0000000000000..885d4b877e7c9 --- /dev/null +++ b/test/Concurrency/sendable_existentials.swift @@ -0,0 +1,54 @@ +// RUN: %target-typecheck-verify-swift -strict-concurrency=targeted +// REQUIRES: concurrency +// REQUIRES: OS=macosx + +@preconcurrency func send(_: Sendable) { } +func sendOpt(_: Sendable?) { } + +enum E { + case something(Sendable) +} + +@available(SwiftStdlib 5.1, *) +func testE(a: Any, aOpt: Any?) async { + send(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}} + sendOpt(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}} + sendOpt(aOpt) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}} + + let _: E = .something(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}} + _ = E.something(a) // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}} + + var sendable: Sendable + sendable = a // expected-warning{{type 'Any' does not conform to the 'Sendable' protocol}} + + var arrayOfSendable: [Sendable] + arrayOfSendable = [a, a] // expected-warning 2{{type 'Any' does not conform to the 'Sendable' protocol}} + + func localFunc() { } + sendable = localFunc // expected-warning{{type '() -> ()' does not conform to the 'Sendable' protocol}} + // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} + + _ = sendable + _ = arrayOfSendable +} + +func testESilently(a: Any, aOpt: Any?) { + send(a) + sendOpt(a) + sendOpt(aOpt) + + let _: E = .something(a) + _ = E.something(a) + + var sendable: Sendable + sendable = a + + var arrayOfSendable: [Sendable] + arrayOfSendable = [a, a] + + func localFunc() { } + sendable = localFunc + + _ = sendable + _ = arrayOfSendable +} From fc8cf54e51744800eaae47e413a54f0cd346509a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 23 Apr 2022 00:21:14 -0700 Subject: [PATCH 32/80] Fix testcase now that we get warnings instead of errors --- test/ClangImporter/objc_async.swift | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 04528006c2675..9cc37340faf9d 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -4,6 +4,7 @@ // REQUIRES: concurrency import Foundation import ObjCConcurrency +// expected-remark@-1{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'ObjCConcurrency'}} @available(SwiftStdlib 5.5, *) @MainActor func onlyOnMainActor() { } @@ -314,6 +315,7 @@ func check() async { _ = await BazFrame(size: 0) } +@available(SwiftStdlib 5.5, *) func testSender( sender: NXSender, sendableObject: SendableClass, @@ -325,7 +327,7 @@ func testSender( nonSendableGeneric: GenericObject, ptr: UnsafeMutableRawPointer, stringArray: [String] -) { +) async { sender.sendAny(sendableObject) sender.sendAny(nonSendableObject) // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} @@ -344,22 +346,22 @@ func testSender( sender.sendProto(sendableProtos) sender.sendProto(nonSendableProtos) - // expected-error@-1 {{argument type 'any LabellyProtocol & ObjCClub' does not conform to expected type 'Sendable'}} - // FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency + // expected-warning@-1 {{type 'any LabellyProtocol & ObjCClub' does not conform to the 'Sendable' protocol}} sender.sendProtos(sendableProtos) sender.sendProtos(nonSendableProtos) - // expected-error@-1 {{argument type 'any LabellyProtocol & ObjCClub' does not conform to expected type 'Sendable'}} - // FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency + // expected-warning@-1 {{type 'any LabellyProtocol & ObjCClub' does not conform to the 'Sendable' protocol}} sender.sendAnyArray([sendableObject]) sender.sendAnyArray([nonSendableObject]) - // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable; this is an error in Swift 6}} sender.sendGeneric(sendableGeneric) + // expected-warning@-1{{type 'GenericObject' does not conform to the 'Sendable' protocol}} + // FIXME: Shouldn't warn + sender.sendGeneric(nonSendableGeneric) - // expected-error@-1 {{argument type 'GenericObject' does not conform to expected type 'Sendable'}} - // FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency + // expected-warning@-1 {{type 'GenericObject' does not conform to the 'Sendable' protocol}} sender.sendPtr(ptr) sender.sendStringArray(stringArray) From eb6015655a72cedaecb5069eefe9c5a922d8e88d Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Fri, 22 Apr 2022 15:22:26 -0700 Subject: [PATCH 33/80] Trivial fix for an LICM assertion in projectLoadValue This seems to compile correctly in release builds. But it does go through an llvm_unreachable path, so really isn't safe to leave unfixed. When the accessPath has an offset, propagate it through the recursive calls. This may have happened when the offset was moved outside of the sequence of path indices. The code rebuilds a path from the indices without adding back the offset. LICM asserts during projectLoadValue when it needs to rematerialize a loaded value within the loop using projections and the loop-invariant address is an index_addr. Basically: %a = index_addr %4 : $*Wrapper, %1 : $Builtin.Word store %_ to %a : $*Wrapper br loop: loop: %f = struct_element_addr %a %v = load %f : $Value %s = struct $Wrapper (%v : $Value) store %s to %a : $*Wrapper Where the store inside the loop is deleted. And where the load is hoisted out of the loop, but now loads $Wrapper instead of $Value. Fixes rdar://92191909 (LICM assertion: isSubObjectProjection(), MemAccessUtils.h, line 1069) (cherry picked from commit c4079179eef0eb5023a809483704abbaed93b656) --- lib/SILOptimizer/LoopTransforms/LICM.cpp | 6 +- test/SILOptimizer/licm.sil | 75 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/LoopTransforms/LICM.cpp b/lib/SILOptimizer/LoopTransforms/LICM.cpp index 8fa06686347c2..0dcf02745863e 100644 --- a/lib/SILOptimizer/LoopTransforms/LICM.cpp +++ b/lib/SILOptimizer/LoopTransforms/LICM.cpp @@ -1173,7 +1173,8 @@ static SILValue projectLoadValue(SILValue addr, AccessPath accessPath, assert(ProjectionIndex(SEI).Index == elementIdx); SILValue val = projectLoadValue( SEI->getOperand(), - AccessPath(accessPath.getStorage(), pathNode.getParent(), 0), + AccessPath(accessPath.getStorage(), pathNode.getParent(), + accessPath.getOffset()), rootVal, rootAccessPath, beforeInst); SILBuilder B(beforeInst); return B.createStructExtract(beforeInst->getLoc(), val, SEI->getField(), @@ -1183,7 +1184,8 @@ static SILValue projectLoadValue(SILValue addr, AccessPath accessPath, assert(ProjectionIndex(TEI).Index == elementIdx); SILValue val = projectLoadValue( TEI->getOperand(), - AccessPath(accessPath.getStorage(), pathNode.getParent(), 0), + AccessPath(accessPath.getStorage(), pathNode.getParent(), + accessPath.getOffset()), rootVal, rootAccessPath, beforeInst); SILBuilder B(beforeInst); return B.createTupleExtract(beforeInst->getLoc(), val, TEI->getFieldIndex(), diff --git a/test/SILOptimizer/licm.sil b/test/SILOptimizer/licm.sil index ad5b7c2f1d594..8a5729a5bc88a 100644 --- a/test/SILOptimizer/licm.sil +++ b/test/SILOptimizer/licm.sil @@ -8,6 +8,13 @@ sil_stage canonical import Builtin import Swift +class Storage { + init() +} + +// globalArray +sil_global @globalArray : $Storage + // CHECK-LABEL: @memset // CHECK: bb0 @@ -1397,3 +1404,71 @@ bb6: return %15 : $() } +struct UInt64 { + @_hasStorage var _value: Builtin.Int64 { get set } + init(_value: Builtin.Int64) +} + +public struct UInt64Wrapper { + @_hasStorage public var rawValue: UInt64 { get set } + private init(_ rawValue: UInt64) + public init() +} + +// rdar://92191909 (LICM assertion: isSubObjectProjection(), MemAccessUtils.h, line 1069) +// +// projectLoadValue needs to rematerialize a loaded value within the +// loop using projections and the loop-invariant address is an +// index_addr. +// +// The store inside the loop is deleted, and the load is hoisted such +// that it now loads the UInt64Wrapper instead of Builtin.Int64 +// CHECK-LABEL: sil @testTailProjection : $@convention(thin) () -> () { +// CHECK: bb0: +// CHECK: [[A:%.*]] = index_addr %4 : $*UInt64Wrapper, %1 : $Builtin.Word +// CHECK: store %{{.*}} to [[A]] : $*UInt64Wrapper +// CHECK: load %5 : $*UInt64Wrapper +// CHECK: br bb1 +// CHECK: bb1(%{{.*}} : $Builtin.Int64, %{{.*}} : $UInt64Wrapper, [[PHI:%.*]] : $UInt64Wrapper): +// CHECK: cond_br undef, bb3, bb2 +// CHECK: bb2: +// CHECK-NOT: (load|store) +// CHECK: struct_extract [[PHI]] : $UInt64Wrapper, #UInt64Wrapper.rawValue +// CHECK: struct_extract +// CHECK: struct $UInt64 +// CHECK: struct $UInt64Wrapper +// CHECK-NOT: (load|store) +// CHECK: br bb1 +// CHECK: bb3: +// CHECK: store [[PHI]] to [[A]] : $*UInt64Wrapper +// CHECK-LABEL: } // end sil function 'testTailProjection' +sil @testTailProjection : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int64, 0 + %1 = integer_literal $Builtin.Word, 1 + %2 = integer_literal $Builtin.Word, 2 + %3 = alloc_ref [tail_elems $UInt64Wrapper * %2 : $Builtin.Word] $Storage + %4 = ref_tail_addr %3 : $Storage, $UInt64Wrapper + %5 = index_addr %4 : $*UInt64Wrapper, %1 : $Builtin.Word + %6 = struct $UInt64 (%0 : $Builtin.Int64) + %7 = struct $UInt64Wrapper (%6 : $UInt64) + store %7 to %5 : $*UInt64Wrapper + %9 = load %5 : $*UInt64Wrapper + br bb1(%0 : $Builtin.Int64, %9 : $UInt64Wrapper) + +bb1(%11 : $Builtin.Int64, %12 : $UInt64Wrapper): + cond_br undef, bb3, bb2 + +bb2: + %14 = struct_element_addr %5 : $*UInt64Wrapper, #UInt64Wrapper.rawValue + %15 = struct_element_addr %14 : $*UInt64, #UInt64._value + %16 = load %15 : $*Builtin.Int64 + %17 = struct $UInt64 (%16 : $Builtin.Int64) + %18 = struct $UInt64Wrapper (%17 : $UInt64) + store %18 to %5 : $*UInt64Wrapper + br bb1(%16 : $Builtin.Int64, %18 : $UInt64Wrapper) + +bb3: + %21 = tuple () + return %21 : $() +} From 1ff7fd9dd17bc236e42374f94b784dcd89de2011 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 23 Apr 2022 08:09:39 -0700 Subject: [PATCH 34/80] Fix test for the branch --- test/ClangImporter/objc_async.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 9cc37340faf9d..610530e02b7c3 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -354,7 +354,7 @@ func testSender( sender.sendAnyArray([sendableObject]) sender.sendAnyArray([nonSendableObject]) - // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable; this is an error in Swift 6}} + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} sender.sendGeneric(sendableGeneric) // expected-warning@-1{{type 'GenericObject' does not conform to the 'Sendable' protocol}} From cec31b560e20b6e3c22f6f10a24134972caf5657 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 7 Apr 2022 14:13:46 -0700 Subject: [PATCH 35/80] [SwiftCompiler/Regex] Use bridged DiagnosticEngine for error reporting This fixes: * An issue where the diagnostic messages were leaked * Diagnose at correct position inside the regex literal To do this: * Introduce 'Parse' SwiftCompiler module that is a bridging layer between '_CompilerRegexParser' and C++ libParse * Move libswiftParseRegexLiteral and libswiftLexRegexLiteral to 'Parse' Also this change makes 'SwiftCompilerSources/Package.swift' be configured by CMake so it can actually be built with 'swift-build'. rdar://92187284 (cherry picked from commit d292a95296ff3a6f424a13fbca0ed7ed5aa88a6b) --- SwiftCompilerSources/CMakeLists.txt | 20 +++ SwiftCompilerSources/Package.swift | 51 ------ SwiftCompilerSources/Package.swift.in | 92 +++++++++++ .../Sources/AST/DiagnosticEngine.swift | 6 + .../Sources/Basic/SourceLoc.swift | 6 + SwiftCompilerSources/Sources/CMakeLists.txt | 5 +- .../Sources/Optimizer/CMakeLists.txt | 5 +- .../PassManager/PassRegistration.swift | 8 +- .../Sources/Parse/CMakeLists.txt | 18 +++ .../Sources/Parse/Regex.swift | 146 ++++++++++++++++++ .../Sources/_RegexParser/CMakeLists.txt | 3 +- .../Sources/_RegexParser/Regex.swift | 6 - include/swift/AST/ASTBridging.h | 4 + include/swift/AST/BridgingUtils.h | 4 + include/swift/Parse/RegexParserBridging.h | 35 +++-- lib/Parse/Lexer.cpp | 24 +-- lib/Parse/ParseRegex.cpp | 19 +-- .../Parse/forward-slash-regex.swift | 9 +- test/StringProcessing/Parse/regex.swift | 4 +- .../Parse/regex_parse_end_of_buffer.swift | 5 +- .../Parse/regex_parse_error.swift | 48 +++--- 21 files changed, 377 insertions(+), 141 deletions(-) delete mode 100644 SwiftCompilerSources/Package.swift create mode 100644 SwiftCompilerSources/Package.swift.in create mode 100644 SwiftCompilerSources/Sources/Parse/CMakeLists.txt create mode 100644 SwiftCompilerSources/Sources/Parse/Regex.swift delete mode 100644 SwiftCompilerSources/Sources/_RegexParser/Regex.swift diff --git a/SwiftCompilerSources/CMakeLists.txt b/SwiftCompilerSources/CMakeLists.txt index 9e8bda8489566..4069e71c785fe 100644 --- a/SwiftCompilerSources/CMakeLists.txt +++ b/SwiftCompilerSources/CMakeLists.txt @@ -251,3 +251,23 @@ else() endif() +# Configure 'SwiftCompilerModules' SwiftPM package. The 'Package.swift' will +# be created at '${build_dir}/SwiftCompilerSources/Package.swift' and can be +# built with 'swift-build'. +# Note that this SwiftPM package itself is just for development purposes, and +# is not actually used for the compiler building. +set(swiftcompiler_source_dir_name "_Sources") +configure_file(Package.swift.in + "${CMAKE_CURRENT_BINARY_DIR}/Package.swift" @ONLY) +# SwiftPM requires all sources are inside the directory of 'Package.swift'. +# Create symlinks to the actual source directories. +execute_process(COMMAND + "${CMAKE_COMMAND}" -E create_symlink + "${CMAKE_CURRENT_SOURCE_DIR}/Sources" + "${CMAKE_CURRENT_BINARY_DIR}/${swiftcompiler_source_dir_name}") +if(SWIFT_BUILD_REGEX_PARSER_IN_COMPILER) + execute_process(COMMAND + "${CMAKE_COMMAND}" -E create_symlink + "${EXPERIMENTAL_STRING_PROCESSING_SOURCE_DIR}/Sources/_RegexParser" + "${CMAKE_CURRENT_BINARY_DIR}/_RegexParser_Sources") +endif() diff --git a/SwiftCompilerSources/Package.swift b/SwiftCompilerSources/Package.swift deleted file mode 100644 index a3c90e3ad278b..0000000000000 --- a/SwiftCompilerSources/Package.swift +++ /dev/null @@ -1,51 +0,0 @@ -// swift-tools-version:5.3 - -import PackageDescription - -let package = Package( - name: "SwiftCompilerSources", - platforms: [ - .macOS("10.9"), - ], - products: [ - .library( - name: "Swift", - type: .static, - targets: ["SIL", "Optimizer", "_CompilerRegexParser"]), - ], - dependencies: [ - ], - // Note that all modules must be added to LIBSWIFT_MODULES in the top-level - // CMakeLists.txt file to get debugging working. - targets: [ - .target( - name: "SIL", - dependencies: [], - swiftSettings: [SwiftSetting.unsafeFlags([ - "-I", "../include/swift", - "-cross-module-optimization" - ])]), - .target( - name: "_CompilerRegexParser", - dependencies: [], - path: "_RegexParser", - swiftSettings: [ - .unsafeFlags([ - "-I", "../include/swift", - "-cross-module-optimization", - ]), - // Workaround until `_RegexParser` is imported as implementation-only - // by `_StringProcessing`. - .unsafeFlags([ - "-Xfrontend", - "-disable-implicit-string-processing-module-import" - ])]), - .target( - name: "Optimizer", - dependencies: ["SIL", "_CompilerRegexParser"], - swiftSettings: [SwiftSetting.unsafeFlags([ - "-I", "../include/swift", - "-cross-module-optimization" - ])]), - ] -) diff --git a/SwiftCompilerSources/Package.swift.in b/SwiftCompilerSources/Package.swift.in new file mode 100644 index 0000000000000..bfbe0e9bd6a52 --- /dev/null +++ b/SwiftCompilerSources/Package.swift.in @@ -0,0 +1,92 @@ +// swift-tools-version:5.3 +//===--- Package.swift.in - SwiftCompiler SwiftPM package -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 - 2022 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 +// +//===----------------------------------------------------------------------===// + +// NOTE: This 'Package.swift.in' file is for CMake configure_file(). +// Generated 'Package.swift' can be found in +// '${swift_build_dir}/SwiftCompilerSources/Package.swift'. + +import PackageDescription + +private extension Target { + static let defaultSwiftSettings: [SwiftSetting] = [ + .unsafeFlags([ + "-Xfrontend", "-validate-tbd-against-ir=none", + "-Xfrontend", "-enable-cxx-interop", + // Bridging modules and headers + "-Xcc", "-I", "-Xcc", "@SWIFT_SOURCE_DIR@/include", + // Generated C headers + "-Xcc", "-I", "-Xcc", "@CMAKE_BINARY_DIR@/include", + "-cross-module-optimization" + ]), + ] + + static func compilerModuleTarget( + name: String, + dependencies: [Dependency], + path: String? = nil, + sources: [String]? = nil, + swiftSettings: [SwiftSetting] = []) -> Target { + .target( + name: name, + dependencies: dependencies, + path: path ?? "@swiftcompiler_source_dir_name@/\(name)", + exclude: ["CMakeLists.txt"], + sources: sources, + swiftSettings: defaultSwiftSettings + swiftSettings) + } +} + +let package = Package( + name: "SwiftCompilerSources", + platforms: [ + .macOS("10.9"), + ], + products: [ + .library( + name: "swiftCompilerModules", + type: .static, + targets: ["Basic", "AST", "Parse", "SIL", "Optimizer", "_CompilerRegexParser"]), + ], + dependencies: [ + ], + // Note that targets and their dependencies must align with + // 'SwiftCompilerSources/Sources/CMakeLists.txt' + targets: [ + .compilerModuleTarget( + name: "_CompilerRegexParser", + dependencies: [], + path: "_RegexParser_Sources", + swiftSettings: [ + // Workaround until `_CompilerRegexParser` is imported as implementation-only + // by `_StringProcessing`. + .unsafeFlags([ + "-Xfrontend", + "-disable-implicit-string-processing-module-import" + ])]), + .compilerModuleTarget( + name: "Basic", + dependencies: []), + .compilerModuleTarget( + name: "AST", + dependencies: ["Basic"]), + .compilerModuleTarget( + name: "Parse", + dependencies: ["Basic", "AST", "_CompilerRegexParser"]), + .compilerModuleTarget( + name: "SIL", + dependencies: ["Basic"]), + .compilerModuleTarget( + name: "Optimizer", + dependencies: ["Basic", "SIL", "Parse"]), + ] +) diff --git a/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift b/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift index 4ad82aedf90c7..78a56c7e8e798 100644 --- a/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift +++ b/SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift @@ -67,6 +67,12 @@ public struct DiagnosticEngine { public init(bridged: BridgedDiagnosticEngine) { self.bridged = bridged } + public init?(bridged: BridgedOptionalDiagnosticEngine) { + guard let object = bridged.object else { + return nil + } + self.bridged = BridgedDiagnosticEngine(object: object) + } public func diagnose(_ position: SourceLoc?, _ id: DiagID, diff --git a/SwiftCompilerSources/Sources/Basic/SourceLoc.swift b/SwiftCompilerSources/Sources/Basic/SourceLoc.swift index f5016a6a9f06a..1fa3dc731d64f 100644 --- a/SwiftCompilerSources/Sources/Basic/SourceLoc.swift +++ b/SwiftCompilerSources/Sources/Basic/SourceLoc.swift @@ -33,6 +33,12 @@ public struct SourceLoc { } } +extension SourceLoc { + public func advanced(by n: Int) -> SourceLoc { + SourceLoc(locationInFile: locationInFile.advanced(by: n))! + } +} + extension Optional where Wrapped == SourceLoc { public var bridged: BridgedSourceLoc { self?.bridged ?? .init(pointer: nil) diff --git a/SwiftCompilerSources/Sources/CMakeLists.txt b/SwiftCompilerSources/Sources/CMakeLists.txt index 515c94dc2de5a..af6900ff8d0e9 100644 --- a/SwiftCompilerSources/Sources/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/CMakeLists.txt @@ -8,10 +8,11 @@ # NOTE: Subdirectories must be added in dependency order. -add_subdirectory(Basic) -add_subdirectory(AST) if(SWIFT_BUILD_REGEX_PARSER_IN_COMPILER) add_subdirectory(_RegexParser) endif() +add_subdirectory(Basic) +add_subdirectory(AST) +add_subdirectory(Parse) add_subdirectory(SIL) add_subdirectory(Optimizer) diff --git a/SwiftCompilerSources/Sources/Optimizer/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/CMakeLists.txt index 929e0a54581ab..ed754dd792c94 100644 --- a/SwiftCompilerSources/Sources/Optimizer/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/CMakeLists.txt @@ -7,10 +7,7 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors set(dependencies) -list(APPEND dependencies Basic SIL) -if(SWIFT_BUILD_REGEX_PARSER_IN_COMPILER) - list(APPEND dependencies _CompilerRegexParser) -endif() +list(APPEND dependencies Basic SIL Parse) add_swift_compiler_module(Optimizer DEPENDS ${dependencies}) diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index 1f6c1f6cd9f2b..c21f8809f6f93 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -12,19 +12,13 @@ import SIL import OptimizerBridging - -#if canImport(_CompilerRegexParser) -import _CompilerRegexParser -#endif +import Parse @_cdecl("initializeSwiftModules") public func initializeSwiftModules() { registerSILClasses() registerSwiftPasses() - - #if canImport(_CompilerRegexParser) registerRegexParser() - #endif } private func registerPass( diff --git a/SwiftCompilerSources/Sources/Parse/CMakeLists.txt b/SwiftCompilerSources/Sources/Parse/CMakeLists.txt new file mode 100644 index 0000000000000..e4c52316ff1df --- /dev/null +++ b/SwiftCompilerSources/Sources/Parse/CMakeLists.txt @@ -0,0 +1,18 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2022 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See http://swift.org/LICENSE.txt for license information +# See http://swift.org/CONTRIBUTORS.txt for Swift project authors + +set(dependencies Basic AST) +if(SWIFT_BUILD_REGEX_PARSER_IN_COMPILER) + list(APPEND dependencies _CompilerRegexParser) +endif() + +add_swift_compiler_module(Parse + DEPENDS + ${dependencies} + SOURCES + Regex.swift) diff --git a/SwiftCompilerSources/Sources/Parse/Regex.swift b/SwiftCompilerSources/Sources/Parse/Regex.swift new file mode 100644 index 0000000000000..22d0c3aa2bcf0 --- /dev/null +++ b/SwiftCompilerSources/Sources/Parse/Regex.swift @@ -0,0 +1,146 @@ +//===--- Regex.swift - SourceLoc bridiging utilities ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 _RegexParserBridging +import AST +import Basic + +#if canImport(_CompilerRegexParser) +import _CompilerRegexParser + +public func registerRegexParser() { + Parser_registerRegexLiteralParsingFn(_RegexLiteralParsingFn) + Parser_registerRegexLiteralLexingFn(_RegexLiteralLexingFn) +} + +/// Bridging between C++ lexer and _CompilerRegexParser.lexRegex() +/// +/// Attempt to lex a regex literal string. +/// +/// - Parameters: +/// - CurPtrPtr: A pointer to the current pointer of lexer, which should be +/// the start of the literal. This will be advanced to the point +/// at which the lexer should resume, or will remain the same if +/// this is not a regex literal. +/// - BufferEndPtr: A pointer to the end of the buffer, which should not be +/// lexed past. +/// - mustBeRegex: A bool value whether an error during lexing should be +/// considered a regex literal, or some thing else. If true +/// advace the curPtrPtr and emit the diagnostic. If false, +/// curPtrPtr won't be modified. +/// - bridgedDiagnosticEngine: Diagnostic engine to emit diagnostics. +/// +/// - Returns: A bool indicating whether lexing was completely erroneous, and +/// cannot be recovered from, or false if there either was no error, +/// or there was a recoverable error. +private func _RegexLiteralLexingFn( + _ curPtrPtr: UnsafeMutablePointer>, + _ bufferEndPtr: UnsafePointer, + _ mustBeRegex: CBool, + _ bridgedDiagnosticEngine: BridgedOptionalDiagnosticEngine +) -> /*CompletelyErroneous*/ CBool { + let inputPtr = curPtrPtr.pointee + + do { + let (_, _, endPtr) = try lexRegex(start: inputPtr, end: bufferEndPtr) + curPtrPtr.pointee = endPtr.assumingMemoryBound(to: CChar.self) + return false + } catch let error as DelimiterLexError { + if !mustBeRegex { + // This token can be something else. Let the client fallback. + return false; + } + if error.kind == .unknownDelimiter { + // An unknown delimiter should be recovered from, as we may want to try + // lex something else. + return false + } + + if let diagEngine = DiagnosticEngine(bridged: bridgedDiagnosticEngine) { + // Emit diagnostic. + let startLoc = SourceLoc( + locationInFile: UnsafeRawPointer(inputPtr).assumingMemoryBound(to: UInt8.self))! + diagEngine.diagnose(startLoc, .regex_literal_parsing_error, "\(error)") + } + + // Advance the current pointer. + curPtrPtr.pointee = error.resumePtr.assumingMemoryBound(to: CChar.self) + + switch error.kind { + case .unterminated, .multilineClosingNotOnNewline: + // These can be recovered from. + return false + case .unprintableASCII, .invalidUTF8: + // We don't currently have good recovery behavior for these. + return true + case .unknownDelimiter: + fatalError("Already handled") + } + } catch { + fatalError("Should be a DelimiterLexError") + } +} + +/// Bridging between C++ parser and _CompilerRegexParser.parseWithDelimiters() +/// +/// - Parameters: +/// - inputPtr: A null-terminated C string. +/// - errOut: A buffer accepting an error string upon error. +/// - versionOut: A buffer accepting a regex literal format +/// version. +/// - captureStructureOut: A buffer accepting a byte sequence representing the +/// capture structure. +/// - captureStructureSize: The size of the capture structure buffer. Must be +/// greater than or equal to `strlen(inputPtr)`. +/// - bridgedDiagnosticBaseLoc: Source location of the start of the literal +/// - bridgedDiagnosticEngine: Diagnostic engine to emit diagnostics. +public func _RegexLiteralParsingFn( + _ inputPtr: UnsafePointer, + _ versionOut: UnsafeMutablePointer, + _ captureStructureOut: UnsafeMutableRawPointer, + _ captureStructureSize: CUnsignedInt, + _ bridgedDiagnosticBaseLoc: BridgedSourceLoc, + _ bridgedDiagnosticEngine: BridgedDiagnosticEngine +) -> Bool { + versionOut.pointee = currentRegexLiteralFormatVersion + + let str = String(cString: inputPtr) + do { + let ast = try parseWithDelimiters(str) + // Serialize the capture structure for later type inference. + assert(captureStructureSize >= str.utf8.count) + let buffer = UnsafeMutableRawBufferPointer( + start: captureStructureOut, count: Int(captureStructureSize)) + ast.captureStructure.encode(to: buffer) + return false; + } catch { + var diagLoc = SourceLoc(bridged: bridgedDiagnosticBaseLoc) + let diagEngine = DiagnosticEngine(bridged: bridgedDiagnosticEngine) + if let _diagLoc = diagLoc, + let locatedError = error as? LocatedErrorProtocol { + let offset = str.utf8.distance(from: str.startIndex, + to: locatedError.location.start) + diagLoc = _diagLoc.advanced(by: offset) + } + diagEngine.diagnose( + diagLoc, .regex_literal_parsing_error, + "cannot parse regular expression: \(String(describing: error))") + return true + } +} + +#else // canImport(_CompilerRegexParser) + +#warning("Regex parsing is disabled") +public func registerRegexParser() {} + +#endif // canImport(_CompilerRegexParser) diff --git a/SwiftCompilerSources/Sources/_RegexParser/CMakeLists.txt b/SwiftCompilerSources/Sources/_RegexParser/CMakeLists.txt index 32fc6f667c1c9..b1d2ae37978f7 100644 --- a/SwiftCompilerSources/Sources/_RegexParser/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/_RegexParser/CMakeLists.txt @@ -16,5 +16,4 @@ endforeach() message(STATUS "Using Experimental String Processing library for libswift _RegexParser (${EXPERIMENTAL_STRING_PROCESSING_SOURCE_DIR}).") add_swift_compiler_module(_CompilerRegexParser - "${LIBSWIFT_REGEX_PARSER_SOURCES}" - Regex.swift) + "${LIBSWIFT_REGEX_PARSER_SOURCES}") diff --git a/SwiftCompilerSources/Sources/_RegexParser/Regex.swift b/SwiftCompilerSources/Sources/_RegexParser/Regex.swift deleted file mode 100644 index fb484bd793529..0000000000000 --- a/SwiftCompilerSources/Sources/_RegexParser/Regex.swift +++ /dev/null @@ -1,6 +0,0 @@ -import _RegexParserBridging - -public func registerRegexParser() { - Parser_registerRegexLiteralParsingFn(libswiftParseRegexLiteral) - Parser_registerRegexLiteralLexingFn(libswiftLexRegexLiteral) -} diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 99ca2e68d6c18..daeb249a76497 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -65,6 +65,10 @@ typedef struct { void * _Nonnull object; } BridgedDiagnosticEngine; +typedef struct { + void *_Nullable object; +} BridgedOptionalDiagnosticEngine; + // FIXME: Can we bridge InFlightDiagnostic? void DiagnosticEngine_diagnose(BridgedDiagnosticEngine, BridgedSourceLoc loc, BridgedDiagID diagID, BridgedArrayRef arguments, diff --git a/include/swift/AST/BridgingUtils.h b/include/swift/AST/BridgingUtils.h index 0de9cb21b5831..6f5a0dfbbbe90 100644 --- a/include/swift/AST/BridgingUtils.h +++ b/include/swift/AST/BridgingUtils.h @@ -21,6 +21,10 @@ namespace swift { inline BridgedDiagnosticEngine getBridgedDiagnosticEngine(DiagnosticEngine *D) { return {(void *)D}; } +inline BridgedOptionalDiagnosticEngine +getBridgedOptionalDiagnosticEngine(DiagnosticEngine *D) { + return {(void *)D}; +} } // namespace swift diff --git a/include/swift/Parse/RegexParserBridging.h b/include/swift/Parse/RegexParserBridging.h index 5fa054cf03689..bc2f97ed2d9cf 100644 --- a/include/swift/Parse/RegexParserBridging.h +++ b/include/swift/Parse/RegexParserBridging.h @@ -14,6 +14,7 @@ #ifndef REGEX_PARSER_BRIDGING #define REGEX_PARSER_BRIDGING +#include "swift/AST/ASTBridging.h" #include #ifdef __cplusplus @@ -28,32 +29,40 @@ extern "C" { /// is not a regex literal. /// - BufferEnd: A pointer to the end of the buffer, which should not be lexed /// past. -/// - ErrorOut: If an error is encountered, this will be set to the error -/// string. +/// - MustBeRegex: whether an error during lexing should be considered a regex +/// literal, or some thing else. +/// - BridgedOptionalDiagnosticEngine: RegexLiteralLexingFn should diagnose the +/// token using this engine. /// /// Returns: A bool indicating whether lexing was completely erroneous, and /// cannot be recovered from, or false if there either was no error, /// or there was a recoverable error. -typedef bool (* RegexLiteralLexingFn)(/*CurPtrPtr*/ const char **, - /*BufferEnd*/ const char *, - /*ErrorOut*/ const char **); -void Parser_registerRegexLiteralLexingFn(RegexLiteralLexingFn fn); +typedef bool (*RegexLiteralLexingFn)( + /*CurPtrPtr*/ const char *_Nonnull *_Nonnull, + /*BufferEnd*/ const char *_Nonnull, + /*MustBeRegex*/ bool, BridgedOptionalDiagnosticEngine); +void Parser_registerRegexLiteralLexingFn(RegexLiteralLexingFn _Nullable fn); /// Parse a regex literal string. Takes the following arguments: /// /// - InputPtr: A null-terminated C string of the regex literal. -/// - ErrorOut: A buffer accepting an error string upon error. /// - VersionOut: A buffer accepting a regex literal format version. /// - CaptureStructureOut: A buffer accepting a byte sequence representing the /// capture structure of the literal. /// - CaptureStructureSize: The size of the capture structure buffer. Must be /// greater than or equal to `strlen(InputPtr) + 3`. -typedef void(* RegexLiteralParsingFn)(/*InputPtr*/ const char *, - /*ErrorOut*/ const char **, - /*VersionOut*/ unsigned *, - /*CaptureStructureOut*/ void *, - /*CaptureStructureSize*/ unsigned); -void Parser_registerRegexLiteralParsingFn(RegexLiteralParsingFn fn); +/// - DiagnosticBaseLoc: Start location of the regex literal. +/// - BridgedDiagnosticEngine: RegexLiteralParsingFn should diagnose the +/// parsing errors using this engine. +/// +/// Returns: A bool value indicating if there was an error while parsing. +typedef bool (*RegexLiteralParsingFn)(/*InputPtr*/ const char *_Nonnull, + /*VersionOut*/ unsigned *_Nonnull, + /*CaptureStructureOut*/ void *_Nonnull, + /*CaptureStructureSize*/ unsigned, + /*DiagnosticBaseLoc*/ BridgedSourceLoc, + BridgedDiagnosticEngine); +void Parser_registerRegexLiteralParsingFn(RegexLiteralParsingFn _Nullable fn); #ifdef __cplusplus } // extern "C" diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 4b08f2776efec..47b02a0ed5f11 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -14,27 +14,27 @@ // //===----------------------------------------------------------------------===// -#include "swift/Parse/Confusables.h" #include "swift/Parse/Lexer.h" +#include "swift/AST/BridgingUtils.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/AST/Identifier.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/SourceManager.h" +#include "swift/Parse/Confusables.h" #include "swift/Parse/RegexParserBridging.h" #include "swift/Syntax/Trivia.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" // FIXME: Figure out if this can be migrated to LLVM. #include "clang/Basic/CharInfo.h" #include // Regex lexing delivered via libSwift. -#include "swift/Parse/RegexParserBridging.h" static RegexLiteralLexingFn regexLiteralLexingFn = nullptr; void Parser_registerRegexLiteralLexingFn(RegexLiteralLexingFn fn) { regexLiteralLexingFn = fn; @@ -2039,25 +2039,18 @@ bool Lexer::tryLexRegexLiteral(const char *TokStart) { // Ask the Swift library to try and lex a regex literal. // - Ptr will not be advanced if this is not for a regex literal. - // - ErrStr will be set if there is any error to emit. // - CompletelyErroneous will be set if there was an error that cannot be // recovered from. auto *Ptr = TokStart; - const char *ErrStr = nullptr; - bool CompletelyErroneous = regexLiteralLexingFn(&Ptr, BufferEnd, &ErrStr); + bool CompletelyErroneous = regexLiteralLexingFn( + &Ptr, BufferEnd, MustBeRegex, + getBridgedOptionalDiagnosticEngine(getTokenDiags())); // If we didn't make any lexing progress, this isn't a regex literal and we // should fallback to lexing as something else. if (Ptr == TokStart) return false; - if (ErrStr) { - if (!MustBeRegex) - return false; - - diagnose(TokStart, diag::regex_literal_parsing_error, ErrStr); - } - // If we're lexing `/.../`, error if we ended on the opening of a comment. // We prefer to lex the comment as it's more likely than not that is what // the user is expecting. @@ -2078,7 +2071,6 @@ bool Lexer::tryLexRegexLiteral(const char *TokStart) { // If the lexing was completely erroneous, form an unknown token. if (CompletelyErroneous) { - assert(ErrStr); formToken(tok::unknown, TokStart); return true; } diff --git a/lib/Parse/ParseRegex.cpp b/lib/Parse/ParseRegex.cpp index c7fa4e95a2917..d0d3e3eae944c 100644 --- a/lib/Parse/ParseRegex.cpp +++ b/lib/Parse/ParseRegex.cpp @@ -14,9 +14,11 @@ // //===----------------------------------------------------------------------===// -#include "swift/Parse/Parser.h" +#include "swift/AST/BridgingUtils.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/Basic/BridgingUtils.h" #include "swift/Parse/ParsedSyntaxRecorder.h" +#include "swift/Parse/Parser.h" #include "swift/Parse/SyntaxParsingContext.h" #include "swift/Syntax/SyntaxKind.h" @@ -41,19 +43,18 @@ ParserResult Parser::parseExprRegexLiteral() { // Let the Swift library parse the contents, returning an error, or null if // successful. - // TODO: We need to be able to pass back a source location to emit the error - // at. - const char *errorStr = nullptr; unsigned version; auto capturesBuf = Context.AllocateUninitialized( RegexLiteralExpr::getCaptureStructureSerializationAllocationSize( regexText.size())); - regexLiteralParsingFn(regexText.str().c_str(), &errorStr, &version, - /*captureStructureOut*/ capturesBuf.data(), - /*captureStructureSize*/ capturesBuf.size()); + bool hadError = + regexLiteralParsingFn(regexText.str().c_str(), &version, + /*captureStructureOut*/ capturesBuf.data(), + /*captureStructureSize*/ capturesBuf.size(), + /*diagBaseLoc*/ getBridgedSourceLoc(Tok.getLoc()), + getBridgedDiagnosticEngine(&Diags)); auto loc = consumeToken(); - if (errorStr) { - diagnose(loc, diag::regex_literal_parsing_error, errorStr); + if (hadError) { return makeParserResult(new (Context) ErrorExpr(loc)); } return makeParserResult(RegexLiteralExpr::createParsed( diff --git a/test/StringProcessing/Parse/forward-slash-regex.swift b/test/StringProcessing/Parse/forward-slash-regex.swift index e416816c2e5b6..26d0a3040829c 100644 --- a/test/StringProcessing/Parse/forward-slash-regex.swift +++ b/test/StringProcessing/Parse/forward-slash-regex.swift @@ -242,16 +242,19 @@ baz(/,/) // expected-error@-2 {{missing argument for parameter #2 in call}} baz((/), /) +func bazbaz(_ x: (Int, Int) -> Int, _ y: Int) {} +bazbaz(/, 0) + func qux(_ x: (Int, Int) -> Int, _ y: T) -> Int { 0 } do { _ = qux(/, 1) / 2 - // expected-error@-1 {{cannot parse regular expression: closing ')' does not balance any groups openings}} - // expected-error@-2 {{expected ',' separator}} + // expected-error@-1:15 {{cannot parse regular expression: closing ')' does not balance any groups openings}} + // expected-error@-2:19 {{expected ',' separator}} } do { _ = qux(/, "(") / 2 // expected-error@-1 {{cannot convert value of type 'Regex<(Substring, Substring)>' to expected argument type '(Int, Int) -> Int'}} - // expected-error@-2 {{expected ',' separator}} + // expected-error@-2:21 {{expected ',' separator}} } _ = qux(/, 1) // this comment tests to make sure we don't try and end the regex on the starting '/' of '//'. diff --git a/test/StringProcessing/Parse/regex.swift b/test/StringProcessing/Parse/regex.swift index f351e503833c6..797fc2af85eec 100644 --- a/test/StringProcessing/Parse/regex.swift +++ b/test/StringProcessing/Parse/regex.swift @@ -19,5 +19,5 @@ _ = #/#/\/\#\\/# _ = ##/#|\|\#\\/## _ = (#/[*/#, #/+]/#, #/.]/#) -// expected-error@-1 {{cannot parse regular expression: quantifier '+' must appear after expression}} -// expected-error@-2 {{cannot parse regular expression: expected ']'}} +// expected-error@-1:16 {{cannot parse regular expression: quantifier '+' must appear after expression}} +// expected-error@-2:10 {{cannot parse regular expression: expected ']'}} diff --git a/test/StringProcessing/Parse/regex_parse_end_of_buffer.swift b/test/StringProcessing/Parse/regex_parse_end_of_buffer.swift index 53ef39918bd7e..5c62181c0c8c2 100644 --- a/test/StringProcessing/Parse/regex_parse_end_of_buffer.swift +++ b/test/StringProcessing/Parse/regex_parse_end_of_buffer.swift @@ -2,5 +2,6 @@ // REQUIRES: swift_in_compiler // Note there is purposefully no trailing newline here. -// expected-error@+1 {{unterminated regex literal}} -var unterminated = #/xy +// expected-error@+2:20 {{unterminated regex literal}} +// expected-error@+1:25 {{cannot parse regular expression: expected ')'}} +var unterminated = #/(xy \ No newline at end of file diff --git a/test/StringProcessing/Parse/regex_parse_error.swift b/test/StringProcessing/Parse/regex_parse_error.swift index b53500d8aed52..80e428469256d 100644 --- a/test/StringProcessing/Parse/regex_parse_error.swift +++ b/test/StringProcessing/Parse/regex_parse_error.swift @@ -1,28 +1,28 @@ // RUN: %target-typecheck-verify-swift -enable-bare-slash-regex -disable-availability-checking // REQUIRES: swift_in_compiler -_ = /(/ // expected-error {{expected ')'}} -_ = #/(/# // expected-error {{expected ')'}} +_ = /(/ // expected-error@:7 {{expected ')'}} +_ = #/(/# // expected-error@:8 {{expected ')'}} // FIXME: Should be 'group openings' -_ = /)/ // expected-error {{closing ')' does not balance any groups openings}} -_ = #/)/# // expected-error {{closing ')' does not balance any groups openings}} +_ = /)/ // expected-error@:6 {{closing ')' does not balance any groups openings}} +_ = #/)/# // expected-error@:7 {{closing ')' does not balance any groups openings}} -_ = #/\\/''/ // expected-error {{unterminated regex literal}} -_ = #/\| // expected-error {{unterminated regex literal}} -_ = #// // expected-error {{unterminated regex literal}} +_ = #/\\/''/ // expected-error@:5 {{unterminated regex literal}} +_ = #/\| // expected-error@:5 {{unterminated regex literal}} +_ = #// // expected-error@:5 {{unterminated regex literal}} -_ = #/xy // expected-error {{unterminated regex literal}} +_ = #/xy // expected-error@:5 {{unterminated regex literal}} -_ = #/(?/# // expected-error {{expected group specifier}} -_ = #/(?'/# // expected-error {{expected group name}} -_ = #/(?'abc/# // expected-error {{expected '''}} -_ = #/(?'abc /# // expected-error {{expected '''}} +_ = #/(?/# // expected-error@:7 {{expected group specifier}} +_ = #/(?'/# // expected-error@:10 {{expected group name}} +_ = #/(?'abc/# // expected-error@:13 {{expected '''}} +_ = #/(?'abc /# // expected-error@:13 {{expected '''}} do { _ = #/(?'a - // expected-error@-1 {{unterminated regex literal}} - // expected-error@-2 {{cannot parse regular expression: expected '''}} + // expected-error@-1:7 {{unterminated regex literal}} + // expected-error@-2:13 {{cannot parse regular expression: expected '''}} } _ = #/\(?'abc/# @@ -30,21 +30,21 @@ _ = #/\(?'abc/# do { _ = /\ / - // expected-error@-2 {{unterminated regex literal}} - // expected-error@-3 {{expected escape sequence}} -} // expected-error {{expected expression after operator}} + // expected-error@-2:7 {{unterminated regex literal}} + // expected-error@-3:9 {{expected escape sequence}} +} // expected-error@:1 {{expected expression after operator}} do { _ = #/\ /# - // expected-error@-2 {{unterminated regex literal}} - // expected-error@-3 {{expected escape sequence}} - // expected-error@-3 {{unterminated regex literal}} - // expected-warning@-4 {{regular expression literal is unused}} + // expected-error@-2:7 {{unterminated regex literal}} + // expected-error@-3:10 {{expected escape sequence}} + // expected-error@-3:3 {{unterminated regex literal}} + // expected-warning@-4:3 {{regular expression literal is unused}} } func foo(_ x: T, _ y: T) {} -foo(#/(?/#, #/abc/#) // expected-error {{expected group specifier}} -foo(#/(?C/#, #/abc/#) // expected-error {{expected ')'}} +foo(#/(?/#, #/abc/#) // expected-error@:7 {{expected group specifier}} +foo(#/(?C/#, #/abc/#) // expected-error@:10 {{expected ')'}} -foo(#/(?'/#, #/abc/#) // expected-error {{expected group name}} +foo(#/(?'/#, #/abc/#) // expected-error@:10 {{expected group name}} From 2ab7d83dd3a33ba13b0142785ad160592667d6ba Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 22 Apr 2022 23:16:52 -0700 Subject: [PATCH 36/80] [SourceKit] Remove an unnecessary link and an include from SourceKitSupport (cherry picked from commit 79bbbf1fa538d9bd6d468c32ffbd1f91cb155cf0) --- tools/SourceKit/lib/Support/CMakeLists.txt | 2 -- tools/SourceKit/lib/Support/Tracing.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/tools/SourceKit/lib/Support/CMakeLists.txt b/tools/SourceKit/lib/Support/CMakeLists.txt index 3b18cc18e98c9..c4e2cbcd31e03 100644 --- a/tools/SourceKit/lib/Support/CMakeLists.txt +++ b/tools/SourceKit/lib/Support/CMakeLists.txt @@ -7,7 +7,6 @@ add_sourcekit_library(SourceKitSupport UIDRegistry.cpp) target_link_libraries(SourceKitSupport PRIVATE swiftBasic - swiftSyntax clangBasic clangRewrite) if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) @@ -15,4 +14,3 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) dispatch BlocksRuntime) endif() - diff --git a/tools/SourceKit/lib/Support/Tracing.cpp b/tools/SourceKit/lib/Support/Tracing.cpp index 00df43a90044c..96eaa99de82a5 100644 --- a/tools/SourceKit/lib/Support/Tracing.cpp +++ b/tools/SourceKit/lib/Support/Tracing.cpp @@ -12,8 +12,6 @@ #include "SourceKit/Support/Tracing.h" -#include "swift/Frontend/Frontend.h" - #include "llvm/Support/Mutex.h" #include "llvm/Support/YAMLTraits.h" From 57463c976ce8ac3018bfd6ec3bfb0f453e9e6f1c Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Mon, 25 Apr 2022 09:40:27 -0700 Subject: [PATCH 37/80] Resolve a structural calculation error with clocks that prevented sleep to properly transmit to dispatch_after (#42526) * Resolve a structural calculation error with clocks that prevented sleep to properly transmit to dispatch_after (#42518) * Resolve a structural calculation error with clocks that prevented sleep to properly transmit to dispatch_after * Remove dispatch dependency * Move clock runtime functions to the same compliation build group as the task sleep build group (#42566) --- stdlib/public/Concurrency/CMakeLists.txt | 2 +- .../Concurrency/DispatchGlobalExecutor.inc | 59 +++------------ test/Concurrency/Runtime/clock.swift | 71 +++++++++++++++++++ 3 files changed, 82 insertions(+), 50 deletions(-) create mode 100644 test/Concurrency/Runtime/clock.swift diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 057322d6baff4..06752c398d077 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -12,7 +12,6 @@ if(NOT swift_concurrency_extra_sources) set(swift_concurrency_extra_sources - Clock.cpp Clock.swift ContinuousClock.swift SuspendingClock.swift @@ -76,6 +75,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I AsyncLet.cpp AsyncLet.swift CheckedContinuation.swift + Clock.cpp GlobalExecutor.cpp Errors.swift Error.cpp diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.inc b/stdlib/public/Concurrency/DispatchGlobalExecutor.inc index 8c5337f9e2bfc..858725f35c987 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.inc +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.inc @@ -225,44 +225,7 @@ static void swift_task_enqueueGlobalWithDelayImpl(JobDelay delay, dispatch_after_f(when, queue, dispatchContext, dispatchFunction); } -// TODO: The following is cribbed from libdispatch, we should replace it with an official API -typedef enum { - DISPATCH_CLOCK_UPTIME, - DISPATCH_CLOCK_MONOTONIC, - DISPATCH_CLOCK_WALL, -#define DISPATCH_CLOCK_COUNT (DISPATCH_CLOCK_WALL + 1) -} dispatch_clock_t; - #define DISPATCH_UP_OR_MONOTONIC_TIME_MASK (1ULL << 63) -#define DISPATCH_WALLTIME_MASK (1ULL << 62) -#define DISPATCH_TIME_MAX_VALUE (DISPATCH_WALLTIME_MASK - 1) -static inline uint64_t -_dispatch_time_nano2mach(uint64_t value) { -#if HAS_MACH_TIME - struct mach_timebase_info info = calculateTimebase(); - return (value * info.denom) / info.numer; -#else - return value; -#endif -} - -static inline dispatch_time_t -_dispatch_clock_and_value_to_time(dispatch_clock_t clock, uint64_t value) -{ - if (value >= DISPATCH_TIME_MAX_VALUE) { - return DISPATCH_TIME_FOREVER; - } - switch (clock) { - case DISPATCH_CLOCK_WALL: - return -_dispatch_time_nano2mach(value); - case DISPATCH_CLOCK_UPTIME: - return _dispatch_time_nano2mach(value); - case DISPATCH_CLOCK_MONOTONIC: - return _dispatch_time_nano2mach(value) | DISPATCH_UP_OR_MONOTONIC_TIME_MASK; - } - __builtin_unreachable(); -} -// END: REPLACEMENT SWIFT_CC(swift) static void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, @@ -282,18 +245,16 @@ static void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, job->SchedulerPrivate[Job::DispatchQueueIndex] = DISPATCH_QUEUE_GLOBAL_EXECUTOR; - dispatch_time_t when; - switch (clock) { - case swift_clock_id_continuous: { - uint64_t q = sec * NSEC_PER_SEC + nsec; - when = _dispatch_clock_and_value_to_time(DISPATCH_CLOCK_MONOTONIC, q); - break; - } - case swift_clock_id_suspending: { - uint64_t q = sec * NSEC_PER_SEC + nsec; - when = _dispatch_clock_and_value_to_time(DISPATCH_CLOCK_UPTIME, q); - break; - } + long long nowSec; + long long nowNsec; + swift_get_time(&nowSec, &nowNsec, (swift_clock_id)clock); + + uint64_t delta = (sec - nowSec) * NSEC_PER_SEC + nsec - nowNsec; + + dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delta); + + if (clock == swift_clock_id_continuous) { + when |= DISPATCH_UP_OR_MONOTONIC_TIME_MASK; } // TODO: this should pass the leeway/tolerance along when it is not -1 nanoseconds // either a dispatch_source can be created or a better dispatch_after_f can be made for this diff --git a/test/Concurrency/Runtime/clock.swift b/test/Concurrency/Runtime/clock.swift new file mode 100644 index 0000000000000..55f24291d8691 --- /dev/null +++ b/test/Concurrency/Runtime/clock.swift @@ -0,0 +1,71 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: executable_test +// REQUIRES: concurrency_runtime + +import _Concurrency +import StdlibUnittest + +var tests = TestSuite("Time") + +@main struct Main { + static func main() async { + tests.test("ContinuousClock sleep") { + let clock = ContinuousClock() + let elapsed = await clock.measure { + try! await clock.sleep(until: .now + .milliseconds(100)) + } + // give a reasonable range of expected elapsed time + expectTrue(elapsed > .milliseconds(90)) + expectTrue(elapsed < .milliseconds(200)) + } + + tests.test("SuspendingClock sleep") { + let clock = SuspendingClock() + let elapsed = await clock.measure { + try! await clock.sleep(until: .now + .milliseconds(100)) + } + // give a reasonable range of expected elapsed time + expectTrue(elapsed > .milliseconds(90)) + expectTrue(elapsed < .milliseconds(200)) + } + + tests.test("duration addition") { + let d1 = Duration.milliseconds(500) + let d2 = Duration.milliseconds(500) + let d3 = Duration.milliseconds(-500) + let sum = d1 + d2 + expectEqual(sum, .seconds(1)) + let comps = sum.components + expectEqual(comps.seconds, 1) + expectEqual(comps.attoseconds, 0) + let adjusted = sum + d3 + expectEqual(adjusted, .milliseconds(500)) + } + + tests.test("duration subtraction") { + let d1 = Duration.nanoseconds(500) + let d2 = d1 - .nanoseconds(100) + expectEqual(d2, .nanoseconds(400)) + let d3 = d1 - .nanoseconds(500) + expectEqual(d3, .nanoseconds(0)) + let d4 = d1 - .nanoseconds(600) + expectEqual(d4, .nanoseconds(-100)) + } + + tests.test("duration division") { + let d1 = Duration.seconds(1) + let halfSecond = d1 / 2 + expectEqual(halfSecond, .milliseconds(500)) + } + + tests.test("duration multiplication") { + let d1 = Duration.seconds(1) + let twoSeconds = d1 * 2 + expectEqual(twoSeconds, .seconds(2)) + } + + await runAllTestsAsync() + } +} \ No newline at end of file From 65ff7ae92386284055bf9051617a28365374cf16 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 25 Apr 2022 11:00:37 -0700 Subject: [PATCH 38/80] Revert "[SR-15703] Fix missing hop in error path of foreign async throws call" This reverts commit afd26d397475fc7a54538718df45f1f0f66e36a8 to solve a critical miscompile caused by a hop_to_executor appearing between a get_continuation and await_continuation instruction in SIL. A reimplementation of SR-15703 will be forthcoming. Fixes rdar://91502776 --- lib/SILGen/ExecutorBreadcrumb.h | 5 ---- lib/SILGen/SILGenApply.cpp | 29 ++++-------------- test/SILGen/objc_async.swift | 40 ------------------------- test/SILGen/objc_async_from_swift.swift | 17 +++-------- 4 files changed, 9 insertions(+), 82 deletions(-) diff --git a/lib/SILGen/ExecutorBreadcrumb.h b/lib/SILGen/ExecutorBreadcrumb.h index e2734e488823d..32c4edea1170e 100644 --- a/lib/SILGen/ExecutorBreadcrumb.h +++ b/lib/SILGen/ExecutorBreadcrumb.h @@ -37,11 +37,6 @@ class ExecutorBreadcrumb { // Emits the hop back sequence, if any, necessary to get back to // the executor represented by this breadcrumb. void emit(SILGenFunction &SGF, SILLocation loc); - -#ifndef NDEBUG - // FOR ASSERTS ONLY: returns true if calling `emit` will emit a hop-back. - bool needsEmit() const { return mustReturnToExecutor; } -#endif }; } // namespace Lowering diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 32afacc5e5bc9..406da583a5a51 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4427,27 +4427,6 @@ class FixLifetimeDestroyCleanup : public Cleanup { #endif } }; - -class EmitBreadcrumbCleanup : public Cleanup { - ExecutorBreadcrumb breadcrumb; - -public: - EmitBreadcrumbCleanup(ExecutorBreadcrumb &&breadcrumb) - : breadcrumb(std::move(breadcrumb)) {} - - void emit(SILGenFunction &SGF, CleanupLocation l, - ForUnwind_t forUnwind) override { - breadcrumb.emit(SGF, l); - } - - void dump(SILGenFunction &SGF) const override { -#ifndef NDEBUG - llvm::errs() << "EmitBreadcrumbCleanup " - << "State:" << getState() - << "NeedsEmit:" << breadcrumb.needsEmit(); -#endif - } -}; } // end anonymous namespace //===----------------------------------------------------------------------===// @@ -4694,14 +4673,12 @@ RValue SILGenFunction::emitApply( *foreignError, calleeTypeInfo.foreign.async); } - // For objc async calls, push cleanups to be used on + // For objc async calls, push cleanup to be used on // both result and throw paths prior to finishing the result plan. if (calleeTypeInfo.foreign.async) { for (auto unmanagedCopy : unmanagedCopies) { Cleanups.pushCleanup(unmanagedCopy); } - // save breadcrumb as a clean-up so it is emitted in result / throw cases. - Cleanups.pushCleanup(std::move(breadcrumb)); } else { assert(unmanagedCopies.empty()); } @@ -4711,6 +4688,10 @@ RValue SILGenFunction::emitApply( directResultsArray, bridgedForeignError); assert(directResultsArray.empty() && "didn't claim all direct results"); + if (calleeTypeInfo.foreign.async) { + breadcrumb.emit(*this, loc); + } + return result; } diff --git a/test/SILGen/objc_async.swift b/test/SILGen/objc_async.swift index bd2f04a309ebe..a5e9174a11d99 100644 --- a/test/SILGen/objc_async.swift +++ b/test/SILGen/objc_async.swift @@ -201,43 +201,3 @@ func testSlowServerFromMain(slowServer: SlowServer) async throws { // CHECK: dealloc_stack [[RESUME_BUF]] let _: Int = await slowServer.doSomethingSlow("mail") } - -// CHECK-LABEL: sil {{.*}}@${{.*}}26testThrowingMethodFromMain -@MainActor -func testThrowingMethodFromMain(slowServer: SlowServer) async -> String { -// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $String -// CHECK: [[STRING_ARG:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@guaranteed String) -> @owned NSString -// CHECK: [[METH:%.*]] = objc_method {{%.*}} : $SlowServer, #SlowServer.doSomethingDangerous!foreign -// CHECK: [[RAW_CONT:%.*]] = get_async_continuation_addr [throws] String, [[RESULT_BUF]] : $*String -// CHECK: [[CONT:%.*]] = struct $UnsafeContinuation ([[RAW_CONT]] : $Builtin.RawUnsafeContinuation) -// CHECK: [[STORE_ALLOC:%.*]] = alloc_stack $@block_storage UnsafeContinuation -// CHECK: [[PROJECTED:%.*]] = project_block_storage [[STORE_ALLOC]] : $*@block_storage -// CHECK: store [[CONT]] to [trivial] [[PROJECTED]] : $*UnsafeContinuation -// CHECK: [[INVOKER:%.*]] = function_ref @$sSo8NSStringCSgSo7NSErrorCSgIeyByy_SSTz_ -// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORE_ALLOC]] {{.*}}, invoke [[INVOKER]] -// CHECK: [[OPTIONAL_BLK:%.*]] = enum {{.*}}, #Optional.some!enumelt, [[BLOCK]] -// CHECK: %28 = apply [[METH]]([[STRING_ARG]], [[OPTIONAL_BLK]], {{%.*}}) : $@convention(objc_method) (NSString, Optional<@convention(block) (Optional, Optional) -> ()>, SlowServer) -> () -// CHECK: [[STRING_ARG_COPY:%.*]] = copy_value [[STRING_ARG]] : $NSString -// CHECK: dealloc_stack [[STORE_ALLOC]] : $*@block_storage UnsafeContinuation -// CHECK: destroy_value [[STRING_ARG]] : $NSString -// CHECK: await_async_continuation [[RAW_CONT]] : $Builtin.RawUnsafeContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]] - -// CHECK: [[RESUME]] -// CHECK: {{.*}} = load [take] [[RESULT_BUF]] : $*String -// CHECK: hop_to_executor {{%.*}} : $MainActor -// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString -// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString -// CHECK: dealloc_stack [[RESULT_BUF]] : $*String - -// CHECK: [[ERROR]] -// CHECK: hop_to_executor {{%.*}} : $MainActor -// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString -// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString -// CHECK: dealloc_stack [[RESULT_BUF]] : $*String - - do { - return try await slowServer.doSomethingDangerous("run-with-scissors") - } catch { - return "none" - } -} diff --git a/test/SILGen/objc_async_from_swift.swift b/test/SILGen/objc_async_from_swift.swift index 40500c664eebf..85dd87c9c83ed 100644 --- a/test/SILGen/objc_async_from_swift.swift +++ b/test/SILGen/objc_async_from_swift.swift @@ -25,28 +25,19 @@ func testSlowServing(p: SlowServing) async throws { // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : let _: String = await p.requestString() + // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional, Optional) -> (), τ_0_0) -> () + // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : + let _: String = try await p.tryRequestString() + // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, NSString) -> (), τ_0_0) -> () // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : let _: (Int, String) = await p.requestIntAndString() // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, Optional, Optional) -> (), τ_0_0) -> () // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : - // CHECK: builtin "willThrow" - // CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] : let _: (Int, String) = try await p.tryRequestIntAndString() } -// CHECK-LABEL: sil {{.*}}@{{.*}}20testSlowServingAgain -func testSlowServingAgain(p: SlowServing) async throws { - // CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional, #Optional.none - // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : - // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional, Optional) -> (), τ_0_0) -> () - // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : - // CHECK: builtin "willThrow" - // CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] : - let _: String = try await p.tryRequestString() -} - class SlowSwiftServer: NSObject, SlowServing { // CHECK-LABEL: sil {{.*}} @$s21objc_async_from_swift15SlowSwiftServerC10requestIntSiyYaF // CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional, #Optional.none From 8b5aaae94dcb53876026688c5b8aa0eb895388b6 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 25 Apr 2022 12:12:26 -0700 Subject: [PATCH 39/80] Revert "Simplify lifetime extension for async ObjC calls." This reverts commit 01d470ce3280c9d04ad32420aae1d9cd4dacaae3. Just to be safe, I'm reverting this part of https://github.com/apple/swift/pull/41571 as well, until it can be reimplemented in light of issues where a hop can appear between a get_continuation and an await_continuation resolves rdar://91502776 --- lib/SILGen/ManagedValue.cpp | 8 ++++---- lib/SILGen/ManagedValue.h | 2 +- lib/SILGen/SILGenApply.cpp | 25 ++++++++++++++++--------- test/SILGen/objc_async.swift | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/SILGen/ManagedValue.cpp b/lib/SILGen/ManagedValue.cpp index 98ab68d0e09b9..cad4fab9efeb4 100644 --- a/lib/SILGen/ManagedValue.cpp +++ b/lib/SILGen/ManagedValue.cpp @@ -58,20 +58,20 @@ ManagedValue ManagedValue::copy(SILGenFunction &SGF, SILLocation loc) const { // Emit an unmanaged copy of this value // WARNING: Callers of this API should manage the cleanup of this value! -SILValue ManagedValue::unmanagedCopy(SILGenFunction &SGF, +ManagedValue ManagedValue::unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const { auto &lowering = SGF.getTypeLowering(getType()); if (lowering.isTrivial()) - return getValue(); + return *this; if (getType().isObject()) { auto copy = SGF.B.emitCopyValueOperation(loc, getValue()); - return copy; + return ManagedValue::forUnmanaged(copy); } SILValue buf = SGF.emitTemporaryAllocation(loc, getType()); SGF.B.createCopyAddr(loc, getValue(), buf, IsNotTake, IsInitialization); - return buf; + return ManagedValue::forUnmanaged(buf); } /// Emit a copy of this value with independent ownership. diff --git a/lib/SILGen/ManagedValue.h b/lib/SILGen/ManagedValue.h index 4294a71ac450c..a7702f226bb6e 100644 --- a/lib/SILGen/ManagedValue.h +++ b/lib/SILGen/ManagedValue.h @@ -280,7 +280,7 @@ class ManagedValue { /// Returns an unmanaged copy of this value. /// WARNING: Callers of this API should manage the cleanup of this value! - SILValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const; + ManagedValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const; /// Emit a copy of this value with independent ownership into the current /// formal evaluation scope. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 406da583a5a51..41b89575b4519 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4586,7 +4586,7 @@ RValue SILGenFunction::emitApply( // generates `await_async_continuation`. // Lifetime is extended by creating unmanaged copies here and by pushing the // cleanups required just before the result plan is generated. - SmallVector unmanagedCopies; + SmallVector unmanagedCopies; if (calleeTypeInfo.foreign.async) { for (auto arg : args) { if (arg.hasCleanup()) { @@ -4673,14 +4673,12 @@ RValue SILGenFunction::emitApply( *foreignError, calleeTypeInfo.foreign.async); } - // For objc async calls, push cleanup to be used on - // both result and throw paths prior to finishing the result plan. - if (calleeTypeInfo.foreign.async) { - for (auto unmanagedCopy : unmanagedCopies) { - Cleanups.pushCleanup(unmanagedCopy); - } - } else { - assert(unmanagedCopies.empty()); + // For objc async calls, push cleanup to be used on throw paths in the result + // planner. + for (unsigned i : indices(unmanagedCopies)) { + SILValue value = unmanagedCopies[i].getValue(); + Cleanups.pushCleanup(value); + unmanagedCopies[i] = ManagedValue(value, Cleanups.getTopCleanup()); } auto directResultsArray = makeArrayRef(directResults); @@ -4688,7 +4686,16 @@ RValue SILGenFunction::emitApply( directResultsArray, bridgedForeignError); assert(directResultsArray.empty() && "didn't claim all direct results"); + // For objc async calls, generate cleanup on the resume path here and forward + // the previously pushed cleanups. if (calleeTypeInfo.foreign.async) { + for (auto unmanagedCopy : unmanagedCopies) { + auto value = unmanagedCopy.forward(*this); + B.emitFixLifetime(loc, value); + B.emitDestroyOperation(loc, value); + } + + // hop back to the current executor breadcrumb.emit(*this, loc); } diff --git a/test/SILGen/objc_async.swift b/test/SILGen/objc_async.swift index a5e9174a11d99..3fad6864861f8 100644 --- a/test/SILGen/objc_async.swift +++ b/test/SILGen/objc_async.swift @@ -195,9 +195,9 @@ func testSlowServerFromMain(slowServer: SlowServer) async throws { // CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]] // CHECK: [[RESUME]]: // CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]] - // CHECK: hop_to_executor %6 : $MainActor // CHECK: fix_lifetime [[COPY]] // CHECK: destroy_value [[COPY]] + // CHECK: hop_to_executor %6 : $MainActor // CHECK: dealloc_stack [[RESUME_BUF]] let _: Int = await slowServer.doSomethingSlow("mail") } From 6d8c2749205c65f6ca3f6a044c1ce8d9179d9a81 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 26 Apr 2022 15:15:13 -0700 Subject: [PATCH 40/80] Disable failing distributed actor tests. Example failure: https://ci.swift.org/job/oss-swift-incremental-ASAN-RA-macos//408/console. Resolves rdar://92277271 --- .../Distributed/Runtime/distributed_actor_in_other_module.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Distributed/Runtime/distributed_actor_in_other_module.swift b/test/Distributed/Runtime/distributed_actor_in_other_module.swift index 0d4889fb320b6..ebae97b8589b9 100644 --- a/test/Distributed/Runtime/distributed_actor_in_other_module.swift +++ b/test/Distributed/Runtime/distributed_actor_in_other_module.swift @@ -15,6 +15,8 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows +// REQUIRES: rdar92277324 + import Distributed import EchoActorModule import FakeDistributedActorSystems From ecf2db3cb3adcc82280f97413e15a1ea403cbf37 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 26 Apr 2022 15:42:09 -0700 Subject: [PATCH 41/80] Downgrade missing @Sendable to a warning in Swift 5.x mode. We're treating Sendable issues as warnings, not errors, so downgrade function conversion errors to warnings as well. Fixes rdar://92291276. --- lib/Sema/CSSimplify.cpp | 14 +++++++++----- test/Concurrency/sendable_checking.swift | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 097d88520a14e..8d5dc905c51d4 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2672,11 +2672,15 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, } /// Whether to downgrade to a concurrency warning. - auto isConcurrencyWarning = [&] { - if (contextRequiresStrictConcurrencyChecking(DC, GetClosureType{*this}) - && !hasPreconcurrencyCallee(this, locator)) + auto isConcurrencyWarning = [&](bool forSendable) { + // Except for Sendable warnings, don't downgrade to an error in strict + // contexts without a preconcurrency callee. + if (!forSendable && + contextRequiresStrictConcurrencyChecking(DC, GetClosureType{*this}) && + !hasPreconcurrencyCallee(this, locator)) return false; + // We can only handle the downgrade for conversions. switch (kind) { case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: @@ -2696,7 +2700,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, auto *fix = AddSendableAttribute::create( *this, func1, func2, getConstraintLocator(locator), - isConcurrencyWarning()); + isConcurrencyWarning(true)); if (recordFix(fix)) return getTypeMatchFailure(locator); } @@ -2731,7 +2735,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, auto *fix = MarkGlobalActorFunction::create( *this, func1, func2, getConstraintLocator(locator), - isConcurrencyWarning()); + isConcurrencyWarning(false)); if (recordFix(fix)) return getTypeMatchFailure(locator); diff --git a/test/Concurrency/sendable_checking.swift b/test/Concurrency/sendable_checking.swift index 9531e579d0335..269ecd597a212 100644 --- a/test/Concurrency/sendable_checking.swift +++ b/test/Concurrency/sendable_checking.swift @@ -59,7 +59,7 @@ func testCV( acceptCV(ns4) // expected-warning{{type 'NS4' does not conform to the 'Sendable' protocol}} acceptCV(fn) // expected-warning{{type '() -> Void' does not conform to the 'Sendable' protocol}} // expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}} - acceptSendableFn(fn) // expected-error{{passing non-sendable parameter 'fn' to function expecting a @Sendable closure}} + acceptSendableFn(fn) // expected-warning{{passing non-sendable parameter 'fn' to function expecting a @Sendable closure}} } // rdar://83942484 - spurious Sendable diagnostics From 5b793f3ab8bbfafbcfe9fcf22cbb52a1c1c6562a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 27 Apr 2022 10:32:11 +0100 Subject: [PATCH 42/80] Merge pull request #58388 from al45tair/eng/PR-91831405 [Demangling] Fix demangling of built generics to be more robust. --- lib/Demangling/OldDemangler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Demangling/OldDemangler.cpp b/lib/Demangling/OldDemangler.cpp index 671265c36ee1b..f8ff94b3294ed 100644 --- a/lib/Demangling/OldDemangler.cpp +++ b/lib/Demangling/OldDemangler.cpp @@ -1044,7 +1044,8 @@ class OldDemangler { // had its generic arguments applied. NodePointer result = Factory.createNode(nominalType->getKind()); result->addChild(parentOrModule, Factory); - result->addChild(nominalType->getChild(1), Factory); + for (unsigned ndx = 1; ndx < nominalType->getNumChildren(); ++ndx) + result->addChild(nominalType->getChild(ndx), Factory); nominalType = result; } From f04ed6039c0971f8e89e92e2725f63e751a908a4 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 6 Apr 2022 15:21:00 -0600 Subject: [PATCH 43/80] [stdlib] remove most uses of _asCChar and _asUInt8 --- stdlib/public/core/CString.swift | 17 ++++++++++------- stdlib/public/core/StringGuts.swift | 3 +-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index c517e599d4568..d398888eadc45 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -45,8 +45,9 @@ extension String { /// - Parameter nullTerminatedUTF8: A pointer to a null-terminated UTF-8 code sequence. public init(cString nullTerminatedUTF8: UnsafePointer) { let len = UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) - self = String._fromUTF8Repairing( - UnsafeBufferPointer(start: nullTerminatedUTF8._asUInt8, count: len)).0 + self = nullTerminatedUTF8.withMemoryRebound(to: UInt8.self, capacity: len) { + String._fromUTF8Repairing(UnsafeBufferPointer(start: $0, count: len)).0 + } } @inlinable @@ -150,8 +151,9 @@ extension String { /// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence. public init?(validatingUTF8 cString: UnsafePointer) { let len = UTF8._nullCodeUnitOffset(in: cString) - guard let str = String._tryFromUTF8( - UnsafeBufferPointer(start: cString._asUInt8, count: len)) + guard let str = cString.withMemoryRebound(to: UInt8.self, capacity: len, { + String._tryFromUTF8(UnsafeBufferPointer(start: $0, count: len)) + }) else { return nil } self = str @@ -165,9 +167,10 @@ extension String { "input of String.init(validatingUTF8:) must be null-terminated" ) } - guard let string = cString.prefix(length).withUnsafeBytes({ - String._tryFromUTF8($0.assumingMemoryBound(to: UInt8.self)) - }) else { return nil } + guard let string = cString.prefix(length).withUnsafeBufferPointer({ + $0.withMemoryRebound(to: UInt8.self, String._tryFromUTF8(_:)) + }) + else { return nil } self = string } diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 5f16b1cec3365..ead304b360505 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -167,8 +167,7 @@ extension _StringGuts { _ f: (UnsafeBufferPointer) throws -> R ) rethrows -> R { return try self.withFastUTF8 { utf8 in - let ptr = utf8.baseAddress._unsafelyUnwrappedUnchecked._asCChar - return try f(UnsafeBufferPointer(start: ptr, count: utf8.count)) + return try utf8.withMemoryRebound(to: CChar.self, f) } } } From ff6dbdbf980824207f8a54c04d972a68713ce131 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 6 Apr 2022 16:18:02 -0600 Subject: [PATCH 44/80] [stdlib] less `assumingMemoryBound`, rebind instead --- stdlib/public/core/CString.swift | 48 +++++++++++++++++------------- test/stdlib/StringAPICString.swift | 28 +++++++++-------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/stdlib/public/core/CString.swift b/stdlib/public/core/CString.swift index d398888eadc45..3a430eb287e36 100644 --- a/stdlib/public/core/CString.swift +++ b/stdlib/public/core/CString.swift @@ -45,16 +45,17 @@ extension String { /// - Parameter nullTerminatedUTF8: A pointer to a null-terminated UTF-8 code sequence. public init(cString nullTerminatedUTF8: UnsafePointer) { let len = UTF8._nullCodeUnitOffset(in: nullTerminatedUTF8) - self = nullTerminatedUTF8.withMemoryRebound(to: UInt8.self, capacity: len) { - String._fromUTF8Repairing(UnsafeBufferPointer(start: $0, count: len)).0 + let buffer = UnsafeBufferPointer(start: nullTerminatedUTF8, count: len) + self = buffer.withMemoryRebound(to: UInt8.self) { + String._fromUTF8Repairing($0).0 } } @inlinable @_alwaysEmitIntoClient public init(cString nullTerminatedUTF8: [CChar]) { - self = nullTerminatedUTF8.withUnsafeBytes { - String(_checkingCString: $0.assumingMemoryBound(to: UInt8.self)) + self = nullTerminatedUTF8.withUnsafeBufferPointer { + $0.withMemoryRebound(to: UInt8.self, String.init(_checkingCString:)) } } @@ -247,14 +248,18 @@ extension String { guard let cPtr = cString else { return nil } if _fastPath(encoding == Unicode.UTF8.self) { - let ptr = UnsafeRawPointer(cPtr).assumingMemoryBound(to: UInt8.self) - let len = UTF8._nullCodeUnitOffset(in: ptr) - let codeUnits = UnsafeBufferPointer(start: ptr, count: len) - if isRepairing { - return String._fromUTF8Repairing(codeUnits) - } else { - guard let str = String._tryFromUTF8(codeUnits) else { return nil } - return (str, false) + let len = UTF8._nullCodeUnitOffset( + in: UnsafeRawPointer(cPtr).assumingMemoryBound(to: UInt8.self) + ) + let bytes = UnsafeBufferPointer(start: cPtr, count: len) + return bytes.withMemoryRebound(to: UInt8.self) { codeUnits in + if isRepairing { + return String._fromUTF8Repairing(codeUnits) + } + else if let str = String._tryFromUTF8(codeUnits) { + return (str, false) + } + return nil } } @@ -282,16 +287,17 @@ extension String { } if _fastPath(encoding == Unicode.UTF8.self) { - return cString.prefix(length).withUnsafeBytes { - buf -> (result: String, repairsMade: Bool)? in - let codeUnits = buf.assumingMemoryBound(to: UInt8.self) - if isRepairing { - return String._fromUTF8Repairing(codeUnits) + return cString.prefix(length).withUnsafeBufferPointer { + buffer -> (result: String, repairsMade: Bool)? in + return buffer.withMemoryRebound(to: UInt8.self) { codeUnits in + if isRepairing { + return String._fromUTF8Repairing(codeUnits) + } + else if let str = String._tryFromUTF8(codeUnits) { + return (str, false) + } + return nil } - else if let str = String._tryFromUTF8(codeUnits) { - return (str, false) - } - return nil } } diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index 0cbbf48e5db9d..1563289cb8dfe 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -253,9 +253,10 @@ CStringTests.test("String.cString.with.Array.CChar.input") { do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } - let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: CChar.self) - let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) - let str = String(cString: Array(buffer)) + let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) + let str = buffer.withMemoryRebound(to: CChar.self) { + String(cString: Array($0)) + } str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) @@ -318,9 +319,10 @@ CStringTests.test("String.validatingUTF8.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } - let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: CChar.self) - let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) - let str = String(validatingUTF8: Array(buffer)) + let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) + let str = buffer.withMemoryRebound(to: CChar.self) { + String(validatingUTF8: Array($0)) + } expectNotNil(str) str?.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { @@ -373,9 +375,10 @@ CStringTests.test("String.decodeCString.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } - let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: Unicode.UTF8.CodeUnit.self) - let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) - let result = String.decodeCString(Array(buffer), as: Unicode.UTF8.self) + let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) + let result = buffer.withMemoryRebound(to: Unicode.UTF8.CodeUnit.self) { + String.decodeCString(Array($0), as: Unicode.UTF8.self) + } expectNotNil(result) expectEqual(result?.repairsMade, false) result?.result.withCString { @@ -436,9 +439,10 @@ CStringTests.test("String.init.decodingCString.with.Array.input") { do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } - let cstr = UnsafeRawPointer(u8p).assumingMemoryBound(to: Unicode.UTF8.CodeUnit.self) - let buffer = UnsafeBufferPointer(start: cstr, count: getUTF8Length(u8p)+1) - let str = String(decodingCString: Array(buffer), as: Unicode.UTF8.self) + let buffer = UnsafeBufferPointer(start: u8p, count: getUTF8Length(u8p)+1) + let str = buffer.withMemoryRebound(to: Unicode.UTF8.CodeUnit.self) { + String(decodingCString: Array($0), as: Unicode.UTF8.self) + } str.withCString { $0.withMemoryRebound(to: UInt8.self, capacity: buffer.count) { expectEqualCString(u8p, $0) From dba5320a15840f206af19f7b048a8ba150c91122 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 15 Feb 2022 16:57:00 -0800 Subject: [PATCH 45/80] Add missing parameter. Here, as in UnsafeMutableRawPointer.storeBytes(of:toByteOffset:as:) "as" is an argument label and "type" is the parameter. Because the function body doesn't use this -- it's just for type information -- changing its name from "as" to "type" doesn't have any impact there. --- stdlib/public/core/UnsafeRawBufferPointer.swift.gyb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index d51fe39bc1a95..90e48c6384467 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -410,7 +410,7 @@ extension Unsafe${Mutable}RawBufferPointer { /// with `type`. @inlinable public func storeBytes( - of value: T, toByteOffset offset: Int = 0, as: T.Type + of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { _debugPrecondition(offset >= 0, "${Self}.storeBytes with negative offset") _debugPrecondition(offset + MemoryLayout.size <= self.count, From d93573cdb8448aff480f0d8649db528460242f2d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 10 Feb 2022 12:20:03 -0700 Subject: [PATCH 46/80] [stdlib] add unaligned loads to UnsafeRawPointer --- stdlib/public/core/UnsafeRawPointer.swift | 61 ++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index 7c1d424982a68..b2ff871d308ab 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -424,7 +424,6 @@ public struct UnsafeRawPointer: _Pointer { let rawPointer = (self + offset)._rawValue #if compiler(>=5.5) && $BuiltinAssumeAlignment - // TODO: to support misaligned raw loads, simply remove this assumption. let alignedPointer = Builtin.assumeAlignment(rawPointer, MemoryLayout.alignment._builtinWordValue) @@ -434,6 +433,35 @@ public struct UnsafeRawPointer: _Pointer { #endif } + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// This function only supports loading trivial types. + /// A trivial type does not contain any reference-counted property + /// within its in-memory representation. + /// The memory at this pointer plus `offset` must be laid out + /// identically to the in-memory representation of `T`. + /// + /// - Note: A trivial type can be copied with just a bit-for-bit copy without + /// any indirection or reference-counting operations. Generally, native + /// Swift types that do not contain strong or weak references or other + /// forms of indirection are trivial, as are imported C structs and enums. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + @_alwaysEmitIntoClient + public func loadUnaligned( + fromByteOffset offset: Int = 0, + as type: T.Type + ) -> T { + _debugPrecondition(_isPOD(T.self)) + return Builtin.loadRaw((self + offset)._rawValue) + } } extension UnsafeRawPointer: Strideable { @@ -1120,7 +1148,6 @@ public struct UnsafeMutableRawPointer: _Pointer { let rawPointer = (self + offset)._rawValue #if compiler(>=5.5) && $BuiltinAssumeAlignment - // TODO: to support misaligned raw loads, simply remove this assumption. let alignedPointer = Builtin.assumeAlignment(rawPointer, MemoryLayout.alignment._builtinWordValue) @@ -1130,6 +1157,36 @@ public struct UnsafeMutableRawPointer: _Pointer { #endif } + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// This function only supports loading trivial types. + /// A trivial type does not contain any reference-counted property + /// within its in-memory representation. + /// The memory at this pointer plus `offset` must be laid out + /// identically to the in-memory representation of `T`. + /// + /// - Note: A trivial type can be copied with just a bit-for-bit copy without + /// any indirection or reference-counting operations. Generally, native + /// Swift types that do not contain strong or weak references or other + /// forms of indirection are trivial, as are imported C structs and enums. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + @_alwaysEmitIntoClient + public func loadUnaligned( + fromByteOffset offset: Int = 0, + as type: T.Type + ) -> T { + _debugPrecondition(_isPOD(T.self)) + return Builtin.loadRaw((self + offset)._rawValue) + } + /// Stores the given value's bytes into raw memory at the specified offset. /// /// The type `T` to be stored must be a trivial type. The memory at this From 5f65198815241e0e8976a97c8bdb18b7879eac85 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 10 Feb 2022 12:18:35 -0700 Subject: [PATCH 47/80] [stdlib] update UnsafeMutableRawPointer.storeBytes - preserve previous version for ABI and source stability. - add new version without alignment restriction. - add explicit POD type enforcement in new version. --- stdlib/public/core/UnsafeRawPointer.swift | 52 ++++++++++++++++++----- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index b2ff871d308ab..34f43b6b883f7 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -1189,9 +1189,8 @@ public struct UnsafeMutableRawPointer: _Pointer { /// Stores the given value's bytes into raw memory at the specified offset. /// - /// The type `T` to be stored must be a trivial type. The memory at this - /// pointer plus `offset` must be properly aligned for accessing `T`. The - /// memory must also be uninitialized, initialized to `T`, or initialized to + /// The type `T` to be stored must be a trivial type. The memory + /// must also be uninitialized, initialized to `T`, or initialized to /// another trivial type that is layout compatible with `T`. /// /// After calling `storeBytes(of:toByteOffset:as:)`, the memory is @@ -1205,14 +1204,14 @@ public struct UnsafeMutableRawPointer: _Pointer { /// Swift types that do not contain strong or weak references or other /// forms of indirection are trivial, as are imported C structs and enums. /// - /// If you need to store a copy of a nontrivial value into memory, or to - /// store a value into memory that contains a nontrivial value, you cannot - /// use the `storeBytes(of:toByteOffset:as:)` method. Instead, you must know - /// the type of value previously in memory and initialize or assign the - /// memory. For example, to replace a value stored in a raw pointer `p`, + /// If you need to store into memory a copy of a value of a type that isn't + /// trivial, you cannot use the `storeBytes(of:toByteOffset:as:)` method. + /// Instead, you must know either initialize the memory or, + /// if you know the memory was already bound to `type`, assign to the memory. + /// For example, to replace a value stored in a raw pointer `p`, /// where `U` is the current type and `T` is the new type, use a typed /// pointer to access and deinitialize the current value before initializing - /// the memory with a new value. + /// the memory with a new value: /// /// let typedPointer = p.bindMemory(to: U.self, capacity: 1) /// typedPointer.deinitialize(count: 1) @@ -1224,8 +1223,41 @@ public struct UnsafeMutableRawPointer: _Pointer { /// nonnegative. The default is zero. /// - type: The type of `value`. @inlinable + @_alwaysEmitIntoClient + @_silgen_name("_swift_se0349_UnsafeMutableRawPointer_storeBytes") public func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) { + _debugPrecondition(_isPOD(T.self)) + + withUnsafePointer(to: value) { source in + // FIXME: to be replaced by _memcpy when conversions are implemented. + Builtin.int_memcpy_RawPointer_RawPointer_Int64( + (self + offset)._rawValue, + source._rawValue, + UInt64(MemoryLayout.size)._value, + /*volatile:*/ false._value + ) + } + } + + // This unavailable implementation uses the expected mangled name + // of `storeBytes(of:toByteOffset:as:)`, and provides an entry point for + // any binary compiled against the stlib binary for Swift 5.6 and older. + @available(*, unavailable) + @_silgen_name("sSv10storeBytes2of12toByteOffset2asyx_SixmtlF") + @usableFromInline func _legacy_se0349_storeBytes( + of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) { + _legacy_se0349_storeBytes_internal( + of: value, toByteOffset: offset, as: T.self + ) + } + + // This is the implementation of `storeBytes` from SwiftStdlib 5.6 + @_alwaysEmitIntoClient + internal func _legacy_se0349_storeBytes_internal( + of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { _debugPrecondition(0 == (UInt(bitPattern: self + offset) & (UInt(MemoryLayout.alignment) - 1)), @@ -1240,7 +1272,7 @@ public struct UnsafeMutableRawPointer: _Pointer { /*volatile:*/ false._value) } } - + /// Copies the specified number of bytes from the given raw pointer's memory /// into this pointer's memory. /// From 04cd339cd0b600eaa83a080355830da54e6eca62 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 10 Feb 2022 14:44:04 -0700 Subject: [PATCH 48/80] [stdlib] add unaligned loads to Unsafe{Mutable}RawBufferPointer --- .../core/UnsafeRawBufferPointer.swift.gyb | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index 90e48c6384467..a5af5ab637e87 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -381,6 +381,49 @@ extension Unsafe${Mutable}RawBufferPointer { return baseAddress!.load(fromByteOffset: offset, as: T.self) } + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// This function only supports loading trivial types. + /// A trivial type does not contain any reference-counted property + /// within its in-memory stored representation. + /// The memory at `offset` bytes into the buffer must be laid out + /// identically to the in-memory representation of `T`. + /// + /// You can use this method to create new values from the buffer pointer's + /// underlying bytes. The following example creates two new `Int32` + /// instances from the memory referenced by the buffer pointer `someBytes`. + /// The bytes for `a` are copied from the first four bytes of `someBytes`, + /// and the bytes for `b` are copied from the next four bytes. + /// + /// let a = someBytes.load(as: Int32.self) + /// let b = someBytes.load(fromByteOffset: 4, as: Int32.self) + /// + /// The memory to read for the new instance must not extend beyond the buffer + /// pointer's memory region---that is, `offset + MemoryLayout.size` must + /// be less than or equal to the buffer pointer's `count`. + /// + /// - Parameters: + /// - offset: The offset, in bytes, into the buffer pointer's memory at + /// which to begin reading data for the new instance. The buffer pointer + /// plus `offset` must be properly aligned for accessing an instance of + /// type `T`. The default is zero. + /// - type: The type to use for the newly constructed instance. The memory + /// must be initialized to a value of a type that is layout compatible + /// with `type`. + /// - Returns: A new instance of type `T`, copied from the buffer pointer's + /// memory. + @_alwaysEmitIntoClient + public func loadUnaligned( + fromByteOffset offset: Int = 0, + as type: T.Type + ) -> T { + _debugPrecondition(offset >= 0, "${Self}.load with negative offset") + _debugPrecondition(offset + MemoryLayout.size <= self.count, + "${Self}.load out of bounds") + return baseAddress!.loadUnaligned(fromByteOffset: offset, as: T.self) + } + % if mutable: /// Stores a value's bytes into the buffer pointer's raw memory at the /// specified byte offset. From 86089dcf2a6676e37e35d33525cd37c782c25515 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 10 Feb 2022 14:51:34 -0700 Subject: [PATCH 49/80] [stdlib] update UnsafeMutableRawBufferPointer.storeBytes - preserve previous version for ABI and source stability. - add new version without alignment restriction. - add explicit POD type enforcement in new version. --- .../core/UnsafeRawBufferPointer.swift.gyb | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index a5af5ab637e87..b651d192e74d2 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -442,6 +442,16 @@ extension Unsafe${Mutable}RawBufferPointer { /// type `U`. Calling `storeBytes(of:toByteOffset:as:)` does not change the /// bound type of the memory. /// + /// - Note: A trivial type can be copied with just a bit-for-bit copy without + /// any indirection or reference-counting operations. Generally, native + /// Swift types that do not contain strong or weak references or other + /// forms of indirection are trivial, as are imported C structs and enums. + /// + /// If you need to store into memory a copy of a value of a type that isn't + /// trivial, you cannot use the `storeBytes(of:toByteOffset:as:)` method. + /// Instead, you must know either initialize the memory or, + /// if you know the memory was already bound to `type`, assign to the memory. + /// /// - Parameters: /// - value: The value to store as raw bytes. /// - offset: The offset in bytes into the buffer pointer's memory to begin @@ -452,6 +462,8 @@ extension Unsafe${Mutable}RawBufferPointer { /// must be initialized to a value of a type that is layout compatible /// with `type`. @inlinable + @_alwaysEmitIntoClient + @_silgen_name("_swift_seNNNN_UnsafeMutableRawBufferPointer_storeBytes") public func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { @@ -459,7 +471,25 @@ extension Unsafe${Mutable}RawBufferPointer { _debugPrecondition(offset + MemoryLayout.size <= self.count, "${Self}.storeBytes out of bounds") - baseAddress!.storeBytes(of: value, toByteOffset: offset, as: T.self) + let pointer = baseAddress._unsafelyUnwrappedUnchecked + pointer.storeBytes(of: value, toByteOffset: offset, as: T.self) + } + + // This unavailable implementation uses the expected mangled name + // of `storeBytes(of:toByteOffset:as:)`, and provides an entry point for + // any binary compiled against the stlib binary for Swift 5.6 and older. + @available(*, unavailable) + @_silgen_name("$sSw10storeBytes2of12toByteOffset2asyx_SixmtlF") + @usableFromInline func _legacy_se0349_storeBytes( + of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) { + _debugPrecondition(offset >= 0, "${Self}.storeBytes with negative offset") + _debugPrecondition(offset + MemoryLayout.size <= self.count, + "${Self}.storeBytes out of bounds") + + baseAddress!._legacy_se0349_storeBytes_internal( + of: value, toByteOffset: offset, as: T.self + ) } /// Copies the bytes from the given buffer to this buffer's memory. From 819ef4fb983cd2ef7ccbe413810b7ef4eb323632 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 31 Mar 2022 10:22:06 -0600 Subject: [PATCH 50/80] [stdlib] state clearly the precondition of loadUnaligned --- stdlib/public/core/UnsafeRawPointer.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/UnsafeRawPointer.swift b/stdlib/public/core/UnsafeRawPointer.swift index 34f43b6b883f7..2ecacf05d1a74 100644 --- a/stdlib/public/core/UnsafeRawPointer.swift +++ b/stdlib/public/core/UnsafeRawPointer.swift @@ -436,7 +436,8 @@ public struct UnsafeRawPointer: _Pointer { /// Returns a new instance of the given type, constructed from the raw memory /// at the specified offset. /// - /// This function only supports loading trivial types. + /// This function only supports loading trivial types, + /// and will trap if this precondition is not met. /// A trivial type does not contain any reference-counted property /// within its in-memory representation. /// The memory at this pointer plus `offset` must be laid out @@ -1160,7 +1161,8 @@ public struct UnsafeMutableRawPointer: _Pointer { /// Returns a new instance of the given type, constructed from the raw memory /// at the specified offset. /// - /// This function only supports loading trivial types. + /// This function only supports loading trivial types, + /// and will trap if this precondition is not met. /// A trivial type does not contain any reference-counted property /// within its in-memory representation. /// The memory at this pointer plus `offset` must be laid out From 7019dcb53eacf36adb74deb1a60863c6988432a3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 10 Feb 2022 13:46:43 -0700 Subject: [PATCH 51/80] [test] fix false results from the ABI checker --- test/api-digester/stability-stdlib-abi-without-asserts.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/api-digester/stability-stdlib-abi-without-asserts.test b/test/api-digester/stability-stdlib-abi-without-asserts.test index 01bdc4fcd4b6f..1407fa3b6ba6b 100644 --- a/test/api-digester/stability-stdlib-abi-without-asserts.test +++ b/test/api-digester/stability-stdlib-abi-without-asserts.test @@ -89,5 +89,7 @@ Func UnsafeBufferPointer.withMemoryRebound(to:_:) has been removed Func UnsafeMutableBufferPointer.withMemoryRebound(to:_:) has been removed Func UnsafeMutablePointer.withMemoryRebound(to:capacity:_:) has been removed Func UnsafePointer.withMemoryRebound(to:capacity:_:) has been removed +Func UnsafeMutableRawBufferPointer.storeBytes(of:toByteOffset:as:) has been removed +Func UnsafeMutableRawPointer.storeBytes(of:toByteOffset:as:) has been removed // *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.) From e923ab5857579f1a7898c457716b90676343fda5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 15 Apr 2022 13:39:17 -0600 Subject: [PATCH 52/80] [gardening] remove unnecessary warnings from test --- test/stdlib/UnsafeRawBufferPointer.swift | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/stdlib/UnsafeRawBufferPointer.swift b/test/stdlib/UnsafeRawBufferPointer.swift index 47bb466341c61..c145e89905fcb 100644 --- a/test/stdlib/UnsafeRawBufferPointer.swift +++ b/test/stdlib/UnsafeRawBufferPointer.swift @@ -318,7 +318,7 @@ UnsafeRawBufferPointerTestSuite.test("subscript.set.underflow") { let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 2, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } - var bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[1..<2]) + let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[1..<2]) if _isDebugAssertConfiguration() { expectCrashLater() @@ -331,7 +331,7 @@ UnsafeRawBufferPointerTestSuite.test("subscript.set.overflow") { let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 2, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } - var bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[0..<1]) + let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[0..<1]) if _isDebugAssertConfiguration() { expectCrashLater() @@ -367,10 +367,10 @@ UnsafeRawBufferPointerTestSuite.test("subscript.range.get.overflow") { } UnsafeRawBufferPointerTestSuite.test("subscript.range.set.underflow") { - var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } - var bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[1..<3]) + let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[1..<3]) if _isDebugAssertConfiguration() { expectCrashLater() @@ -380,10 +380,10 @@ UnsafeRawBufferPointerTestSuite.test("subscript.range.set.underflow") { } UnsafeRawBufferPointerTestSuite.test("subscript.range.set.overflow") { - var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } - var bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[0..<2]) + let bytes = UnsafeMutableRawBufferPointer(rebasing: buffer[0..<2]) if _isDebugAssertConfiguration() { expectCrashLater() @@ -394,7 +394,7 @@ UnsafeRawBufferPointerTestSuite.test("subscript.range.set.overflow") { } UnsafeRawBufferPointerTestSuite.test("subscript.range.narrow") { - var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } if _isDebugAssertConfiguration() { @@ -405,7 +405,7 @@ UnsafeRawBufferPointerTestSuite.test("subscript.range.narrow") { } UnsafeRawBufferPointerTestSuite.test("subscript.range.wide") { - var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } if _isDebugAssertConfiguration() { @@ -431,7 +431,7 @@ UnsafeRawBufferPointerTestSuite.test("_copyContents") { #if !os(WASI) // Trap tests aren't available on WASI. UnsafeRawBufferPointerTestSuite.test("copyMemory.overflow") { - var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } let bytes = buffer[0..<2] @@ -461,7 +461,7 @@ UnsafeRawBufferPointerTestSuite.test("copyBytes.withoutContiguouseStorage") { #if !os(WASI) // Trap tests aren't available on WASI. UnsafeRawBufferPointerTestSuite.test("copyBytes.sequence.overflow") { - var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) + let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } let bytes = buffer[0..<2] From ea7f5a88e8b830eef8d0d5a89683277a1679755d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 14 Apr 2022 13:52:02 -0600 Subject: [PATCH 53/80] [test] new stdlib load/store behaviours --- test/stdlib/UnsafeRawBufferPointer.swift | 68 ++++++++++++++++++++++++ test/stdlib/UnsafeRawPointer.swift | 63 ++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/test/stdlib/UnsafeRawBufferPointer.swift b/test/stdlib/UnsafeRawBufferPointer.swift index c145e89905fcb..dbcfc533029b3 100644 --- a/test/stdlib/UnsafeRawBufferPointer.swift +++ b/test/stdlib/UnsafeRawBufferPointer.swift @@ -498,6 +498,47 @@ UnsafeRawBufferPointerTestSuite.test("load.after") } } +UnsafeRawBufferPointerTestSuite.test("load.aligned") { + var data: [UInt8] = [0, 0, 0, 0, .max, .max, .max, .max] + data.withUnsafeBytes { + let x = $0.load(fromByteOffset: 4, as: UInt32.self) + expectEqual(x, .max) + } + data.withUnsafeMutableBytes { + let x = $0.load(fromByteOffset: 0, as: UInt32.self) + expectEqual(x, 0) + } +} + +UnsafeRawBufferPointerTestSuite.test("load.invalid") +.skip(.custom({ !_isDebugAssertConfiguration() }, // require debugAssert + reason: "This tests a debug precondition..")) +.code { + let data: [UInt8] = [0, 0, 0, .max, .max, .max, .max, 0] + let i = data.firstIndex(of: .max)! + expectCrashLater() + _ = data.withUnsafeBytes { + $0.load(fromByteOffset: i, as: UInt32.self) + } +} + +UnsafeRawBufferPointerTestSuite.test("load.unaligned") +.skip(.custom({ // require SwiftStdlib 5.7 + if #available(SwiftStdlib 5.7, *) { return false } else { return true } +}, reason: "Requires stdlib from Swift 5.7")) +.code { + var data: [UInt8] = [0, 0, 0, .max, .max, .max, .max, 0] + let i = data.firstIndex(of: .max)! + data.withUnsafeBytes { + let x = $0.loadUnaligned(fromByteOffset: i, as: UInt32.self) + expectEqual(x, .max) + } + data.withUnsafeMutableBytes { + let x = $0.loadUnaligned(fromByteOffset: i-1, as: UInt32.self) + expectEqual(UInt32(littleEndian: x), 0xffffff00) + } +} + UnsafeRawBufferPointerTestSuite.test("store.before") .skip(.custom( { !_isDebugAssertConfiguration() }, @@ -521,6 +562,33 @@ UnsafeRawBufferPointerTestSuite.test("store.after") } } +UnsafeRawBufferPointerTestSuite.test("store.invalid") +.skip(.custom({ !_isDebugAssertConfiguration() }, // require debugAssert + reason: "This tests a debug precondition..")) +.skip(.custom({ // require SwiftStdlib 5.7 + if #available(SwiftStdlib 5.7, *) { return false } else { return true } +}, reason: "Requires stdlib from Swift 5.7")) +.code { + let t = "Text that is longer than fits in a small String." + let p1 = UnsafeMutableRawPointer.allocate( + byteCount: MemoryLayout.size, + alignment: MemoryLayout.alignment + ) + defer { p1.deallocate() } + expectCrashLater() + p1.storeBytes(of: t, as: String.self) + expectUnreachable() +} + +UnsafeRawBufferPointerTestSuite.test("store.valid") { + let value32 = UInt32.max + var value64 = Int64.zero + withUnsafeMutableBytes(of: &value64) { + $0.storeBytes(of: value32, toByteOffset: MemoryLayout.stride, as: UInt32.self) + } + expectEqual(value64, 0xffffffff << 32) +} + UnsafeRawBufferPointerTestSuite.test("copy.bytes.overflow") .skip(.custom( { !_isDebugAssertConfiguration() }, diff --git a/test/stdlib/UnsafeRawPointer.swift b/test/stdlib/UnsafeRawPointer.swift index c2df7bba40821..cd90bc7449930 100644 --- a/test/stdlib/UnsafeRawPointer.swift +++ b/test/stdlib/UnsafeRawPointer.swift @@ -94,6 +94,69 @@ UnsafeMutableRawPointerExtraTestSuite.test("load/store") { expectEqual(6, p1.load(fromByteOffset: 2 * MemoryLayout.stride, as: Int.self)) } +UnsafeMutableRawPointerExtraTestSuite.test("load.unaligned") +.skip(.custom({ + if #available(SwiftStdlib 5.7, *) { return false } + return true +}, reason: "Requires Swift 5.7's stdlib")) +.code { + var data: [UInt8] = [0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0] + var result: UInt32 + result = data.withUnsafeBytes { + $0.loadUnaligned(fromByteOffset: 3, as: UInt32.self) + } + expectEqual(result, 0xffff_ffff) + result = data.withUnsafeMutableBytes { + $0[0] = 0 + return $0.loadUnaligned(fromByteOffset: 1, as: UInt32.self) + } + expectEqual(result, 0xffff_0000) +} + +UnsafeMutableRawPointerExtraTestSuite.test("load.invalid") +.skip(.custom({ !_isDebugAssertConfiguration() }, + reason: "This tests a debug precondition..")) +.code { + let data: [UInt8] = [0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0] + expectCrashLater() + _ = data.withUnsafeBytes { + $0.load(fromByteOffset: 3, as: UInt32.self) + } + expectUnreachable() +} + +UnsafeMutableRawPointerExtraTestSuite.test("load.invalid.mutable") +.skip(.custom({ !_isDebugAssertConfiguration() }, + reason: "This tests a debug precondition..")) +.code { + var data: [UInt8] = [0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0] + expectCrashLater() + _ = data.withUnsafeMutableBytes { + $0.load(fromByteOffset: 1, as: UInt32.self) + } + expectUnreachable() +} + +UnsafeMutableRawPointerExtraTestSuite.test("store.invalid") +.skip(.custom({ !_isDebugAssertConfiguration() }, + reason: "This tests a debug precondition..")) +.skip(.custom({ + if #available(SwiftStdlib 5.7, *) { return false } + return true +}, reason: "Requires Swift 5.7's stdlib")) +.code { + Missile.missilesLaunched = 0 + let m = Missile(0) + let p1 = UnsafeMutableRawPointer.allocate( + byteCount: MemoryLayout.size, + alignment: MemoryLayout.alignment + ) + defer { p1.deallocate() } + expectCrashLater() + p1.storeBytes(of: m, as: Missile.self) + expectUnreachable() +} + UnsafeMutableRawPointerExtraTestSuite.test("copyMemory") { let sizeInBytes = 4 * MemoryLayout.stride let rawPtr = UnsafeMutableRawPointer.allocate( From 4058424befde3b0a2bd739f990800a28906f4ea8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 18 Apr 2022 10:44:06 -0600 Subject: [PATCH 54/80] [test] Test new unaligned store behaviour --- test/stdlib/UnsafeRawBufferPointer.swift | 27 ++++++++++++++++++++++ test/stdlib/UnsafeRawPointer.swift | 29 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/test/stdlib/UnsafeRawBufferPointer.swift b/test/stdlib/UnsafeRawBufferPointer.swift index dbcfc533029b3..3b05b3df507be 100644 --- a/test/stdlib/UnsafeRawBufferPointer.swift +++ b/test/stdlib/UnsafeRawBufferPointer.swift @@ -562,6 +562,33 @@ UnsafeRawBufferPointerTestSuite.test("store.after") } } +UnsafeRawBufferPointerTestSuite.test("store.unaligned") +.skip(.custom({ + if #available(SwiftStdlib 5.7, *) { return false } + return true +}, reason: "Requires Swift 5.7's stdlib")) +.code { + let count = MemoryLayout.stride * 2 + let p1 = UnsafeMutableRawBufferPointer.allocate( + byteCount: count, + alignment: MemoryLayout.alignment + ) + defer { p1.deallocate() } + p1.copyBytes(from: repeatElement(UInt8.zero, count: count)) + let value = UInt.max + let offset = 3 + p1.storeBytes(of: value, toByteOffset: offset, as: UInt.self) + expectEqual(p1.load(fromByteOffset: offset-1, as: UInt8.self), + 0) + expectEqual(p1.load(fromByteOffset: offset, as: UInt8.self), + .max) + let storedLength = MemoryLayout.size + expectEqual(p1.load(fromByteOffset: offset-1+storedLength, as: UInt8.self), + .max) + expectEqual(p1.load(fromByteOffset: offset+storedLength, as: UInt8.self), + 0) +} + UnsafeRawBufferPointerTestSuite.test("store.invalid") .skip(.custom({ !_isDebugAssertConfiguration() }, // require debugAssert reason: "This tests a debug precondition..")) diff --git a/test/stdlib/UnsafeRawPointer.swift b/test/stdlib/UnsafeRawPointer.swift index cd90bc7449930..ce2517cd0156c 100644 --- a/test/stdlib/UnsafeRawPointer.swift +++ b/test/stdlib/UnsafeRawPointer.swift @@ -137,6 +137,35 @@ UnsafeMutableRawPointerExtraTestSuite.test("load.invalid.mutable") expectUnreachable() } +UnsafeMutableRawPointerExtraTestSuite.test("store.unaligned") +.skip(.custom({ + if #available(SwiftStdlib 5.7, *) { return false } + return true +}, reason: "Requires Swift 5.7's stdlib")) +.code { + let count = MemoryLayout.stride * 2 + let p1 = UnsafeMutableRawPointer.allocate( + byteCount: count, + alignment: MemoryLayout.alignment + ) + defer { p1.deallocate() } + Array(repeating: UInt8.zero, count: count).withUnsafeBufferPointer { + p1.copyBytes(from: UnsafeRawPointer($0.baseAddress!), count: $0.count) + } + let value = UInt.max + let offset = 3 + p1.storeBytes(of: value, toByteOffset: offset, as: UInt.self) + expectEqual(p1.load(fromByteOffset: offset-1, as: UInt8.self), + 0) + expectEqual(p1.load(fromByteOffset: offset, as: UInt8.self), + .max) + let storedLength = MemoryLayout.size + expectEqual(p1.load(fromByteOffset: offset-1+storedLength, as: UInt8.self), + .max) + expectEqual(p1.load(fromByteOffset: offset+storedLength, as: UInt8.self), + 0) +} + UnsafeMutableRawPointerExtraTestSuite.test("store.invalid") .skip(.custom({ !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) From a37248ae26111a6aead72962262ca8d514c6270b Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 18 Apr 2022 12:50:19 -0600 Subject: [PATCH 55/80] [stdlib] incorporate proposal number in silgen name --- stdlib/public/core/UnsafeRawBufferPointer.swift.gyb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index b651d192e74d2..fce7d0fcd929d 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -463,7 +463,7 @@ extension Unsafe${Mutable}RawBufferPointer { /// with `type`. @inlinable @_alwaysEmitIntoClient - @_silgen_name("_swift_seNNNN_UnsafeMutableRawBufferPointer_storeBytes") + @_silgen_name("_swift_se0349_UnsafeMutableRawBufferPointer_storeBytes") public func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) { From f160e2bea5457724b6bdb26ef0b63c811fa51b6b Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 18 Apr 2022 16:27:08 -0600 Subject: [PATCH 56/80] [test] make back-deployment testing possible --- test/stdlib/UnsafeRawBufferPointer.swift | 1 + test/stdlib/UnsafeRawPointer.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/test/stdlib/UnsafeRawBufferPointer.swift b/test/stdlib/UnsafeRawBufferPointer.swift index 3b05b3df507be..736634e255222 100644 --- a/test/stdlib/UnsafeRawBufferPointer.swift +++ b/test/stdlib/UnsafeRawBufferPointer.swift @@ -527,6 +527,7 @@ UnsafeRawBufferPointerTestSuite.test("load.unaligned") if #available(SwiftStdlib 5.7, *) { return false } else { return true } }, reason: "Requires stdlib from Swift 5.7")) .code { + guard #available(SwiftStdlib 5.7, *) else { return } var data: [UInt8] = [0, 0, 0, .max, .max, .max, .max, 0] let i = data.firstIndex(of: .max)! data.withUnsafeBytes { diff --git a/test/stdlib/UnsafeRawPointer.swift b/test/stdlib/UnsafeRawPointer.swift index ce2517cd0156c..d450335fa4b6a 100644 --- a/test/stdlib/UnsafeRawPointer.swift +++ b/test/stdlib/UnsafeRawPointer.swift @@ -100,6 +100,7 @@ UnsafeMutableRawPointerExtraTestSuite.test("load.unaligned") return true }, reason: "Requires Swift 5.7's stdlib")) .code { + guard #available(SwiftStdlib 5.7, *) else { return } var data: [UInt8] = [0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0] var result: UInt32 result = data.withUnsafeBytes { From 7bd26e4be3c5768ab500586c3e2018f03d3d6a0d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 27 Apr 2022 14:07:39 -0600 Subject: [PATCH 57/80] [ChangeLog] add SE-0349 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c05181799af0c..f4c7e6c0c9904 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,30 @@ _**Note:** This is in reverse chronological order, so newer entries are added to ## Swift 5.7 +* [SE-0349][]: + + Loading data from raw memory represented by `UnsafeRawPointer`, + `UnsafeRawBufferPointer` and their mutable counterparts now supports unaligned + accesses. This previously required a workaround involving an intermediate + copy: + + ```swift + let result = unalignedData.withUnsafeBytes { buffer -> UInt32 in + var storage = UInt32.zero + withUnsafeMutableBytes(of: &storage) { + $0.copyBytes(from: buffer.prefix(MemoryLayout.size)) + } + return storage + } + ``` + Now: + ```swift + let result = unalignedData.withUnsafeBytes { $0.loadUnaligned(as: UInt32.self) } + ``` + Additionally, the counterpart `storeBytes(of:toByteOffset:as:)` had its + alignment restriction lifted, so that storing to arbitrary offsets of raw + memory can now succeed. + * References to `optional` methods on a protocol metatype, as well as references to dynamically looked up methods on the `AnyObject` metatype are now supported. These references always have the type of a function that accepts a single argument and returns an optional value of function type: ```swift @@ -9203,6 +9227,7 @@ Swift 1.0 [SE-0345]: [SE-0326]: [SE-0347]: +[SE-0349]: [SE-0352]: [SR-75]: From 77a9433be87535c0f3cbaa588d9b8d4bb68765fa Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Wed, 27 Apr 2022 14:11:36 -0700 Subject: [PATCH 58/80] Revert "Revert "Simplify lifetime extension for async ObjC calls."" This reverts commit 8441e07c0bba093a85d0417db6ba56151801ffd5. --- lib/SILGen/ManagedValue.cpp | 8 ++++---- lib/SILGen/ManagedValue.h | 2 +- lib/SILGen/SILGenApply.cpp | 25 +++++++++---------------- test/SILGen/objc_async.swift | 2 +- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/lib/SILGen/ManagedValue.cpp b/lib/SILGen/ManagedValue.cpp index cad4fab9efeb4..98ab68d0e09b9 100644 --- a/lib/SILGen/ManagedValue.cpp +++ b/lib/SILGen/ManagedValue.cpp @@ -58,20 +58,20 @@ ManagedValue ManagedValue::copy(SILGenFunction &SGF, SILLocation loc) const { // Emit an unmanaged copy of this value // WARNING: Callers of this API should manage the cleanup of this value! -ManagedValue ManagedValue::unmanagedCopy(SILGenFunction &SGF, +SILValue ManagedValue::unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const { auto &lowering = SGF.getTypeLowering(getType()); if (lowering.isTrivial()) - return *this; + return getValue(); if (getType().isObject()) { auto copy = SGF.B.emitCopyValueOperation(loc, getValue()); - return ManagedValue::forUnmanaged(copy); + return copy; } SILValue buf = SGF.emitTemporaryAllocation(loc, getType()); SGF.B.createCopyAddr(loc, getValue(), buf, IsNotTake, IsInitialization); - return ManagedValue::forUnmanaged(buf); + return buf; } /// Emit a copy of this value with independent ownership. diff --git a/lib/SILGen/ManagedValue.h b/lib/SILGen/ManagedValue.h index a7702f226bb6e..4294a71ac450c 100644 --- a/lib/SILGen/ManagedValue.h +++ b/lib/SILGen/ManagedValue.h @@ -280,7 +280,7 @@ class ManagedValue { /// Returns an unmanaged copy of this value. /// WARNING: Callers of this API should manage the cleanup of this value! - ManagedValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const; + SILValue unmanagedCopy(SILGenFunction &SGF, SILLocation loc) const; /// Emit a copy of this value with independent ownership into the current /// formal evaluation scope. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 41b89575b4519..406da583a5a51 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4586,7 +4586,7 @@ RValue SILGenFunction::emitApply( // generates `await_async_continuation`. // Lifetime is extended by creating unmanaged copies here and by pushing the // cleanups required just before the result plan is generated. - SmallVector unmanagedCopies; + SmallVector unmanagedCopies; if (calleeTypeInfo.foreign.async) { for (auto arg : args) { if (arg.hasCleanup()) { @@ -4673,12 +4673,14 @@ RValue SILGenFunction::emitApply( *foreignError, calleeTypeInfo.foreign.async); } - // For objc async calls, push cleanup to be used on throw paths in the result - // planner. - for (unsigned i : indices(unmanagedCopies)) { - SILValue value = unmanagedCopies[i].getValue(); - Cleanups.pushCleanup(value); - unmanagedCopies[i] = ManagedValue(value, Cleanups.getTopCleanup()); + // For objc async calls, push cleanup to be used on + // both result and throw paths prior to finishing the result plan. + if (calleeTypeInfo.foreign.async) { + for (auto unmanagedCopy : unmanagedCopies) { + Cleanups.pushCleanup(unmanagedCopy); + } + } else { + assert(unmanagedCopies.empty()); } auto directResultsArray = makeArrayRef(directResults); @@ -4686,16 +4688,7 @@ RValue SILGenFunction::emitApply( directResultsArray, bridgedForeignError); assert(directResultsArray.empty() && "didn't claim all direct results"); - // For objc async calls, generate cleanup on the resume path here and forward - // the previously pushed cleanups. if (calleeTypeInfo.foreign.async) { - for (auto unmanagedCopy : unmanagedCopies) { - auto value = unmanagedCopy.forward(*this); - B.emitFixLifetime(loc, value); - B.emitDestroyOperation(loc, value); - } - - // hop back to the current executor breadcrumb.emit(*this, loc); } diff --git a/test/SILGen/objc_async.swift b/test/SILGen/objc_async.swift index 3fad6864861f8..a5e9174a11d99 100644 --- a/test/SILGen/objc_async.swift +++ b/test/SILGen/objc_async.swift @@ -195,9 +195,9 @@ func testSlowServerFromMain(slowServer: SlowServer) async throws { // CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]] // CHECK: [[RESUME]]: // CHECK: [[RESULT:%.*]] = load [trivial] [[RESUME_BUF]] + // CHECK: hop_to_executor %6 : $MainActor // CHECK: fix_lifetime [[COPY]] // CHECK: destroy_value [[COPY]] - // CHECK: hop_to_executor %6 : $MainActor // CHECK: dealloc_stack [[RESUME_BUF]] let _: Int = await slowServer.doSomethingSlow("mail") } From 836e90c97edf84c48205b3eb46feeee3c614b4aa Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Wed, 27 Apr 2022 14:11:44 -0700 Subject: [PATCH 59/80] Revert "Revert "[SR-15703] Fix missing hop in error path of foreign async throws call"" This reverts commit 05214f0d00260566a5d4dc58793630688f4290aa. --- lib/SILGen/ExecutorBreadcrumb.h | 5 ++++ lib/SILGen/SILGenApply.cpp | 29 ++++++++++++++---- test/SILGen/objc_async.swift | 40 +++++++++++++++++++++++++ test/SILGen/objc_async_from_swift.swift | 17 ++++++++--- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/lib/SILGen/ExecutorBreadcrumb.h b/lib/SILGen/ExecutorBreadcrumb.h index 32c4edea1170e..e2734e488823d 100644 --- a/lib/SILGen/ExecutorBreadcrumb.h +++ b/lib/SILGen/ExecutorBreadcrumb.h @@ -37,6 +37,11 @@ class ExecutorBreadcrumb { // Emits the hop back sequence, if any, necessary to get back to // the executor represented by this breadcrumb. void emit(SILGenFunction &SGF, SILLocation loc); + +#ifndef NDEBUG + // FOR ASSERTS ONLY: returns true if calling `emit` will emit a hop-back. + bool needsEmit() const { return mustReturnToExecutor; } +#endif }; } // namespace Lowering diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 406da583a5a51..32afacc5e5bc9 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4427,6 +4427,27 @@ class FixLifetimeDestroyCleanup : public Cleanup { #endif } }; + +class EmitBreadcrumbCleanup : public Cleanup { + ExecutorBreadcrumb breadcrumb; + +public: + EmitBreadcrumbCleanup(ExecutorBreadcrumb &&breadcrumb) + : breadcrumb(std::move(breadcrumb)) {} + + void emit(SILGenFunction &SGF, CleanupLocation l, + ForUnwind_t forUnwind) override { + breadcrumb.emit(SGF, l); + } + + void dump(SILGenFunction &SGF) const override { +#ifndef NDEBUG + llvm::errs() << "EmitBreadcrumbCleanup " + << "State:" << getState() + << "NeedsEmit:" << breadcrumb.needsEmit(); +#endif + } +}; } // end anonymous namespace //===----------------------------------------------------------------------===// @@ -4673,12 +4694,14 @@ RValue SILGenFunction::emitApply( *foreignError, calleeTypeInfo.foreign.async); } - // For objc async calls, push cleanup to be used on + // For objc async calls, push cleanups to be used on // both result and throw paths prior to finishing the result plan. if (calleeTypeInfo.foreign.async) { for (auto unmanagedCopy : unmanagedCopies) { Cleanups.pushCleanup(unmanagedCopy); } + // save breadcrumb as a clean-up so it is emitted in result / throw cases. + Cleanups.pushCleanup(std::move(breadcrumb)); } else { assert(unmanagedCopies.empty()); } @@ -4688,10 +4711,6 @@ RValue SILGenFunction::emitApply( directResultsArray, bridgedForeignError); assert(directResultsArray.empty() && "didn't claim all direct results"); - if (calleeTypeInfo.foreign.async) { - breadcrumb.emit(*this, loc); - } - return result; } diff --git a/test/SILGen/objc_async.swift b/test/SILGen/objc_async.swift index a5e9174a11d99..bd2f04a309ebe 100644 --- a/test/SILGen/objc_async.swift +++ b/test/SILGen/objc_async.swift @@ -201,3 +201,43 @@ func testSlowServerFromMain(slowServer: SlowServer) async throws { // CHECK: dealloc_stack [[RESUME_BUF]] let _: Int = await slowServer.doSomethingSlow("mail") } + +// CHECK-LABEL: sil {{.*}}@${{.*}}26testThrowingMethodFromMain +@MainActor +func testThrowingMethodFromMain(slowServer: SlowServer) async -> String { +// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $String +// CHECK: [[STRING_ARG:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@guaranteed String) -> @owned NSString +// CHECK: [[METH:%.*]] = objc_method {{%.*}} : $SlowServer, #SlowServer.doSomethingDangerous!foreign +// CHECK: [[RAW_CONT:%.*]] = get_async_continuation_addr [throws] String, [[RESULT_BUF]] : $*String +// CHECK: [[CONT:%.*]] = struct $UnsafeContinuation ([[RAW_CONT]] : $Builtin.RawUnsafeContinuation) +// CHECK: [[STORE_ALLOC:%.*]] = alloc_stack $@block_storage UnsafeContinuation +// CHECK: [[PROJECTED:%.*]] = project_block_storage [[STORE_ALLOC]] : $*@block_storage +// CHECK: store [[CONT]] to [trivial] [[PROJECTED]] : $*UnsafeContinuation +// CHECK: [[INVOKER:%.*]] = function_ref @$sSo8NSStringCSgSo7NSErrorCSgIeyByy_SSTz_ +// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[STORE_ALLOC]] {{.*}}, invoke [[INVOKER]] +// CHECK: [[OPTIONAL_BLK:%.*]] = enum {{.*}}, #Optional.some!enumelt, [[BLOCK]] +// CHECK: %28 = apply [[METH]]([[STRING_ARG]], [[OPTIONAL_BLK]], {{%.*}}) : $@convention(objc_method) (NSString, Optional<@convention(block) (Optional, Optional) -> ()>, SlowServer) -> () +// CHECK: [[STRING_ARG_COPY:%.*]] = copy_value [[STRING_ARG]] : $NSString +// CHECK: dealloc_stack [[STORE_ALLOC]] : $*@block_storage UnsafeContinuation +// CHECK: destroy_value [[STRING_ARG]] : $NSString +// CHECK: await_async_continuation [[RAW_CONT]] : $Builtin.RawUnsafeContinuation, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + +// CHECK: [[RESUME]] +// CHECK: {{.*}} = load [take] [[RESULT_BUF]] : $*String +// CHECK: hop_to_executor {{%.*}} : $MainActor +// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString +// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString +// CHECK: dealloc_stack [[RESULT_BUF]] : $*String + +// CHECK: [[ERROR]] +// CHECK: hop_to_executor {{%.*}} : $MainActor +// CHECK: fix_lifetime [[STRING_ARG_COPY]] : $NSString +// CHECK: destroy_value [[STRING_ARG_COPY]] : $NSString +// CHECK: dealloc_stack [[RESULT_BUF]] : $*String + + do { + return try await slowServer.doSomethingDangerous("run-with-scissors") + } catch { + return "none" + } +} diff --git a/test/SILGen/objc_async_from_swift.swift b/test/SILGen/objc_async_from_swift.swift index 85dd87c9c83ed..40500c664eebf 100644 --- a/test/SILGen/objc_async_from_swift.swift +++ b/test/SILGen/objc_async_from_swift.swift @@ -25,19 +25,28 @@ func testSlowServing(p: SlowServing) async throws { // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : let _: String = await p.requestString() - // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional, Optional) -> (), τ_0_0) -> () - // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : - let _: String = try await p.tryRequestString() - // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, NSString) -> (), τ_0_0) -> () // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : let _: (Int, String) = await p.requestIntAndString() // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Int, Optional, Optional) -> (), τ_0_0) -> () // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : + // CHECK: builtin "willThrow" + // CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] : let _: (Int, String) = try await p.tryRequestIntAndString() } +// CHECK-LABEL: sil {{.*}}@{{.*}}20testSlowServingAgain +func testSlowServingAgain(p: SlowServing) async throws { + // CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional, #Optional.none + // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : + // CHECK: objc_method {{.*}} $@convention(objc_method) <τ_0_0 where τ_0_0 : SlowServing> (@convention(block) (Optional, Optional) -> (), τ_0_0) -> () + // CHECK: hop_to_executor [[GENERIC_EXECUTOR]] : + // CHECK: builtin "willThrow" + // CHECK-NEXT: hop_to_executor [[GENERIC_EXECUTOR]] : + let _: String = try await p.tryRequestString() +} + class SlowSwiftServer: NSObject, SlowServing { // CHECK-LABEL: sil {{.*}} @$s21objc_async_from_swift15SlowSwiftServerC10requestIntSiyYaF // CHECK: [[GENERIC_EXECUTOR:%.*]] = enum $Optional, #Optional.none From 812ef3449bbd83738d5f2ed00b4ada181f8086ac Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 25 Apr 2022 17:03:15 -0700 Subject: [PATCH 60/80] [SILVerifier] a hop_to_executor may suspend the task. With this change, the SILVerifier should now catch and reject the appearance of a hop_to_executor between the get_continuation and await_continuation instructions. --- lib/SIL/IR/SILInstruction.cpp | 4 ++++ test/SIL/verifier-no-nested-suspend.sil | 26 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 test/SIL/verifier-no-nested-suspend.sil diff --git a/lib/SIL/IR/SILInstruction.cpp b/lib/SIL/IR/SILInstruction.cpp index 2a8a4c504d1df..4907aae486930 100644 --- a/lib/SIL/IR/SILInstruction.cpp +++ b/lib/SIL/IR/SILInstruction.cpp @@ -1646,6 +1646,10 @@ bool SILInstruction::maySuspend() const { // await_async_continuation always suspends the current task. if (isa(this)) return true; + + // hop_to_executor also may cause a suspension + if (isa(this)) + return true; // Fully applying an async function may suspend the caller. if (auto applySite = FullApplySite::isa(const_cast(this))) { diff --git a/test/SIL/verifier-no-nested-suspend.sil b/test/SIL/verifier-no-nested-suspend.sil new file mode 100644 index 0000000000000..bb3af4c21c256 --- /dev/null +++ b/test/SIL/verifier-no-nested-suspend.sil @@ -0,0 +1,26 @@ +// RUN: not --crash %target-sil-opt %s 2>&1 | %FileCheck %s +// REQUIRES: asserts + +// CHECK: cannot suspend async task while unawaited continuation is active + +sil_stage raw + +import Builtin +import Swift +import SwiftShims +import _Concurrency + +// hello(_:) +sil hidden [ossa] @$s4main5helloyS2bYaF : $@convention(thin) @async (Bool) -> Bool { +bb0(%0 : $Bool): + debug_value %0 : $Bool, let, name "b", argno 1 + %2 = enum $Optional, #Optional.none!enumelt + %22 = alloc_stack $Bool + %27 = get_async_continuation_addr Bool, %22 : $*Bool + hop_to_executor %2 : $Optional // <- the bad nested suspension + await_async_continuation %27 : $Builtin.RawUnsafeContinuation, resume bb1 + +bb1: + dealloc_stack %22 : $*Bool + return %0 : $Bool +} From a87aad58181c681ba605039a7d7e46c10e025c5e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 27 Apr 2022 17:58:14 -0600 Subject: [PATCH 61/80] [ChangeLog] synchronize with main - adds omitted SE-0333 and SE-0334 changes - sorts the SE links at the bottom --- CHANGELOG.md | 87 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4c7e6c0c9904..ad496e0758840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ _**Note:** This is in reverse chronological order, so newer entries are added to ## Swift 5.7 +* References to `optional` methods on a protocol metatype, as well as references to dynamically looked up methods on the `AnyObject` metatype are now supported. These references always have the type of a function that accepts a single argument and returns an optional value of function type: + + ```swift + class Object { + @objc func getTag() -> Int + } + + @objc protocol P { + @objc optional func didUpdateObject(withTag tag: Int) + } + + let getTag: (AnyObject) -> (() -> Int)? = AnyObject.getTag + + let didUpdateObject: (any P) -> ((Int) -> Void)? = P.didUpdateObject + ``` + * [SE-0349][]: Loading data from raw memory represented by `UnsafeRawPointer`, @@ -29,21 +45,47 @@ _**Note:** This is in reverse chronological order, so newer entries are added to alignment restriction lifted, so that storing to arbitrary offsets of raw memory can now succeed. -* References to `optional` methods on a protocol metatype, as well as references to dynamically looked up methods on the `AnyObject` metatype are now supported. These references always have the type of a function that accepts a single argument and returns an optional value of function type: +* [SE-0334][]: - ```swift - class Object { - @objc func getTag() -> Int - } + - `UnsafeRawPointer` and `UnsafeMutableRawPointer` have new functionality for + pointer arithmetic, adding functions to obtain a pointer advanced to the next + or previous alignment boundary: - @objc protocol P { - @objc optional func didUpdateObject(withTag tag: Int) - } + ```swift + extension UnsafeRawPointer { + public func alignedUp(for: T.type) -> UnsafeRawPointer + public func alignedDown(for: T.type) -> UnsafeRawPointer + public func alignedUp(toMultipleOf alignment: Int) -> UnsafeRawPointer + public func alignedDown(toMultipleOf alignment: Int) -> UnsafeRawPointer + } + ``` + - It is now possible to use a pointer to `struct` to obtain a pointer to one + of its stored properties: - let getTag: (AnyObject) -> (() -> Int)? = AnyObject.getTag + ```swift + withUnsafeMutablePointer(to: &myStruct) { + let interiorPointer = $0.pointer(to: \.myProperty)! + return myCFunction(interiorPointer) + } + ``` + - Comparisons between pointers have been simplified by being more permissive. + Since pointers are representations of memory locations within a single pool of + underlying memory, Swift now allows comparing pointers without requiring type + conversions with the `==`, `!=`, `<`,`<=`,`>`, and `>=` operators. - let didUpdateObject: (any P) -> ((Int) -> Void)? = P.didUpdateObject - ``` +* [SE-0333][]: + + It is now possible to use the `withMemoryRebound()` method on raw memory, + that is `UnsafeRawPointer` , `UnsafeRawBufferPointer` and their mutable + counterparts. Additionally, we clarified the semantics of + `withMemoryRebound()` when used on typed memory (`UnsafePointer`, + `UnsafeBufferPointer` and their mutable counterparts). Whereas + `Pointee` and `T` were previously required to have the same stride, you can + now rebind in cases where `Pointee` is an aggregate of `T` or vice-versa. For + example, given an `UnsafeMutableBufferPointer`, you can now use + `withMemoryRebound` to operate temporarily on a + `UnsafeMutableBufferPointer`, because `CGPoint` is an aggregate of + `CGFloat`. * [SE-0352][]: @@ -168,7 +210,7 @@ _**Note:** This is in reverse chronological order, so newer entries are added to Distributed actors provide stronger isolation guarantees than "local" actors, and enable additional checks to be made on return types and parameters of distributed methods, e.g. checking if they conform to `Codable`. Distributed methods can be called on "remote" references of distributed actors, turning those invocations into remote procedure calls, by means of pluggable and user extensible distributed actor system implementations. - Swift does not provide any specific distributed actor system by itself, however, packages in the ecosystem fulfil the role of providing those implementations. + Swift does not provide any specific distributed actor system by itself, however, packages in the ecosystem fulfill the role of providing those implementations. ```swift distributed actor Greeter { @@ -297,9 +339,12 @@ _**Note:** This is in reverse chronological order, so newer entries are added to return [ 1: "One", 2: "Two" ] } ``` + Swift 5.6 --------- +### 2022-03-14 (Xcode 13.3) + * [SE-0327][]: In Swift 5 mode, a warning is now emitted if the default-value expression of an @@ -528,8 +573,6 @@ Swift 5.6 } ``` -**Add new entries to the top of this section, not here!** - Swift 5.5 --------- @@ -948,8 +991,6 @@ Swift 5.5 Asynchronous for loops use asynchronous sequences, defined by the protocol `AsyncSequence` and its corresponding `AsyncIterator`. -**Add new entries to the top of this section, not here!** - Swift 5.4 --------- @@ -1116,8 +1157,6 @@ Swift 5.4 let _: Foo? = .bar.anotherFoo.getFoo().optionalFoo?.optionalFoo![] ``` -**Add new entries to the top of this section, not here!** - Swift 5.3 --------- @@ -9213,19 +9252,21 @@ Swift 1.0 [SE-0316]: [SE-0320]: [SE-0322]: -[SE-0324]: [SE-0323]: +[SE-0324]: +[SE-0326]: [SE-0327]: [SE-0328]: [SE-0331]: -[SE-0337]: +[SE-0333]: +[SE-0334]: [SE-0335]: -[SE-0341]: [SE-0336]: -[SE-0343]: +[SE-0337]: [SE-0340]: +[SE-0341]: +[SE-0343]: [SE-0345]: -[SE-0326]: [SE-0347]: [SE-0349]: [SE-0352]: From 27ea039ca80b2e7109fb3e9805c0b05f11b9a91b Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 27 Apr 2022 20:38:25 -0700 Subject: [PATCH 62/80] [IRGen] Restore the old code path for emitting existential type metadata for plain protocol and protocol composition types. These types should always be wrapped in ExistentialType, but there isn't sufficient validation of this throughout the compiler yet. Change the fatal error when the metadata request sees these plain types to an assert and restore the old type metadata emission path for protocol and protocol composition types to avoid crashing in those cases. --- lib/IRGen/MetadataRequest.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 487e2776e886a..a1d5e3a4e9c77 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1883,7 +1883,16 @@ namespace { MetadataResponse visitProtocolType(CanProtocolType type, DynamicMetadataRequest request) { - llvm_unreachable("constraint type should be wrapped in existential type"); + assert(false && "constraint type should be wrapped in existential type"); + + CanExistentialType existential( + ExistentialType::get(type)->castTo()); + + if (auto metatype = tryGetLocal(existential, request)) + return metatype; + + auto metadata = emitExistentialTypeMetadata(existential); + return setLocal(type, MetadataResponse::forComplete(metadata)); } MetadataResponse @@ -1892,7 +1901,16 @@ namespace { if (type->isAny() || type->isAnyObject()) return emitSingletonExistentialTypeMetadata(type); - llvm_unreachable("constraint type should be wrapped in existential type"); + assert(false && "constraint type should be wrapped in existential type"); + + CanExistentialType existential( + ExistentialType::get(type)->castTo()); + + if (auto metatype = tryGetLocal(existential, request)) + return metatype; + + auto metadata = emitExistentialTypeMetadata(existential); + return setLocal(type, MetadataResponse::forComplete(metadata)); } MetadataResponse From 2ee820cb40c7b38b7d578b5f22751801615e38f8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 27 Apr 2022 13:56:06 -0700 Subject: [PATCH 63/80] [ConstraintSystem] Extend availability check to cover unavailable extensions The solver used to check availability attribute on the declaration itself, a better approach is to go through `AvailableAttr::isUnavailable` because that also covers unavailable extensions. Resolves: rdar://92364955 (cherry picked from commit 9a32db7871258ac4a33f4c28707a191e0f65ac11) --- lib/Sema/ConstraintSystem.cpp | 2 +- test/Constraints/availability.swift | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1701aa154caf1..d8012eb89400c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -6263,7 +6263,7 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { bool ConstraintSystem::isDeclUnavailable(const Decl *D, ConstraintLocator *locator) const { // First check whether this declaration is universally unavailable. - if (D->getAttrs().isUnavailable(getASTContext())) + if (AvailableAttr::isUnavailable(D)) return true; return TypeChecker::isDeclarationUnavailable(D, DC, [&] { diff --git a/test/Constraints/availability.swift b/test/Constraints/availability.swift index 2fe93b9f20286..3e70211d47089 100644 --- a/test/Constraints/availability.swift +++ b/test/Constraints/availability.swift @@ -43,3 +43,25 @@ func unavailableFunction(_ x: Int) -> Bool { true } // expected-note {{'unavaila func sr13260(_ arr: [Int]) { for x in arr where unavailableFunction(x) {} // expected-error {{'unavailableFunction' is unavailable}} } + +// rdar://92364955 - ambiguity with member declared in unavailable extension +struct WithUnavailableExt { +} + +@available(*, unavailable) +extension WithUnavailableExt { + static var foo: WithUnavailableExt = WithUnavailableExt() +} + +func test_no_ambiguity_with_unavailable_ext() { + struct A { + static var foo: A = A() + } + + struct Test { + init(_: A) {} + init(_: WithUnavailableExt) {} + } + + _ = Test(.foo) // Ok `A.foo` since `foo` from `WithUnavailableExt` is unavailable +} From 891a1fc7431821443f9b06be1686c96a641c9d54 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 22 Apr 2022 17:25:00 -0700 Subject: [PATCH 64/80] [CSStep] Abort any binding step when constraint system is 'too complex' Previously the "too complex" would be detected by `ComponentStep::take` but binding steps would still proceed until the producer is exhausted because it considers failure of the previous resume to be just a failed choice. (cherry picked from commit 03fab6752ccb971059175b1d0f01db8301455e6a) --- lib/Sema/CSStep.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index a02536931e7ed..55269861313bb 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -504,6 +504,11 @@ template class BindingStep : public SolverStep { public: StepResult take(bool prevFailed) override { + // Before attempting the next choice, let's check whether the constraint + // system is too complex already. + if (CS.getExpressionTooComplex(Solutions)) + return done(/*isSuccess=*/false); + while (auto choice = Producer()) { if (shouldSkip(*choice)) continue; From 4fb62041fd3b7e87dafba67bc46f60f5aa31bd14 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 22 Apr 2022 17:34:03 -0700 Subject: [PATCH 65/80] [ConstraintSystem] `getExpressionTooComplex` should check the flag before computing anything (cherry picked from commit e2b475959cb378d299fa7b96a00515c2eb1be858) --- include/swift/Sema/ConstraintSystem.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 3edd230ebb928..2d98f28dc5542 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5450,6 +5450,9 @@ class ConstraintSystem { } bool getExpressionTooComplex(SmallVectorImpl const &solutions) { + if (isExpressionAlreadyTooComplex) + return true; + size_t solutionMemory = 0; for (auto const& s : solutions) { solutionMemory += s.getTotalMemory(); From 8465a2304b9f7689705e5802b5ee7eebf99409b7 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Apr 2022 10:14:05 -0700 Subject: [PATCH 66/80] [ConstraintSystem] NFC: Drop `Expression` from `isTooComplex` check Solver can now handle multiple different targets e.g. multi-statement closures, result builders etc. So it's more appropriate to say that the constraint system is too complex. (cherry picked from commit 7dab90cd9830d992ae9e8e901abfbbde65c1eb30) --- include/swift/Sema/ConstraintSystem.h | 18 +++++++++--------- lib/Sema/CSSolver.cpp | 4 ++-- lib/Sema/CSStep.cpp | 4 ++-- lib/Sema/CSStep.h | 2 +- lib/Sema/ConstraintSystem.cpp | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 2d98f28dc5542..7d8e2b59222fc 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5415,9 +5415,9 @@ class ConstraintSystem { /// Determine if we've already explored too many paths in an /// attempt to solve this expression. - bool isExpressionAlreadyTooComplex = false; - bool getExpressionTooComplex(size_t solutionMemory) { - if (isExpressionAlreadyTooComplex) + bool isAlreadyTooComplex = false; + bool isTooComplex(size_t solutionMemory) { + if (isAlreadyTooComplex) return true; auto CancellationFlag = getASTContext().CancellationFlag; @@ -5428,7 +5428,7 @@ class ConstraintSystem { MaxMemory = std::max(used, MaxMemory); auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold; if (MaxMemory > threshold) { - return isExpressionAlreadyTooComplex= true; + return isAlreadyTooComplex= true; } if (Timer && Timer->isExpired()) { @@ -5437,27 +5437,27 @@ class ConstraintSystem { // emitting an error. Timer->disableWarning(); - return isExpressionAlreadyTooComplex = true; + return isAlreadyTooComplex = true; } // Bail out once we've looked at a really large number of // choices. if (CountScopes > getASTContext().TypeCheckerOpts.SolverBindingThreshold) { - return isExpressionAlreadyTooComplex = true; + return isAlreadyTooComplex = true; } return false; } - bool getExpressionTooComplex(SmallVectorImpl const &solutions) { - if (isExpressionAlreadyTooComplex) + bool isTooComplex(SmallVectorImpl const &solutions) { + if (isAlreadyTooComplex) return true; size_t solutionMemory = 0; for (auto const& s : solutions) { solutionMemory += s.getTotalMemory(); } - return getExpressionTooComplex(solutionMemory); + return isTooComplex(solutionMemory); } // If the given constraint is an applied disjunction, get the argument function diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index eb2aadbdbf751..06259186fe432 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1374,7 +1374,7 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, SmallVector solutions; solve(solutions, allowFreeTypeVariables); - if (getExpressionTooComplex(solutions)) + if (isTooComplex(solutions)) return SolutionResult::forTooComplex(); switch (solutions.size()) { @@ -1415,7 +1415,7 @@ bool ConstraintSystem::solve(SmallVectorImpl &solutions, filterSolutions(solutions); // We fail if there is no solution or the expression was too complex. - return solutions.empty() || getExpressionTooComplex(solutions); + return solutions.empty() || isTooComplex(solutions); } void ConstraintSystem::solveImpl(SmallVectorImpl &solutions) { diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 5cb3ccd17aacb..1ae3b69fef678 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -251,7 +251,7 @@ bool SplitterStep::mergePartialSolutions() const { // Since merging partial solutions can go exponential, make sure we didn't // pass the "too complex" thresholds including allocated memory and time. - if (CS.getExpressionTooComplex(solutionMemory)) + if (CS.isTooComplex(solutionMemory)) return false; } while (nextCombination(counts, indices)); @@ -313,7 +313,7 @@ StepResult ComponentStep::take(bool prevFailed) { // One of the previous components created by "split" // failed, it means that we can't solve this component. if ((prevFailed && DependsOnPartialSolutions.empty()) || - CS.getExpressionTooComplex(Solutions)) + CS.isTooComplex(Solutions)) return done(/*isSuccess=*/false); // Setup active scope, only if previous component didn't fail. diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 55269861313bb..66d3edb0cb5c8 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -506,7 +506,7 @@ template class BindingStep : public SolverStep { StepResult take(bool prevFailed) override { // Before attempting the next choice, let's check whether the constraint // system is too complex already. - if (CS.getExpressionTooComplex(Solutions)) + if (CS.isTooComplex(Solutions)) return done(/*isSuccess=*/false); while (auto choice = Producer()) { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1701aa154caf1..472fa2788370f 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3776,7 +3776,7 @@ SolutionResult ConstraintSystem::salvage() { // Fall through to produce diagnostics. } - if (getExpressionTooComplex(viable)) + if (isTooComplex(viable)) return SolutionResult::forTooComplex(); // Could not produce a specific diagnostic; punt to the client. From 0753024bf424af30b88dd8bea0f23941af903533 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Apr 2022 10:31:53 -0700 Subject: [PATCH 67/80] [ConstraintSystem] Save "too complex" source range if known This would help to diagnose the initial point where constraint system has been determine to be "too complex" (cherry picked from commit ef2f24b82a1b8d6fd7ff0538190172405ff9ddaf) --- include/swift/Sema/ConstraintSystem.h | 26 ++++++++++++++++----- lib/Sema/ConstraintSystem.cpp | 33 ++++++++++++++++----------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 7d8e2b59222fc..b867cf736bc81 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -194,6 +194,8 @@ class ExpressionTimer { AnchorType getAnchor() const { return Anchor; } + SourceRange getAffectedRange() const; + unsigned getWarnLimit() const { return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking; } @@ -5415,9 +5417,17 @@ class ConstraintSystem { /// Determine if we've already explored too many paths in an /// attempt to solve this expression. - bool isAlreadyTooComplex = false; + std::pair isAlreadyTooComplex = {false, SourceRange()}; + + /// If optional is not nil, result is guaranteed to point at a valid + /// location. + Optional getTooComplexRange() const { + auto range = isAlreadyTooComplex.second; + return range.isValid() ? range : Optional(); + } + bool isTooComplex(size_t solutionMemory) { - if (isAlreadyTooComplex) + if (isAlreadyTooComplex.first) return true; auto CancellationFlag = getASTContext().CancellationFlag; @@ -5428,7 +5438,9 @@ class ConstraintSystem { MaxMemory = std::max(used, MaxMemory); auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold; if (MaxMemory > threshold) { - return isAlreadyTooComplex= true; + // No particular location for OoM problems. + isAlreadyTooComplex.first = true; + return true; } if (Timer && Timer->isExpired()) { @@ -5437,20 +5449,22 @@ class ConstraintSystem { // emitting an error. Timer->disableWarning(); - return isAlreadyTooComplex = true; + isAlreadyTooComplex = {true, Timer->getAffectedRange()}; + return true; } // Bail out once we've looked at a really large number of // choices. if (CountScopes > getASTContext().TypeCheckerOpts.SolverBindingThreshold) { - return isAlreadyTooComplex = true; + isAlreadyTooComplex.first = true; + return true; } return false; } bool isTooComplex(SmallVectorImpl const &solutions) { - if (isAlreadyTooComplex) + if (isAlreadyTooComplex.first) return true; size_t solutionMemory = 0; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 472fa2788370f..1f74882266610 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -56,6 +56,22 @@ ExpressionTimer::ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS, PrintDebugTiming(CS.getASTContext().TypeCheckerOpts.DebugTimeExpressions), PrintWarning(true) {} +SourceRange ExpressionTimer::getAffectedRange() const { + ASTNode anchor; + + if (auto *locator = Anchor.dyn_cast()) { + anchor = simplifyLocatorToAnchor(locator); + // If locator couldn't be simplified down to a single AST + // element, let's use its root. + if (!anchor) + anchor = locator->getAnchor(); + } else { + anchor = Anchor.get(); + } + + return anchor.getSourceRange(); +} + ExpressionTimer::~ExpressionTimer() { auto elapsed = getElapsedProcessTimeInFractionalSeconds(); unsigned elapsedMS = static_cast(elapsed * 1000); @@ -81,22 +97,13 @@ ExpressionTimer::~ExpressionTimer() { if (WarnLimit == 0 || elapsedMS < WarnLimit) return; - ASTNode anchor; - if (auto *locator = Anchor.dyn_cast()) { - anchor = simplifyLocatorToAnchor(locator); - // If locator couldn't be simplified down to a single AST - // element, let's warn about its root. - if (!anchor) - anchor = locator->getAnchor(); - } else { - anchor = Anchor.get(); - } + auto sourceRange = getAffectedRange(); - if (anchor.getStartLoc().isValid()) { + if (sourceRange.Start.isValid()) { Context.Diags - .diagnose(anchor.getStartLoc(), diag::debug_long_expression, elapsedMS, + .diagnose(sourceRange.Start, diag::debug_long_expression, elapsedMS, WarnLimit) - .highlight(anchor.getSourceRange()); + .highlight(sourceRange); } } From 736863bcfa3a43ffdfb77bc5494d82006204a312 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Apr 2022 14:09:45 -0700 Subject: [PATCH 68/80] [ConstraintSystem] Use affected range (if any) to diagnose 'too complex' failures (cherry picked from commit a40e8e80c1e1212bf43751693c50b4837b02dc85) --- include/swift/Sema/SolutionResult.h | 11 ++++++++--- lib/Sema/CSApply.cpp | 6 ++++++ lib/Sema/CSSolver.cpp | 19 ++++++++++++++----- lib/Sema/ConstraintSystem.cpp | 2 +- .../type_checker_perf/slow/rdar19612086.swift | 3 +-- .../type_checker_perf/slow/rdar22079400.swift | 3 +-- 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/include/swift/Sema/SolutionResult.h b/include/swift/Sema/SolutionResult.h index 04bac3b6dc8dd..99cbb4bf5bc8d 100644 --- a/include/swift/Sema/SolutionResult.h +++ b/include/swift/Sema/SolutionResult.h @@ -64,6 +64,9 @@ class SolutionResult { /// \c numSolutions entries. Solution *solutions = nullptr; + /// A source range that was too complex to solve. + Optional TooComplexAt = None; + /// General constructor for the named constructors. SolutionResult(Kind kind) : kind(kind) { emittedDiagnostic = false; @@ -95,9 +98,7 @@ class SolutionResult { /// Produce a "too complex" failure, which was not yet been /// diagnosed. - static SolutionResult forTooComplex() { - return SolutionResult(TooComplex); - } + static SolutionResult forTooComplex(Optional affected); /// Produce a failure that has already been diagnosed. static SolutionResult forError() { @@ -123,6 +124,10 @@ class SolutionResult { /// Take the set of solutions when there is an ambiguity. MutableArrayRef takeAmbiguousSolutions() &&; + /// Retrieve a range of source that has been determined to be too + /// complex to solve in a reasonable time. + Optional getTooComplexAt() const { return TooComplexAt; } + /// Whether this solution requires the client to produce a diagnostic. bool requiresDiagnostic() const { switch (kind) { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d7d3d914f3f3a..0bf11a0ae25d6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -9145,6 +9145,12 @@ SolutionResult SolutionResult::forAmbiguous( return result; } +SolutionResult SolutionResult::forTooComplex(Optional affected) { + SolutionResult result(Kind::TooComplex); + result.TooComplexAt = affected; + return result; +} + SolutionResult::~SolutionResult() { assert((!requiresDiagnostic() || emittedDiagnostic) && "SolutionResult was destroyed without emitting a diagnostic"); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 06259186fe432..969b860d1a611 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1298,12 +1298,21 @@ Optional> ConstraintSystem::solve( maybeProduceFallbackDiagnostic(target); return None; - case SolutionResult::TooComplex: - getASTContext().Diags.diagnose( - target.getLoc(), diag::expression_too_complex) - .highlight(target.getSourceRange()); + case SolutionResult::TooComplex: { + auto affectedRange = solution.getTooComplexAt(); + + // If affected range is unknown, let's use whole + // target. + if (!affectedRange) + affectedRange = target.getSourceRange(); + + getASTContext() + .Diags.diagnose(affectedRange->Start, diag::expression_too_complex) + .highlight(*affectedRange); + solution.markAsDiagnosed(); return None; + } case SolutionResult::Ambiguous: // If salvaging produced an ambiguous result, it has already been @@ -1375,7 +1384,7 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, solve(solutions, allowFreeTypeVariables); if (isTooComplex(solutions)) - return SolutionResult::forTooComplex(); + return SolutionResult::forTooComplex(getTooComplexRange()); switch (solutions.size()) { case 0: diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1f74882266610..ffabea5807026 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3784,7 +3784,7 @@ SolutionResult ConstraintSystem::salvage() { } if (isTooComplex(viable)) - return SolutionResult::forTooComplex(); + return SolutionResult::forTooComplex(getTooComplexRange()); // Could not produce a specific diagnostic; punt to the client. return SolutionResult::forUndiagnosedError(); diff --git a/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift b/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift index da6e11712f1ee..1b85cdf490590 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar19612086.swift @@ -11,12 +11,11 @@ struct rdar19612086 { let x = 1.0 var description : String { - return "\(i)" + Stringly(format: "%.2f", x) + + return "\(i)" + Stringly(format: "%.2f", x) + // expected-error {{reasonable time}} "\(i+1)" + Stringly(format: "%.2f", x) + "\(i+2)" + Stringly(format: "%.2f", x) + "\(i+3)" + Stringly(format: "%.2f", x) + "\(i+4)" + Stringly(format: "%.2f", x) + "\(i+5)" + Stringly(format: "%.2f", x) - // expected-error@-1 {{reasonable time}} } } diff --git a/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift b/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift index 8f22a4ab7a37b..ea8334f5b28fa 100644 --- a/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift +++ b/validation-test/Sema/type_checker_perf/slow/rdar22079400.swift @@ -1,9 +1,8 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: tools-release,no_asan -let _ = (0...1).lazy.flatMap { +let _ = (0...1).lazy.flatMap { // expected-error {{reasonable time}} a in (1...2).lazy.map { b in (a, b) } }.filter { - // expected-error@-1 {{reasonable time}} 1 < $0 && $0 < $1 && $0 + $1 < 3 } From 7796f53910fa561a748f351ba0cac0e6bc8b3327 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 25 Apr 2022 14:10:58 -0700 Subject: [PATCH 69/80] [TypeChecker] NFC: Add a "slow" perf test-case for rdar://91310777 (cherry picked from commit 7869b73a73408ad03efab189527e401e912aff9f) --- .../type_checker_perf/slow/rdar91310777.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 validation-test/Sema/type_checker_perf/slow/rdar91310777.swift diff --git a/validation-test/Sema/type_checker_perf/slow/rdar91310777.swift b/validation-test/Sema/type_checker_perf/slow/rdar91310777.swift new file mode 100644 index 0000000000000..eb9dcbee848c8 --- /dev/null +++ b/validation-test/Sema/type_checker_perf/slow/rdar91310777.swift @@ -0,0 +1,16 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: tools-release,no_asan + +func compute(_ cont: () -> Void) {} + +func test() { + compute { + let x = 42 + compute { + print(x) + let v: UInt64 = UInt64((24 / UInt32(1)) + UInt32(0) - UInt32(0) - 24 / 42 - 42) + // expected-error@-1 {{the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions}} + print(v) + } + } +} From 185887878246ffcd3f781cc77ef9803eb0db96dc Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 6 Apr 2022 20:26:49 -0700 Subject: [PATCH 70/80] Revert "Add `-async-main` flag to favor asynchronous main" This reverts commit da0a3311a59c87fa5a111e23b1602530e1953893. --- include/swift/Basic/LangOptions.h | 3 --- include/swift/Option/Options.td | 4 ---- include/swift/Sema/ConstraintSystem.h | 6 +----- include/swift/Sema/IDETypeChecking.h | 8 ++------ lib/Driver/ToolChains.cpp | 1 - lib/Frontend/CompilerInvocation.cpp | 2 -- lib/Sema/CSGen.cpp | 7 +++---- lib/Sema/ConstraintSystem.cpp | 5 ----- lib/Sema/TypeCheckAttr.cpp | 8 +------- 9 files changed, 7 insertions(+), 37 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 8e38e4be0e4c5..5eeb955fd8f9d 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -223,9 +223,6 @@ namespace swift { /// Emit a remark after loading a module. bool EnableModuleLoadingRemarks = false; - /// Resolve main function as though it were called from an async context - bool EnableAsyncMainResolution = false; - /// /// Support for alternate usage modes /// diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 770c81c0855a0..36085ad77d86e 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -270,10 +270,6 @@ def pch_output_dir: Separate<["-"], "pch-output-dir">, Flags<[FrontendOption, HelpHidden, ArgumentIsPath]>, HelpText<"Directory to persist automatically created precompiled bridging headers">; -def async_main: Flag<["-"], "async-main">, - Flags<[FrontendOption]>, - HelpText<"Resolve main function as if it were called from an asynchronous context">; - // FIXME: Unhide this once it doesn't depend on an output file map. def incremental : Flag<["-"], "incremental">, Flags<[NoInteractiveOption, HelpHidden, DoesNotAffectIncrementalBuild]>, diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 3edd230ebb928..704e3db8564dc 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1491,7 +1491,7 @@ enum class ConstraintSystemFlags { /// Note that this flag is automatically applied to all constraint systems, /// when \c DebugConstraintSolver is set in \c TypeCheckerOptions. It can be /// automatically enabled for select constraint solving attempts by setting - /// \c DebugConstraintSolverAttempt. Finally, it can also be automatically + /// \c DebugConstraintSolverAttempt. Finally, it can also be automatically /// enabled for a pre-configured set of expressions on line numbers by setting /// \c DebugConstraintSolverOnLines. DebugConstraints = 0x10, @@ -1515,10 +1515,6 @@ enum class ConstraintSystemFlags { /// calling conventions, say due to Clang attributes such as /// `__attribute__((ns_consumed))`. UseClangFunctionTypes = 0x80, - - /// When set, nominal typedecl contexts are asynchronous contexts. - /// This is set while searching for the main function - ConsiderNominalTypeContextsAsync = 0x100, }; /// Options that affect the constraint system as a whole. diff --git a/include/swift/Sema/IDETypeChecking.h b/include/swift/Sema/IDETypeChecking.h index 750aa5ef50c3b..c5e53591d007c 100644 --- a/include/swift/Sema/IDETypeChecking.h +++ b/include/swift/Sema/IDETypeChecking.h @@ -21,7 +21,6 @@ #include "swift/AST/Identifier.h" #include "swift/Basic/SourceLoc.h" -#include "swift/Basic/OptionSet.h" #include #include @@ -51,8 +50,6 @@ namespace swift { class ConstraintSystem; class Solution; class SolutionApplicationTarget; - enum class ConstraintSystemFlags; - using ConstraintSystemOptions = OptionSet; } /// Typecheck binding initializer at \p bindingIndex. @@ -96,9 +93,8 @@ namespace swift { /// Unlike other member lookup functions, \c swift::resolveValueMember() /// should be used when you want to look up declarations with the same name as /// one you already have. - ResolvedMemberResult - resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name, - constraints::ConstraintSystemOptions Options = {}); + ResolvedMemberResult resolveValueMember(DeclContext &DC, Type BaseTy, + DeclName Name); /// Given a type and an extension to the original type decl of that type, /// decide if the extension has been applied, i.e. if the requirements of the diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 0f1913d5c7939..ef6d7900ff334 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -300,7 +300,6 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_access_notes_path); inputArgs.AddLastArg(arguments, options::OPT_library_level); inputArgs.AddLastArg(arguments, options::OPT_enable_bare_slash_regex); - inputArgs.AddLastArg(arguments, options::OPT_async_main); // Pass on any build config options inputArgs.AddAllArgs(arguments, options::OPT_D); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 33c289132f9af..b517d20aa20df 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -489,8 +489,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Diags.diagnose(SourceLoc(), diag::warn_flag_deprecated, "-enable-experimental-async-top-level"); - Opts.EnableAsyncMainResolution = Args.hasArg(OPT_async_main); - Opts.DiagnoseInvalidEphemeralnessAsError |= Args.hasArg(OPT_enable_invalid_ephemeralness_as_error); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index cfd7fc74bb266..baf18816113bf 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4424,11 +4424,10 @@ getMemberDecls(InterestedMemberKind Kind) { llvm_unreachable("unhandled kind"); } -ResolvedMemberResult -swift::resolveValueMember(DeclContext &DC, Type BaseTy, DeclName Name, - ConstraintSystemOptions Options) { +ResolvedMemberResult swift::resolveValueMember(DeclContext &DC, Type BaseTy, + DeclName Name) { ResolvedMemberResult Result; - ConstraintSystem CS(&DC, Options); + ConstraintSystem CS(&DC, None); // Look up all members of BaseTy with the given Name. MemberLookupResult LookupResult = CS.performMemberLookup( diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1701aa154caf1..97bb8ff8a2ab6 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2900,11 +2900,6 @@ bool ConstraintSystem::isAsynchronousContext(DeclContext *dc) { FunctionType::ExtInfo()).isAsync(); } - if (Options.contains( - ConstraintSystemFlags::ConsiderNominalTypeContextsAsync) && - isa(dc)) - return true; - return false; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 932834ca7df9b..a2c8a66ae28b6 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2171,14 +2171,8 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, // mainType.main() from the entry point, and that would require fully // type-checking the call to mainType.main(). - constraints::ConstraintSystemOptions lookupOptions; - if (context.LangOpts.EnableAsyncMainResolution) - lookupOptions |= - constraints::ConstraintSystemFlags::ConsiderNominalTypeContextsAsync; - auto resolution = resolveValueMember( - *declContext, nominal->getInterfaceType(), context.Id_main, - lookupOptions); + *declContext, nominal->getInterfaceType(), context.Id_main); FuncDecl *mainFunction = resolveMainFunctionDecl(declContext, resolution, context); if (!mainFunction) { From edda7a9d467eca2e46cfc1b275255becf902aa97 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 20 Apr 2022 11:03:12 -0700 Subject: [PATCH 71/80] Ignore async/sync mismatch on main The main function is different from other function resolutions. It isn't being called from a synchronous or asynchronous context, but defines whether the program starts in a synchronous or async context. As a result, we should not prefer one over the other, so the scoring mechanism shouldn't involve the async/sync score when resolving the main function. This patch adds a constraint solver flag to ignore async/sync context mismatches so that we do not favor one over the other, but otherwise use the normal resolution behavior. --- include/swift/Sema/ConstraintSystem.h | 3 +++ lib/Sema/ConstraintSystem.cpp | 3 ++- lib/Sema/TypeCheckAttr.cpp | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 704e3db8564dc..89f37ff01f743 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -1515,6 +1515,9 @@ enum class ConstraintSystemFlags { /// calling conventions, say due to Clang attributes such as /// `__attribute__((ns_consumed))`. UseClangFunctionTypes = 0x80, + + /// When set, ignore async/sync mismatches + IgnoreAsyncSyncMismatch = 0x100, }; /// Options that affect the constraint system as a whole. diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 97bb8ff8a2ab6..9474d73749d11 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3304,7 +3304,8 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // If we're choosing an asynchronous declaration within a synchronous // context, or vice-versa, increase the async/async mismatch score. if (auto func = dyn_cast(decl)) { - if (!func->hasPolymorphicEffect(EffectKind::Async) && + if (!Options.contains(ConstraintSystemFlags::IgnoreAsyncSyncMismatch) && + !func->hasPolymorphicEffect(EffectKind::Async) && func->isAsyncContext() != isAsynchronousContext(useDC)) { increaseScore( func->isAsyncContext() ? SK_AsyncInSyncMismatch : SK_SyncInAsync); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index a2c8a66ae28b6..bcfa1a1ac1026 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2172,7 +2172,8 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, // type-checking the call to mainType.main(). auto resolution = resolveValueMember( - *declContext, nominal->getInterfaceType(), context.Id_main); + *declContext, nominal->getInterfaceType(), context.Id_main, + constraints::ConstraintSystemFlags::IgnoreAsyncSyncMismatch); FuncDecl *mainFunction = resolveMainFunctionDecl(declContext, resolution, context); if (!mainFunction) { From 1b419c92ff192d1a1cf869ba74286dc1d57dbd5d Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Tue, 26 Apr 2022 13:57:39 -0700 Subject: [PATCH 72/80] Use disjunction to resolve main function Using `resolveValueMember` can result in the wrong, or unrelated main functions being selected. If an error occurs while resolving, such as an ambiguous resolution, the solution will contain everything called 'main'. During the resolution, the `BestOverload` will not be set, so the code skips down to iterating over the member decls and selecting the first declaration that is a `MainTypeMainMethod`. The order of candidates in the failure state is related to the order that the declarations appear in source. The selected and potentially unrelated main function is called from `$main`. The call in the expression will then detect the invalid state later. When only a single main function could exist in a given context, this was not a problem. Any other possible solutions would result in an error, either due to a duplicate declaration, or from calling an unrelated function. When we introduced the asynchronous main function, the resolution can be ambiguous because we allow asynchronous overloads of synchronous functions. This enables us to legally write ``` struct MainType { static func main() { } static func main() async { } } ``` From the perspective of duplicate declarations, this is not an issue. It is, however, ambiguous since we have no calling context with which to break the tie. In the original code, this example would select the synchronous main function because it comes first in the source. If we flip the declarations, the asynchronous main function is selected because it comes first in the source. Instead, using the constraint solver to solve for the correct overload will ensure that we only get back a valid main function. If the constraint solver reports no solutions or many solutions, we can emit an error immediately, as it will not consider unrelated functions. --- lib/Sema/TypeCheckAttr.cpp | 106 +++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 27 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index bcfa1a1ac1026..8dab8184d3386 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2096,28 +2096,6 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) { return std::make_pair(body, /*typechecked=*/false); } -static FuncDecl *resolveMainFunctionDecl(DeclContext *declContext, - ResolvedMemberResult &resolution, - ASTContext &ctx) { - // Choose the best overload if it's a main function - if (resolution.hasBestOverload()) { - ValueDecl *best = resolution.getBestOverload(); - if (FuncDecl *func = dyn_cast(best)) { - if (func->isMainTypeMainMethod()) { - return func; - } - } - } - // Look for the most highly-ranked main-function candidate - for (ValueDecl *candidate : resolution.getMemberDecls(Viable)) { - if (FuncDecl *func = dyn_cast(candidate)) { - if (func->isMainTypeMainMethod()) - return func; - } - } - return nullptr; -} - FuncDecl * SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, Decl *D) const { @@ -2171,11 +2149,85 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, // mainType.main() from the entry point, and that would require fully // type-checking the call to mainType.main(). - auto resolution = resolveValueMember( - *declContext, nominal->getInterfaceType(), context.Id_main, - constraints::ConstraintSystemFlags::IgnoreAsyncSyncMismatch); - FuncDecl *mainFunction = - resolveMainFunctionDecl(declContext, resolution, context); + constraints::ConstraintSystem CS( + declContext, constraints::ConstraintSystemFlags::IgnoreAsyncSyncMismatch); + constraints::ConstraintLocator *locator = CS.getConstraintLocator({}); + // Allowed main function types + // `() -> Void` + // `() async -> Void` + // `() throws -> Void` + // `() async throws -> Void` + // `@MainActor () -> Void` + // `@MainActor () async -> Void` + // `@MainActor () throws -> Void` + // `@MainActor () async throws -> Void` + { + llvm::SmallVector mainTypes = { + + FunctionType::get(/*params*/ {}, context.TheEmptyTupleType, + ASTExtInfo()), + FunctionType::get( + /*params*/ {}, context.TheEmptyTupleType, + ASTExtInfoBuilder().withAsync().build()), + + FunctionType::get(/*params*/ {}, context.TheEmptyTupleType, + ASTExtInfoBuilder().withThrows().build()), + + FunctionType::get( + /*params*/ {}, context.TheEmptyTupleType, + ASTExtInfoBuilder().withAsync().withThrows().build())}; + + Type mainActor = context.getMainActorType(); + if (mainActor) { + mainTypes.push_back(FunctionType::get( + /*params*/ {}, context.TheEmptyTupleType, + ASTExtInfoBuilder().withGlobalActor(mainActor).build())); + mainTypes.push_back(FunctionType::get( + /*params*/ {}, context.TheEmptyTupleType, + ASTExtInfoBuilder().withAsync().withGlobalActor(mainActor).build())); + mainTypes.push_back(FunctionType::get( + /*params*/ {}, context.TheEmptyTupleType, + ASTExtInfoBuilder().withThrows().withGlobalActor(mainActor).build())); + mainTypes.push_back(FunctionType::get(/*params*/ {}, + context.TheEmptyTupleType, + ASTExtInfoBuilder() + .withAsync() + .withThrows() + .withGlobalActor(mainActor) + .build())); + } + + llvm::SmallVector mainTypeConstraints; + for (const Type &mainType : mainTypes) { + constraints::Constraint *fnConstraint = + constraints::Constraint::createMember( + CS, constraints::ConstraintKind::ValueMember, + nominal->getInterfaceType(), mainType, + DeclNameRef(context.Id_main), declContext, + FunctionRefKind::SingleApply, locator); + mainTypeConstraints.push_back(fnConstraint); + } + + CS.addDisjunctionConstraint(mainTypeConstraints, locator); + } + + FuncDecl *mainFunction = nullptr; + llvm::SmallVector candidates; + + if (!CS.solve(candidates, FreeTypeVariableBinding::Disallow)) { + if (candidates.size() != 1) { + context.Diags.diagnose(nominal->getLoc(), diag::ambiguous_decl_ref, + DeclNameRef(context.Id_main)); + // TODO: CS.diagnoseAmbiguity doesn't report anything because the types + // are different. It would be good to get notes on the decls causing the + // ambiguity. + attr->setInvalid(); + return nullptr; + } + mainFunction = dyn_cast( + candidates[0].overloadChoices[locator].choice.getDecl()); + } + if (!mainFunction) { const bool hasAsyncSupport = AvailabilityContext::forDeploymentTarget(context).isContainedIn( From d2038562020bff49ea006dbef9dfb348c18600f0 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Tue, 26 Apr 2022 14:01:27 -0700 Subject: [PATCH 73/80] Allow checking for invalid @main Changing the overload resolution behavior results in the ordering that `main` and `$main` are typechecked. If there is an error in parsing or sema before we have typechecked these two functions, we get weird errors about `$main` calling the main-actor `main` function being invalid. This is because we have already set the `@main` attribute to invalid, so we don't recognize the `$main` context as being a main entrypoint. The error message compounds to making a confusing situation worse given that `$main` is implicit. So by ignoring the failed `@main` attribute, we still annotate the `$main` and `main` function as being on the main actor, so everyone is happy in that respect. To get the nasty behavior, you can forget to pass `-parse-as-library` with this commit removed. --- lib/Sema/TypeCheckConcurrency.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index c579a926fd682..810c7c379f063 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -3695,7 +3695,8 @@ getActorIsolationForMainFuncDecl(FuncDecl *fnDecl) { if (!declContext) return {}; const bool isMainDeclContext = - declContext->getAttrs().hasAttribute(); + declContext->getAttrs().hasAttribute( + /*allow invalid*/ true); ASTContext &ctx = fnDecl->getASTContext(); From 7acbb87a7f885bcf4b9600384b7f359c37241fb5 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Tue, 26 Apr 2022 15:49:05 -0700 Subject: [PATCH 74/80] Update tests This patch contains the updates for the tests. The merge removes the `-async-main` flag, so the tests using that flag fail on that. Additionally, the tests reflect the newer behavior of the main resolution. `async_main_resolution` verifies that we're not preferring an async function over a synchronous function, and vice versa. This is also verified in `where_clause_main_resolution`, where we select a main function using various configuration typealiases. Finally, we only select a valid, usable main function. The old machinery could select one sort of at random (technically it selected the first overload declared in the source file) if an error occurred. Errors that resulted in this behavior included a missing valid function, or an ambiguous overload. In the case of the missing valid function, the function returned would be from an invalid location if one existed, which was called from `$main`. `$main` had sufficient context to realize that the main function being called was not valid, and would emit an error saying why. It would be better to realize that we're not getting a valid main function earlier, and later we can emit notes saying why each function called main could not be selected. --- test/Concurrency/async_main_resolution.swift | 38 +++++++++---------- .../where_clause_main_resolution.swift | 28 ++++---------- ...truct_from_two_protocols_one_missing.swift | 4 +- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/test/Concurrency/async_main_resolution.swift b/test/Concurrency/async_main_resolution.swift index 8fe98e35c5307..51af600be6187 100644 --- a/test/Concurrency/async_main_resolution.swift +++ b/test/Concurrency/async_main_resolution.swift @@ -1,6 +1,11 @@ +// This test aims to show that no preference is given to either the async or +// sync main function. The most specific, valid, main function will be +// selected if one exists. If two main functions could exist, the usage is +// ambiguous. + // async main is nested deeper in protocols than sync, use sync // sync main is nested deeper in protocols than async, use async -// async and sync are same level, use async +// async and sync are same level, error // REQUIRES: concurrency @@ -10,23 +15,17 @@ // BOTH: MainProtocol has both sync and async main // INHERIT_SYNC: main type directly conforms to synchronous main protocol -// | async flag | has async main | has sync main | both | inherits sync | nested async | Result | Run | -// | | | | | | | Error | RUN: not %target-swift-frontend -disable-availability-checking -DNO_ASYNC -DNO_SYNC -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR -// | | x | | | | | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DNO_SYNC -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC -// | x | | x | | | | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DNO_ASYNC -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC -// | x | x | x | | | | Async Main | RUN: %target-swift-frontend -disable-availability-checking -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC -// | | x | x | | | | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC -// | | x | x | | | x | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC -// | | x | x | | x | x | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DINHERIT_SYNC -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC -// | x | x | x | | x | x | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DINHERIT_SYNC -DASYNC_NESTED -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC -// | x | | x | | x | x | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DNO_ASYNC -DINHERIT_SYNC -DASYNC_NESTED -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC -// | | | x | x | | | Sync Main | RUN: %target-swift-frontend -disable-availability-checking -DBOTH -DNO_ASYNC -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC -// | x | | x | x | | | Async Main | RUN: %target-swift-frontend -disable-availability-checking -DBOTH -DNO_ASYNC -async-main -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC - -// tldr; -// If async flag is set, will pick an asynchronous main function if one is available and related. If none exist, will fall back on synchronous main. -// If async flag is not set, will pick a asynchronous main function if one is available and related. If none exist, will fall back on an asynchronous main -// If neither are available; error +// | has async main | has sync main | both | inherits sync | nested async | Result | | Run | +// | | | | | | Error | No main | RUN: not %target-swift-frontend -disable-availability-checking -DNO_SYNC -DNO_ASYNC -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR1 +// | x | x | x | x | | Error | Ambiguous main in MainP | RUN: not %target-swift-frontend -disable-availability-checking -DBOTH -DINHERIT_SYNC -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR2 +// | | x | x | x | | Error | Ambiguous main in MainP | RUN: not %target-swift-frontend -disable-availability-checking -DBOTH -DINHERIT_SYNC -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR2 +// | x | x | x | | | Async | Directly selected | RUN: %target-swift-frontend -disable-availability-checking -DBOTH -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC +// | x | x | | | | Async | Directly selected | RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC +// | | x | | | | Sync | Indirectly selected | RUN: %target-swift-frontend -disable-availability-checking -DNO_ASYNC -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC +// | x | x | | x | x | Sync | Directly selected | RUN: %target-swift-frontend -disable-availability-checking -DINHERIT_SYNC -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-SYNC +// | x | | | x | x | Async | Indirectly selected | RUN: %target-swift-frontend -disable-availability-checking -DNO_SYNC -DINHERIT_SYNC -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s | %FileCheck %s --check-prefix=CHECK-IS-ASYNC +// | x | | | x | | Error | Unrelated async main | RUN: not %target-swift-frontend -disable-availability-checking -DNO_SYNC -DINHERIT_SYNC -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR1 +// | | x | | | x | Error | Unrelated sync main | RUN: not %target-swift-frontend -disable-availability-checking -DNO_ASYNC -DASYNC_NESTED -parse-as-library -typecheck -dump-ast %s 2>&1 | %FileCheck %s --check-prefix=CHECK-IS-ERROR1 #if ASYNC_NESTED protocol AsyncMainProtocol { } @@ -71,4 +70,5 @@ extension MainProtocol { // CHECK-IS-ASYNC: (func_decl implicit "$main()" interface type='(MyMain.Type) -> () async -> ()' // CHECK-IS-ASYNC: (declref_expr implicit type='(MyMain.Type) -> () async -> ()' -// CHECK-IS-ERROR: error: 'MyMain' is annotated with @main and must provide a main static function of type {{\(\) -> Void or \(\) throws -> Void|\(\) -> Void, \(\) throws -> Void, \(\) async -> Void, or \(\) async throws -> Void}} +// CHECK-IS-ERROR1: error: 'MyMain' is annotated with @main and must provide a main static function of type {{\(\) -> Void or \(\) throws -> Void|\(\) -> Void, \(\) throws -> Void, \(\) async -> Void, or \(\) async throws -> Void}} +// CHECK-IS-ERROR2: error: ambiguous use of 'main' diff --git a/test/Concurrency/where_clause_main_resolution.swift b/test/Concurrency/where_clause_main_resolution.swift index f650ec6f48257..9f491b974b51f 100644 --- a/test/Concurrency/where_clause_main_resolution.swift +++ b/test/Concurrency/where_clause_main_resolution.swift @@ -1,9 +1,6 @@ // RUN: %target-swift-frontend -disable-availability-checking -D CONFIG1 -dump-ast -parse-as-library %s | %FileCheck %s --check-prefixes=CHECK,CHECK-CONFIG1 -// RUN: %target-swift-frontend -disable-availability-checking -D CONFIG1 -dump-ast -parse-as-library -async-main %s | %FileCheck %s --check-prefixes=CHECK,CHECK-CONFIG1-ASYNC // RUN: %target-swift-frontend -disable-availability-checking -D CONFIG2 -dump-ast -parse-as-library %s | %FileCheck %s --check-prefixes=CHECK,CHECK-CONFIG2 -// RUN: %target-swift-frontend -disable-availability-checking -D CONFIG2 -dump-ast -parse-as-library -async-main %s | %FileCheck %s --check-prefixes=CHECK,CHECK-CONFIG2-ASYNC // RUN: %target-swift-frontend -disable-availability-checking -D CONFIG3 -dump-ast -parse-as-library %s | %FileCheck %s --check-prefixes=CHECK,CHECK-CONFIG3 -// RUN: %target-swift-frontend -disable-availability-checking -D CONFIG3 -dump-ast -parse-as-library -async-main %s | %FileCheck %s --check-prefixes=CHECK,CHECK-CONFIG3-ASYNC // REQUIRES: concurrency @@ -21,40 +18,31 @@ protocol App { // CHECK: (source_file "[[SOURCE_FILE:[^"]+]]" // CHECK: (extension_decl range={{\[}}[[SOURCE_FILE]]:{{[0-9]+}}:{{[0-9]+}} - line:{{[0-9]+}}:{{[0-9]+}}{{\]}} App where // CHECK: (extension_decl range={{\[}}[[SOURCE_FILE]]:{{[0-9]+}}:{{[0-9]+}} - line:{{[0-9]+}}:{{[0-9]+}}{{\]}} App where +// CHECK: (extension_decl range={{\[}}[[SOURCE_FILE]]:{{[0-9]+}}:{{[0-9]+}} - line:{{[0-9]+}}:{{[0-9]+}}{{\]}} App where // CHECK: (extension_decl range={{\[}}[[SOURCE_FILE]]:{{[0-9]+}}:{{[0-9]+}} - line:{{[0-9]+}}:{{[0-9]+}}{{\]}} // CHECK-NOT: where // CHECK-NEXT: (func_decl range={{\[}}[[SOURCE_FILE]]:[[DEFAULT_ASYNCHRONOUS_MAIN_LINE:[0-9]+]]:{{[0-9]+}} - line:{{[0-9]+}}:{{[0-9]+}}{{\]}} "main()" // CHECK-SAME: interface type=' (Self.Type) -> () async -> ()' -// CHECK: (func_decl range={{\[}}[[SOURCE_FILE]]:[[DEFAULT_SYNCHRONOUS_MAIN_LINE:[0-9]+]]:{{[0-9]+}} - line:{{[0-9]+}}:{{[0-9]+}}{{\]}} "main()" -// CHECK-SAME: interface type=' (Self.Type) -> () -> ()' - extension App where Configuration == Config1 { // CHECK-CONFIG1: (func_decl implicit "$main()" interface type='(MainType.Type) -> () -> ()' // CHECK-CONFIG1: [[SOURCE_FILE]]:[[# @LINE+1 ]] static func main() { } - -// CHECK-CONFIG1-ASYNC: (func_decl implicit "$main()" interface type='(MainType.Type) -> () async -> ()' -// CHECK-CONFIG1-ASYNC: [[SOURCE_FILE]]:[[DEFAULT_ASYNCHRONOUS_MAIN_LINE]] } extension App where Configuration == Config2 { -// CHECK-CONFIG2: (func_decl implicit "$main()" interface type='(MainType.Type) -> () -> ()' -// CHECK-CONFIG2: [[SOURCE_FILE]]:[[DEFAULT_SYNCHRONOUS_MAIN_LINE]] - -// CHECK-CONFIG2-ASYNC: (func_decl implicit "$main()" interface type='(MainType.Type) -> () async -> ()' -// CHECK-CONFIG2-ASYNC: [[SOURCE_FILE]]:[[# @LINE+1 ]] +// CHECK-CONFIG2: (func_decl implicit "$main()" interface type='(MainType.Type) -> () async -> ()' +// CHECK-CONFIG2: [[SOURCE_FILE]]:[[# @LINE+1 ]] static func main() async { } } -extension App { +extension App where Configuration == Config3 { // CHECK-CONFIG3-ASYNC: (func_decl implicit "$main()" interface type='(MainType.Type) -> () async -> ()' -// CHECK-CONFIG3-ASYNC: [[SOURCE_FILE]]:[[# @LINE+1 ]] - static func main() async { } +// CHECK-CONFIG3-ASYNC: [[SOURCE_FILE]]:[[DEFAULT_ASYNCHRONOUS_MAIN_LINE]] +} -// CHECK-CONFIG3: (func_decl implicit "$main()" interface type='(MainType.Type) -> () -> ()' -// CHECK-CONFIG3: [[SOURCE_FILE]]:[[# @LINE+1 ]] - static func main() { } +extension App { + static func main() async { } } @main diff --git a/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift b/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift index 561fac3ace780..483a7b90db9b2 100644 --- a/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift +++ b/test/attr/ApplicationMain/attr_main_struct_from_two_protocols_one_missing.swift @@ -8,14 +8,14 @@ protocol Runnable { protocol OtherThing { } -extension Runnable where Self : OtherThing { // expected-note{{where 'Self' = 'EntryPoint'}} +extension Runnable where Self : OtherThing { static func main() { let it = Self.init() it.run() } } -@main // expected-error{{referencing static method 'main()' on 'Runnable' requires that 'EntryPoint' conform to 'OtherThing'}} +@main //expected-error{{'EntryPoint' is annotated with @main and must provide a main static function}} struct EntryPoint : Runnable { func run() { } From 17b4b5deeb2acffe47525910d31381ad892507fe Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 27 Apr 2022 13:09:42 -0700 Subject: [PATCH 75/80] Update disjunction constraints This sets up the type constraints as equalities with a type variable. This is apparently more performant in the solver, so that should be good. It doesn't have additional effect on the resulting behavior. --- lib/Sema/TypeCheckAttr.cpp | 43 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 8dab8184d3386..2e50d65452457 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2148,10 +2148,11 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, // usual type-checking. The alternative would be to directly call // mainType.main() from the entry point, and that would require fully // type-checking the call to mainType.main(). - - constraints::ConstraintSystem CS( - declContext, constraints::ConstraintSystemFlags::IgnoreAsyncSyncMismatch); - constraints::ConstraintLocator *locator = CS.getConstraintLocator({}); + using namespace constraints; + ConstraintSystem CS(declContext, + ConstraintSystemFlags::IgnoreAsyncSyncMismatch); + ConstraintLocator *locator = + CS.getConstraintLocator({}, ConstraintLocator::Member); // Allowed main function types // `() -> Void` // `() async -> Void` @@ -2196,31 +2197,35 @@ SynthesizeMainFunctionRequest::evaluate(Evaluator &evaluator, .withGlobalActor(mainActor) .build())); } - - llvm::SmallVector mainTypeConstraints; - for (const Type &mainType : mainTypes) { - constraints::Constraint *fnConstraint = - constraints::Constraint::createMember( - CS, constraints::ConstraintKind::ValueMember, - nominal->getInterfaceType(), mainType, - DeclNameRef(context.Id_main), declContext, - FunctionRefKind::SingleApply, locator); - mainTypeConstraints.push_back(fnConstraint); + TypeVariableType *mainType = + CS.createTypeVariable(locator, /*options=*/0); + llvm::SmallVector typeEqualityConstraints; + typeEqualityConstraints.reserve(mainTypes.size()); + for (const Type &candidateMainType : mainTypes) { + typeEqualityConstraints.push_back( + Constraint::create(CS, ConstraintKind::Equal, Type(mainType), + candidateMainType, locator)); } - CS.addDisjunctionConstraint(mainTypeConstraints, locator); + CS.addDisjunctionConstraint(typeEqualityConstraints, locator); + CS.addValueMemberConstraint( + nominal->getInterfaceType(), DeclNameRef(context.Id_main), + Type(mainType), declContext, FunctionRefKind::SingleApply, {}, locator); } FuncDecl *mainFunction = nullptr; - llvm::SmallVector candidates; + llvm::SmallVector candidates; if (!CS.solve(candidates, FreeTypeVariableBinding::Disallow)) { + // We can't use CS.diagnoseAmbiguity directly since the locator is empty + // Sticking the main type decl `D` in results in an assert due to a + // unsimplifiable locator anchor since it appears to be looking for an + // expression, which we don't have. + // (locator could not be simplified to anchor) + // TODO: emit notes for each of the ambiguous candidates if (candidates.size() != 1) { context.Diags.diagnose(nominal->getLoc(), diag::ambiguous_decl_ref, DeclNameRef(context.Id_main)); - // TODO: CS.diagnoseAmbiguity doesn't report anything because the types - // are different. It would be good to get notes on the decls causing the - // ambiguity. attr->setInvalid(); return nullptr; } From 862dfa9774aff74d32e36ada8aa0c69456c1b137 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 28 Apr 2022 12:53:06 -0400 Subject: [PATCH 76/80] [Test] Disable objc_old_swift.swift on ARM64e. The bit twiddling done by this test falls afoul of ptrauth on ARM64e. We don't support pre-stable Swift ABI code on ARM64e anyway, so just disable the test there. rdar://92469961 (cherry picked from commit a0dec1d30aaea41f0cc97722eebea627ed70861c) --- test/Interpreter/SDK/objc_old_swift.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/Interpreter/SDK/objc_old_swift.swift b/test/Interpreter/SDK/objc_old_swift.swift index 76c9a8900ff4d..4b9250eb1121a 100644 --- a/test/Interpreter/SDK/objc_old_swift.swift +++ b/test/Interpreter/SDK/objc_old_swift.swift @@ -9,6 +9,11 @@ // REQUIRES: executable_test // REQUIRES: objc_interop +// The bit twiddling done by this test falls afoul of ptrauth on ARM64e. We +// don't support pre-stable Swift ABI code on ARM64e anyway, so just disable the +// test there. +// UNSUPPORTED: CPU=arm64e + // Verify that objects that appear to be from the pre-stable Swift ABI // are correctly ignored by stable Swift's entry points. From e49d2692ec478cac850aaf98473308ef4c33a1a6 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 26 Apr 2022 15:34:19 -0700 Subject: [PATCH 77/80] Sema: When diagnosing required explicit availability, skip members of extensions that have been declared explicitly unavailable. The existing logic only checked for an unavailable attr in the direct attributes of the decl. Resolves rdar://85429703 --- lib/Sema/TypeCheckAvailability.cpp | 7 ++-- test/attr/require_explicit_availability.swift | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 9ed2bbb135d12..90205c86671ce 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -3914,11 +3914,14 @@ static bool declNeedsExplicitAvailability(const Decl *decl) { decl->isImplicit()) return false; + // Skip unavailable decls. + if (AvailableAttr::isUnavailable(decl)) + return false; + // Warn on decls without an introduction version. auto &ctx = decl->getASTContext(); auto safeRangeUnderApprox = AvailabilityInference::availableRange(decl, ctx); - return !safeRangeUnderApprox.getOSVersion().hasLowerEndpoint() && - !decl->getAttrs().isUnavailable(ctx); + return !safeRangeUnderApprox.getOSVersion().hasLowerEndpoint(); } void swift::checkExplicitAvailability(Decl *decl) { diff --git a/test/attr/require_explicit_availability.swift b/test/attr/require_explicit_availability.swift index d45778728d79d..4d8d362791e0b 100644 --- a/test/attr/require_explicit_availability.swift +++ b/test/attr/require_explicit_availability.swift @@ -7,6 +7,11 @@ public struct S { // expected-warning {{public declarations should have an avail public func method() { } } +@available(macOS, unavailable) +public struct UnavailableStruct { + public func okMethod() { } +} + public func foo() { bar() } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} @usableFromInline @@ -41,10 +46,25 @@ public func +(lhs: S, rhs: S) -> S { } // expected-warning {{public declarations public enum E { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +@available(macOS, unavailable) +public enum UnavailableEnum { + case caseOk +} + public class C { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +@available(macOS, unavailable) +public class UnavailableClass { + public func okMethod() { } +} + public protocol P { } // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} +@available(macOS, unavailable) +public protocol UnavailableProto { + func requirementOk() +} + private protocol PrivateProto { } extension S { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} @@ -56,11 +76,19 @@ extension S { public func okWhenTheExtensionHasAttribute() { } } +@available(macOS, unavailable) +extension S { + public func okWhenTheExtensionIsUnavailable() { } +} + extension S { internal func dontWarnWithoutPublicMembers() { } private func dontWarnWithoutPublicMembers1() { } } +// An empty extension should be ok. +extension S { } + extension S : P { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} } @@ -96,6 +124,9 @@ public var publicVar = S() // expected-warning {{public declarations should have @available(macOS 10.10, *) public var publicVarOk = S() +@available(macOS, unavailable) +public var unavailablePublicVarOk = S() + public var (a, b) = (S(), S()) // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} @available(macOS 10.10, *) @@ -112,6 +143,11 @@ public var implicitGetOk: S { return S() } +@available(macOS, unavailable) +public var unavailableImplicitGetOk: S { + return S() +} + public var computed: S { // expected-warning {{public declarations should have an availability attribute when building with -require-explicit-availability}} {{1-1=@available(macOS 10.10, *)\n}} get { return S() } set { } From 278edd00e5b572d6523d9eb38d917e9df03fc840 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 28 Apr 2022 18:04:38 -0400 Subject: [PATCH 78/80] [RemoteMirror] Add bounds checking to BitMask operations. If we encounter any multi-paylaod enum descriptors with bad data, we can end up writing off the end of the bitmask allocation, causing heap corruption. Add range checks to let us fail gracefully instead. rdar://91423283 (cherry picked from commit a6de05b298278f5d3cc6e6f4b98994a458c28f47) --- stdlib/public/Reflection/TypeLowering.cpp | 79 ++++++++++++++++++++--- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/stdlib/public/Reflection/TypeLowering.cpp b/stdlib/public/Reflection/TypeLowering.cpp index ba6093a0f0b87..742a398300c39 100644 --- a/stdlib/public/Reflection/TypeLowering.cpp +++ b/stdlib/public/Reflection/TypeLowering.cpp @@ -20,6 +20,7 @@ #if SWIFT_ENABLE_REFLECTION +#include "llvm/Support/MathExtras.h" #include "swift/ABI/Enum.h" #include "swift/ABI/MetadataValues.h" #include "swift/Reflection/TypeLowering.h" @@ -645,6 +646,8 @@ class SimpleMultiPayloadEnumTypeInfo: public EnumTypeInfo { // A variable-length bitmap used to track "spare bits" for general multi-payload // enums. class BitMask { + static constexpr unsigned maxSize = 128 * 1024 * 1024; // 128MB + unsigned size; // Size of mask in bytes uint8_t *mask; public: @@ -654,18 +657,72 @@ class BitMask { // Construct a bitmask of the appropriate number of bytes // initialized to all bits set BitMask(unsigned sizeInBytes): size(sizeInBytes) { - assert(sizeInBytes < std::numeric_limits::max()); + // Gracefully fail by constructing an empty mask if we exceed the size + // limit. + if (size > maxSize) { + size = 0; + mask = nullptr; + return; + } + mask = (uint8_t *)malloc(size); + + if (!mask) { + // Malloc might fail if size is large due to some bad data. Assert in + // asserts builds, and fail gracefully in non-asserts builds by + // constructing an empty BitMask. + assert(false && "Failed to allocate BitMask"); + size = 0; + return; + } + memset(mask, 0xff, size); } // Construct a bitmask of the appropriate number of bytes // initialized with bits from the specified buffer - BitMask(unsigned sizeInBytes, const uint8_t *initialValue, unsigned initialValueBytes, unsigned offset) - : size(sizeInBytes) - { - assert(sizeInBytes < std::numeric_limits::max()); - assert(initialValueBytes + offset <= sizeInBytes); + BitMask(unsigned sizeInBytes, const uint8_t *initialValue, + unsigned initialValueBytes, unsigned offset) + : size(sizeInBytes) { + // Gracefully fail by constructing an empty mask if we exceed the size + // limit. + if (size > maxSize) { + size = 0; + mask = nullptr; + return; + } + + // Bad data could cause the initial value location to be off the end of our + // size. If initialValueBytes + offset is beyond sizeInBytes (or overflows), + // assert in asserts builds, and fail gracefully in non-asserts builds by + // constructing an empty BitMask. + bool overflowed = false; + unsigned initialValueEnd = + llvm::SaturatingAdd(initialValueBytes, offset, &overflowed); + if (overflowed) { + assert(false && "initialValueBytes + offset overflowed"); + size = 0; + mask = nullptr; + return; + } + assert(initialValueEnd <= sizeInBytes); + if (initialValueEnd > size) { + assert(false && "initialValueBytes + offset is greater than size"); + size = 0; + mask = nullptr; + return; + } + mask = (uint8_t *)calloc(1, size); + + if (!mask) { + // Malloc might fail if size is large due to some bad data. Assert in + // asserts builds, and fail gracefully in non-asserts builds by + // constructing an empty BitMask. + assert(false && "Failed to allocate BitMask"); + size = 0; + return; + } + memcpy(mask + offset, initialValue, initialValueBytes); } // Move constructor moves ownership and zeros the src @@ -864,10 +921,12 @@ class BitMask { void andNotMask(void *maskData, unsigned len, unsigned offset) { assert(offset < size); - unsigned common = std::min(len, size - offset); - uint8_t *maskBytes = (uint8_t *)maskData; - for (unsigned i = 0; i < common; ++i) { - mask[i + offset] &= ~maskBytes[i]; + if (offset < size) { + unsigned common = std::min(len, size - offset); + uint8_t *maskBytes = (uint8_t *)maskData; + for (unsigned i = 0; i < common; ++i) { + mask[i + offset] &= ~maskBytes[i]; + } } } }; From 38222d59253a0f2e30c7a4abc1147d3182f65ecd Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 20 Apr 2022 08:43:43 +0000 Subject: [PATCH 79/80] [Wasm] Disable crash tests in test/stdlib/UnsafeRawPointer.swift --- test/stdlib/UnsafeRawPointer.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/stdlib/UnsafeRawPointer.swift b/test/stdlib/UnsafeRawPointer.swift index d450335fa4b6a..5223bc373b369 100644 --- a/test/stdlib/UnsafeRawPointer.swift +++ b/test/stdlib/UnsafeRawPointer.swift @@ -114,6 +114,7 @@ UnsafeMutableRawPointerExtraTestSuite.test("load.unaligned") expectEqual(result, 0xffff_0000) } +#if !os(WASI) UnsafeMutableRawPointerExtraTestSuite.test("load.invalid") .skip(.custom({ !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) @@ -137,6 +138,7 @@ UnsafeMutableRawPointerExtraTestSuite.test("load.invalid.mutable") } expectUnreachable() } +#endif UnsafeMutableRawPointerExtraTestSuite.test("store.unaligned") .skip(.custom({ @@ -167,6 +169,7 @@ UnsafeMutableRawPointerExtraTestSuite.test("store.unaligned") 0) } +#if !os(WASI) UnsafeMutableRawPointerExtraTestSuite.test("store.invalid") .skip(.custom({ !_isDebugAssertConfiguration() }, reason: "This tests a debug precondition..")) @@ -186,6 +189,7 @@ UnsafeMutableRawPointerExtraTestSuite.test("store.invalid") p1.storeBytes(of: m, as: Missile.self) expectUnreachable() } +#endif UnsafeMutableRawPointerExtraTestSuite.test("copyMemory") { let sizeInBytes = 4 * MemoryLayout.stride From 72875f0dc3dd6284be6866b6375a0fac232f8d4a Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 11 Apr 2022 13:25:19 +0000 Subject: [PATCH 80/80] [Wasm] Disable crash tests in test/stdlib/StringAPICString.swift --- test/stdlib/StringAPICString.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/stdlib/StringAPICString.swift b/test/stdlib/StringAPICString.swift index 1563289cb8dfe..ede6086c8ed14 100644 --- a/test/stdlib/StringAPICString.swift +++ b/test/stdlib/StringAPICString.swift @@ -7,6 +7,12 @@ import StdlibUnittest +#if arch(wasm32) +let enableCrashTests = false +#else +let enableCrashTests = true +#endif + var CStringTests = TestSuite("CStringTests") func getNullUTF8() -> UnsafeMutablePointer? { @@ -227,6 +233,7 @@ CStringTests.test("Substring.withCString") { CStringTests.test("String.cString.with.Array.UInt8.input") { guard #available(SwiftStdlib 5.7, *) else { return } + do { let (u8p, dealloc) = getASCIIUTF8() defer { dealloc() } @@ -239,6 +246,7 @@ CStringTests.test("String.cString.with.Array.UInt8.input") { } } } + guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) @@ -263,6 +271,7 @@ CStringTests.test("String.cString.with.Array.CChar.input") { } } } + guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) @@ -292,6 +301,7 @@ CStringTests.test("String.cString.with.inout.UInt8.conversion") { var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 + guard enableCrashTests else { return } expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) // withMessage: "input of String.init(cString:) must be null-terminated" @@ -306,6 +316,7 @@ CStringTests.test("String.cString.with.inout.CChar.conversion") { var str = String(cString: &c) expectTrue(str.isEmpty) c = 100 + guard enableCrashTests else { return } expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) // withMessage: "input of String.init(cString:) must be null-terminated" @@ -330,6 +341,7 @@ CStringTests.test("String.validatingUTF8.with.Array.input") { } } } + guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) @@ -362,6 +374,7 @@ CStringTests.test("String.validatingUTF8.with.inout.conversion") { expectNotNil(str) expectEqual(str?.isEmpty, true) c = 100 + guard enableCrashTests else { return } expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) // withMessage: "input of String.init(validatingUTF8:) must be null-terminated" @@ -387,6 +400,7 @@ CStringTests.test("String.decodeCString.with.Array.input") { } } } + guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) @@ -426,6 +440,7 @@ CStringTests.test("String.decodeCString.with.inout.conversion") { expectEqual(result?.result.isEmpty, true) expectEqual(result?.repairsMade, false) c = 100 + guard enableCrashTests else { return } expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) // withMessage: "input of decodeCString(_:as:repairingInvalidCodeUnits:) must be null-terminated" @@ -449,6 +464,7 @@ CStringTests.test("String.init.decodingCString.with.Array.input") { } } } + guard enableCrashTests else { return } // no need to test every case; that is covered in other tests expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) @@ -478,6 +494,7 @@ CStringTests.test("String.init.decodingCString.with.inout.conversion") { var str = String(decodingCString: &c, as: Unicode.UTF8.self) expectEqual(str.isEmpty, true) c = 100 + guard enableCrashTests else { return } expectCrashLater( // Workaround for https://bugs.swift.org/browse/SR-16103 (rdar://91365967) // withMessage: "input of String.init(decodingCString:as:) must be null-terminated"