diff --git a/include/swift/ABI/Executor.h b/include/swift/ABI/Executor.h index 88c0a39796f8a..47274269211c1 100644 --- a/include/swift/ABI/Executor.h +++ b/include/swift/ABI/Executor.h @@ -124,6 +124,9 @@ class ExecutorRef { /// Is this executor the main executor? bool isMainExecutor() const; + /// Get the raw value of the Implementation field, for tracing. + uintptr_t getRawImplementation() { return Implementation; } + bool operator==(ExecutorRef other) const { return Identity == other.Identity; } diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h index f5425c73c7a4f..771234af79fc1 100644 --- a/include/swift/ABI/Task.h +++ b/include/swift/ABI/Task.h @@ -272,6 +272,14 @@ class AsyncTask : public Job { /// Set the task's ID field to the next task ID. void setTaskId(); + uint64_t getTaskId(); + + /// Get the task's resume function, for logging purposes only. This will + /// attempt to see through the various adapters that are sometimes used, and + /// failing that will return ResumeTask. The returned function pointer may + /// have a different signature than ResumeTask, and it's only for identifying + /// code associated with the task. + const void *getResumeFunctionForLogging(); /// Given that we've already fully established the job context /// in the current thread, start running this task. To establish diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h index 9c75247f98b37..6e8f87680bbcc 100644 --- a/include/swift/ABI/TaskStatus.h +++ b/include/swift/ABI/TaskStatus.h @@ -35,7 +35,7 @@ namespace swift { /// access by a cancelling thread. In particular, the chain of /// status records must not be disturbed. When the task leaves /// the scope that requires the status record, the record can -/// be unregistered from the task with `swift_task_removeStatusRecord`, +/// be unregistered from the task with `removeStatusRecord`, /// at which point the memory can be returned to the system. class TaskStatusRecord { public: @@ -256,7 +256,7 @@ class TaskGroupTaskStatusRecord : public TaskStatusRecord { /// /// The end of any call to the function will be ordered before the /// end of a call to unregister this record from the task. That is, -/// code may call `swift_task_removeStatusRecord` and freely +/// code may call `removeStatusRecord` and freely /// assume after it returns that this function will not be /// subsequently used. class CancellationNotificationStatusRecord : public TaskStatusRecord { @@ -284,7 +284,7 @@ class CancellationNotificationStatusRecord : public TaskStatusRecord { /// /// The end of any call to the function will be ordered before the /// end of a call to unregister this record from the task. That is, -/// code may call `swift_task_removeStatusRecord` and freely +/// code may call `removeStatusRecord` and freely /// assume after it returns that this function will not be /// subsequently used. class EscalationNotificationStatusRecord : public TaskStatusRecord { diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index d1942205756db..6a5bc49bc695d 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -251,8 +251,11 @@ ERROR(enum_case_dot_prefix,none, ERROR(static_var_decl_global_scope,none, "%select{%error|static properties|class properties}0 may only be declared on a type", (StaticSpellingKind)) -ERROR(computed_property_no_accessors, none, - "%select{computed property|subscript}0 must have accessors specified", (bool)) +ERROR(unexpected_curly_braces_in_decl, none, + "unexpected '{' in declaration", ()) +ERROR(missing_accessor_return_decl,none, + "missing return in %select{accessor|subscript}0 expected to return %1", + (bool, TypeRepr*)) ERROR(expected_getset_in_protocol,none, "expected get or set in a protocol property", ()) ERROR(unexpected_getset_implementation_in_protocol,none, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index bc42105b4ac18..63dbe7f1ea7d8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1108,7 +1108,7 @@ WARNING(conditional_downcast_same_type,none, "bridging conversion; did you mean to use 'as'?}2", (Type, Type, unsigned)) WARNING(is_expr_same_type,none, - "checking a value with optional type %0 against dynamic type %1 " + "checking a value with optional type %0 against type %1 " "succeeds whenever the value is non-nil; did you mean to use " "'!= nil'?", (Type, Type)) WARNING(downcast_to_unrelated,none, diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index d53cb996b808c..78499df4b11ce 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -128,6 +128,9 @@ struct PrintOptions { /// Whether to print function definitions. bool FunctionDefinitions = false; + /// Whether to print expressions. + bool PrintExprs = false; + /// Whether to print '{ get set }' on readwrite computed properties. bool PrintGetSetOnRWProperties = true; @@ -644,6 +647,13 @@ struct PrintOptions { /// /// This is only intended for debug output. static PrintOptions printEverything() { + PrintOptions result = printDeclarations(); + result.FunctionDefinitions = true; + result.PrintExprs = true; + return result; + } + + static PrintOptions printDeclarations() { PrintOptions result = printVerbose(); result.ExcludeAttrList.clear(); result.ExcludeAttrList.push_back(DAK_FixedLayout); diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index ea748d6550679..5078b58ab8ed5 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -118,6 +118,7 @@ class FrontendOptions { EmitSyntax, ///< Parse and dump Syntax tree as JSON DumpAST, ///< Parse, type-check, and dump AST PrintAST, ///< Parse, type-check, and pretty-print AST + PrintASTDecl, ///< Parse, type-check, and pretty-print AST declarations /// Parse and dump scope map. DumpScopeMaps, diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 5df786fd2d044..a4a80428ac67b 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1051,6 +1051,10 @@ def print_ast : Flag<["-"], "print-ast">, HelpText<"Parse and type-check input file(s) and pretty print AST(s)">, ModeOpt, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; +def print_ast_decl : Flag<["-"], "print-ast-decl">, + HelpText<"Parse and type-check input file(s) and pretty print declarations from AST(s)">, + ModeOpt, + Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild]>; def dump_pcm : Flag<["-"], "dump-pcm">, HelpText<"Dump debugging information about a precompiled Clang module">, ModeOpt, diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index def788321011a..2dc026a887974 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1169,10 +1169,9 @@ class Parser { struct ParsedAccessors; ParserStatus parseGetSet(ParseDeclOptions Flags, GenericParamList *GenericParams, - ParameterList *Indices, + ParameterList *Indices, TypeRepr *ResultType, ParsedAccessors &accessors, - AbstractStorageDecl *storage, - SourceLoc StaticLoc); + AbstractStorageDecl *storage, SourceLoc StaticLoc); ParserResult parseDeclVarGetSet(PatternBindingEntry &entry, ParseDeclOptions Flags, SourceLoc StaticLoc, diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index c6bd1af5763f3..173e2e246e7bc 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -17,9 +17,9 @@ #ifndef SWIFT_RUNTIME_CONCURRENCY_H #define SWIFT_RUNTIME_CONCURRENCY_H +#include "swift/ABI/AsyncLet.h" #include "swift/ABI/Task.h" #include "swift/ABI/TaskGroup.h" -#include "swift/ABI/AsyncLet.h" #include "swift/ABI/TaskStatus.h" #pragma clang diagnostic push @@ -466,40 +466,6 @@ void swift_asyncLet_consume_throwing(SWIFT_ASYNC_CONTEXT AsyncContext *, SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) bool swift_taskGroup_hasTaskGroupRecord(); -/// Add a status record to a task. The record should not be -/// modified while it is registered with a task. -/// -/// This must be called synchronously with the task. -/// -/// If the task is already cancelled, returns `false` but still adds -/// the status record. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -bool swift_task_addStatusRecord(TaskStatusRecord *record); - -/// Add a status record to a task if the task has not already -/// been cancelled. The record should not be modified while it is -/// registered with a task. -/// -/// This must be called synchronously with the task. -/// -/// If the task is already cancelled, returns `false` and does not -/// add the status record. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -bool swift_task_tryAddStatusRecord(TaskStatusRecord *record); - -/// Remove a status record from a task. After this call returns, -/// the record's memory can be freely modified or deallocated. -/// -/// This must be called synchronously with the task. The record must -/// be registered with the task or else this may crash. -/// -/// The given record need not be the last record added to -/// the task, but the operation may be less efficient if not. -/// -/// Returns false if the task has been cancelled. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -bool swift_task_removeStatusRecord(TaskStatusRecord *record); - /// Signifies whether the current task is in the middle of executing the /// operation block of a `with(Throwing)TaskGroup(...) { }`. /// @@ -509,18 +475,6 @@ bool swift_task_removeStatusRecord(TaskStatusRecord *record); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) bool swift_task_hasTaskGroupStatusRecord(); -/// Attach a child task to its parent task and return the newly created -/// `ChildTaskStatusRecord`. -/// -/// The record must be removed with by the parent invoking -/// `swift_task_detachChild` when the child has completed. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -ChildTaskStatusRecord* swift_task_attachChild(AsyncTask *child); - -/// Remove a child task from the parent tracking it. -SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_task_detachChild(ChildTaskStatusRecord *record); - SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) size_t swift_task_getJobFlags(AsyncTask* task); diff --git a/lib/APIDigester/ModuleAnalyzerNodes.cpp b/lib/APIDigester/ModuleAnalyzerNodes.cpp index 0924dbd2aca48..e90da097f4313 100644 --- a/lib/APIDigester/ModuleAnalyzerNodes.cpp +++ b/lib/APIDigester/ModuleAnalyzerNodes.cpp @@ -1906,10 +1906,11 @@ void SwiftDeclCollector::lookupVisibleDecls(ArrayRef Modules) { for (auto *D: KnownDecls) { if (auto *Ext = dyn_cast(D)) { if (HandledExtensions.find(Ext) == HandledExtensions.end()) { - auto *NTD = Ext->getExtendedNominal(); - // Check if the extension is from other modules. - if (!llvm::is_contained(Modules, NTD->getModuleContext())) { - ExtensionMap[NTD].push_back(Ext); + if (auto *NTD = Ext->getExtendedNominal()) { + // Check if the extension is from other modules. + if (!llvm::is_contained(Modules, NTD->getModuleContext())) { + ExtensionMap[NTD].push_back(Ext); + } } } } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 42d1e81a4ad9e..84982ee54c39a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -389,6 +389,7 @@ struct ASTContext::Implementation { llvm::FoldingSet TypeAliasTypes; llvm::FoldingSet TupleTypes; llvm::FoldingSet PackTypes; + llvm::FoldingSet PackExpansionTypes; llvm::DenseMap, MetatypeType*> MetatypeTypes; llvm::DenseMap, @@ -471,7 +472,6 @@ struct ASTContext::Implementation { llvm::FoldingSet SILBoxTypes; llvm::DenseMap IntegerTypes; llvm::FoldingSet BuiltinVectorTypes; - llvm::FoldingSet PackExpansionTypes; llvm::FoldingSet CompoundNames; llvm::DenseMap OpenedExistentialArchetypes; llvm::FoldingSet IndexSubsets; @@ -2960,20 +2960,25 @@ Type TupleTypeElt::getType() const { PackExpansionType *PackExpansionType::get(Type patternTy) { assert(patternTy && "Missing pattern type in expansion"); + auto properties = patternTy->getRecursiveProperties(); + auto arena = getArena(properties); + auto &context = patternTy->getASTContext(); llvm::FoldingSetNodeID id; PackExpansionType::Profile(id, patternTy); void *insertPos; if (PackExpansionType *expType = - context.getImpl().PackExpansionTypes.FindNodeOrInsertPos(id, - insertPos)) + context.getImpl() + .getArena(arena) + .PackExpansionTypes.FindNodeOrInsertPos(id, insertPos)) return expType; const ASTContext *canCtx = patternTy->isCanonical() ? &context : nullptr; PackExpansionType *expansionTy = new (context, AllocationArena::Permanent) PackExpansionType(patternTy, canCtx); - context.getImpl().PackExpansionTypes.InsertNode(expansionTy, insertPos); + context.getImpl().getArena(arena).PackExpansionTypes.InsertNode(expansionTy, + insertPos); return expansionTy; } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index f1b184cdb1807..a0dc3450413e3 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -908,6 +908,10 @@ class PrintAST : public ASTVisitor { /// to match the original function declaration. void printFunctionParameters(AbstractFunctionDecl *AFD); + void printArgument(const Argument &arg); + + void printStmtCondition(StmtCondition stmt); + #define DECL(Name,Parent) void visit##Name##Decl(Name##Decl *decl); #define ABSTRACT_DECL(Name, Parent) #define DECL_RANGE(Name,Start,End) @@ -916,6 +920,11 @@ class PrintAST : public ASTVisitor { #define STMT(Name, Parent) void visit##Name##Stmt(Name##Stmt *stmt); #include "swift/AST/StmtNodes.def" +#define EXPR(Name,Parent) void visit##Name##Expr(Name##Expr *expr); +#define ABSTRACT_EXPR(Name, Parent) +#define DECL_RANGE(Name,Start,End) +#include "swift/AST/ExprNodes.def" + void printSynthesizedExtension(Type ExtendedType, ExtensionDecl *ExtDecl); void printExtension(ExtensionDecl* ExtDecl); @@ -947,6 +956,14 @@ class PrintAST : public ASTVisitor { using ASTVisitor::visit; + bool visit(Expr *E) { + if (!Options.PrintExprs) { + return false; + } + ASTVisitor::visit(E); + return true; + } + bool visit(Decl *D) { #if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB return false; // not needed for the parser library. @@ -1291,7 +1308,7 @@ void PrintAST::printPattern(const Pattern *pattern) { case PatternKind::EnumElement: { auto elt = cast(pattern); - // FIXME: Print element expr. + Printer << "." << elt->getElementDecl()->getBaseName(); if (elt->hasSubPattern()) printPattern(elt->getSubPattern()); break; @@ -1307,15 +1324,19 @@ void PrintAST::printPattern(const Pattern *pattern) { : tok::kw_false); break; - case PatternKind::Expr: - // FIXME: Print expr. + case PatternKind::Expr: { + auto expr = cast(pattern)->getSubExpr(); + visit(expr); break; + } - case PatternKind::Binding: + case PatternKind::Binding: { + auto bPattern = cast(pattern); Printer.printIntroducerKeyword( - cast(pattern)->isLet() ? "let" : "var", + bPattern->isLet() ? "let" : "var", Options, " "); - printPattern(cast(pattern)->getSubPattern()); + printPattern(bPattern->getSubPattern()); + } } } @@ -3050,7 +3071,12 @@ void PrintAST::visitPatternBindingDecl(PatternBindingDecl *decl) { printPatternType(pattern); } - if (Options.VarInitializers) { + if (Options.PrintExprs) { + if (auto initExpr = decl->getInit(idx)) { + Printer << " = "; + visit(initExpr); + } + } else if (Options.VarInitializers) { auto *vd = decl->getAnchoringVarDecl(idx); if (decl->hasInitStringRepresentation(idx) && vd->isInitExposedToClients()) { @@ -3579,8 +3605,7 @@ bool PrintAST::printASTNodes(const ArrayRef &Elements, } else if (auto stmt = element.dyn_cast()) { visit(stmt); } else { - // FIXME: print expression - // visit(element.get()); + visit(element.get()); } } return PrintedSomething; @@ -4047,6 +4072,473 @@ void PrintAST::visitMissingMemberDecl(MissingMemberDecl *decl) { Printer << " */"; } +void PrintAST::visitIntegerLiteralExpr(IntegerLiteralExpr *expr) { + Printer << expr->getDigitsText(); +} + +void PrintAST::visitFloatLiteralExpr(FloatLiteralExpr *expr) { + Printer << expr->getDigitsText(); +} + +void PrintAST::visitNilLiteralExpr(NilLiteralExpr *expr) { + Printer << "nil"; +} + +void PrintAST::visitStringLiteralExpr(StringLiteralExpr *expr) { + Printer << "\"" << expr->getValue() << "\""; +} + +void PrintAST::visitBooleanLiteralExpr(BooleanLiteralExpr *expr) { + if (expr->getValue()) { + Printer << "true"; + } else { + Printer << "false"; + } +} + +void PrintAST::visitRegexLiteralExpr(RegexLiteralExpr *expr) { + Printer << expr->getRegexText(); +} + +void PrintAST::visitErrorExpr(ErrorExpr *expr) { + Printer << ""; +} + +void PrintAST::visitIfExpr(IfExpr *expr) { +} + +void PrintAST::visitIsExpr(IsExpr *expr) { +} + +void PrintAST::visitTapExpr(TapExpr *expr) { +} + +void PrintAST::visitTryExpr(TryExpr *expr) { + Printer << "try "; + visit(expr->getSubExpr()); +} + +void PrintAST::visitCallExpr(CallExpr *expr) { + visit(expr->getFn()); + Printer << "("; + auto args = expr->getArgs()->getOriginalArgs(); + bool isFirst = true; + // FIXME: handle trailing closures. + for (auto arg : *args) { + if (!isFirst) { + Printer << ", "; + } + printArgument(arg); + isFirst = false; + } + Printer << ")"; +} + +void PrintAST::printArgument(const Argument &arg) { + auto label = arg.getLabel(); + if (!label.empty()) { + Printer << label.str(); + Printer << ": "; + } + if (arg.isInOut()) { + Printer << "&"; + } + visit(arg.getExpr()); +} + +void PrintAST::visitLoadExpr(LoadExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitTypeExpr(TypeExpr *expr) { + printType(expr->getType()); +} + +void PrintAST::visitArrayExpr(ArrayExpr *expr) { + Printer << "["; + bool isFirst = true; + auto elements = expr->getElements(); + for (auto element : elements) { + if (!isFirst) { + Printer << ", "; + } + visit(element); + isFirst = false; + } + Printer << "]"; +} + +void PrintAST::visitDictionaryExpr(DictionaryExpr *expr) { + Printer << "["; + bool isFirst = true; + auto elements = expr->getElements(); + for (auto element : elements) { + auto *tupleExpr = cast(element); + if (!isFirst) { + Printer << ", "; + } + visit(tupleExpr->getElement(0)); + Printer << ": "; + visit(tupleExpr->getElement(1)); + isFirst = false; + } + Printer << "]"; +} + +void PrintAST::visitArrowExpr(ArrowExpr *expr) { +} + +void PrintAST::visitAwaitExpr(AwaitExpr *expr) { + Printer << "await "; + visit(expr->getSubExpr()); +} + +void PrintAST::visitInOutExpr(InOutExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitParenExpr(ParenExpr *expr) { + Printer << "("; + visit(expr->getSubExpr()); + Printer << ")"; +} + +void PrintAST::visitTupleExpr(TupleExpr *expr) { + Printer << "("; + bool isFirst = true; + auto elements = expr->getElements(); + for (auto element : elements) { + if (!isFirst) { + Printer << ", "; + } + visit(element); + isFirst = false; + } + Printer << ")"; +} + +void PrintAST::visitPackExpr(PackExpr *expr) { +} + +void PrintAST::visitReifyPackExpr(ReifyPackExpr *expr) { +} + +void PrintAST::visitAssignExpr(AssignExpr *expr) { + visit(expr->getDest()); + Printer << " = "; + visit(expr->getSrc()); +} + +void PrintAST::visitBinaryExpr(BinaryExpr *expr) { + visit(expr->getLHS()); + Printer << " "; + if (auto operatorRef = expr->getFn()->getMemberOperatorRef()) { + Printer << operatorRef->getDecl()->getBaseName(); + } + Printer << " "; + visit(expr->getRHS()); +} + +void PrintAST::visitCoerceExpr(CoerceExpr *expr) { +} + +void PrintAST::visitOneWayExpr(OneWayExpr *expr) { +} + +void PrintAST::visitClosureExpr(ClosureExpr *expr) { +} + +void PrintAST::visitDeclRefExpr(DeclRefExpr *expr) { + Printer << expr->getDecl()->getBaseName(); +} + +void PrintAST::visitDotSelfExpr(DotSelfExpr *expr) { + visit(expr->getSubExpr()); + Printer << ".self"; +} + +void PrintAST::visitErasureExpr(ErasureExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitKeyPathExpr(KeyPathExpr *expr) { +} + +void PrintAST::visitForceTryExpr(ForceTryExpr *expr) { + Printer << "try! "; + visit(expr->getSubExpr()); +} + +void PrintAST::visitSequenceExpr(SequenceExpr *expr) { +} + +void PrintAST::visitSuperRefExpr(SuperRefExpr *expr) { +} + +void PrintAST::visitMemberRefExpr(MemberRefExpr *expr) { + visit(expr->getBase()); + Printer << "."; + Printer << expr->getMember().getDecl()->getName(); +} + +void PrintAST::visitSubscriptExpr(SubscriptExpr *expr) { +} + +void PrintAST::visitEnumIsCaseExpr(EnumIsCaseExpr *expr) { +} + +void PrintAST::visitForceValueExpr(ForceValueExpr *expr) { +} + +void PrintAST::visitKeyPathDotExpr(KeyPathDotExpr *expr) { +} + +void PrintAST::visitAutoClosureExpr(AutoClosureExpr *expr) { + visit(expr->getSingleExpressionBody()); +} + +void PrintAST::visitCaptureListExpr(CaptureListExpr *expr) { +} + +void PrintAST::visitDynamicTypeExpr(DynamicTypeExpr *expr) { +} + +void PrintAST::visitOpaqueValueExpr(OpaqueValueExpr *expr) { +} + +void PrintAST::visitOptionalTryExpr(OptionalTryExpr *expr) { +} + +void PrintAST::visitPrefixUnaryExpr(PrefixUnaryExpr *expr) { +} + +void PrintAST::visitBindOptionalExpr(BindOptionalExpr *expr) { +} + +void PrintAST::visitBridgeToObjCExpr(BridgeToObjCExpr *expr) { +} + +void PrintAST::visitObjCSelectorExpr(ObjCSelectorExpr *expr) { +} + +void PrintAST::visitPostfixUnaryExpr(PostfixUnaryExpr *expr) { +} + +void PrintAST::visitTupleElementExpr(TupleElementExpr *expr) { +} + +void PrintAST::visitDerivedToBaseExpr(DerivedToBaseExpr *expr) { +} + +void PrintAST::visitDotSyntaxCallExpr(DotSyntaxCallExpr *expr) { + visit(expr->getBase()); + Printer << "."; + visit(expr->getFn()); +} + +void PrintAST::visitObjectLiteralExpr(ObjectLiteralExpr *expr) { +} + +void PrintAST::visitUnresolvedDotExpr(UnresolvedDotExpr *expr) { + visit(expr->getBase()); + Printer << "."; + Printer << expr->getName().getBaseName(); +} + +void PrintAST::visitArrayToPointerExpr(ArrayToPointerExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitBridgeFromObjCExpr(BridgeFromObjCExpr *expr) { +} + +void PrintAST::visitCodeCompletionExpr(CodeCompletionExpr *expr) { +} + +void PrintAST::visitInOutToPointerExpr(InOutToPointerExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitLinearFunctionExpr(LinearFunctionExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitDefaultArgumentExpr(DefaultArgumentExpr *expr) { +} + +void PrintAST::visitLazyInitializerExpr(LazyInitializerExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitOpenExistentialExpr(OpenExistentialExpr *expr) { + visit(expr->getExistentialValue()); + visit(expr->getSubExpr()); +} + +void PrintAST::visitStringToPointerExpr(StringToPointerExpr *expr) { +} + +void PrintAST::visitVarargExpansionExpr(VarargExpansionExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitArchetypeToSuperExpr(ArchetypeToSuperExpr *expr) { +} + +void PrintAST::visitDestructureTupleExpr(DestructureTupleExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) { +} + +void PrintAST::visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) { +} + +void PrintAST::visitPointerToPointerExpr(PointerToPointerExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { +} + +void PrintAST::visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) { + Printer << "_"; +} + +void PrintAST::visitEditorPlaceholderExpr(EditorPlaceholderExpr *expr) { +} + +void PrintAST::visitForcedCheckedCastExpr(ForcedCheckedCastExpr *expr) { + visit(expr->getSubExpr()); + Printer << " as! "; + printType(expr->getCastType()); +} + +void PrintAST::visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr) { + visit(expr->getSubExpr()); + Printer << " as? "; + printType(expr->getCastType()); +} + +void PrintAST::visitOverloadedDeclRefExpr(OverloadedDeclRefExpr *expr) { +} + +void PrintAST::visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) { +} + +void PrintAST::visitUnresolvedPatternExpr(UnresolvedPatternExpr *expr) { +} + +void PrintAST::visitAnyHashableErasureExpr(AnyHashableErasureExpr *expr) { +} + +void PrintAST::visitConstructorRefCallExpr(ConstructorRefCallExpr *expr) { + if (auto type = expr->getType()) { + if (auto *funcType = type->getAs()) { + printType(funcType->getResult()); + } + } +} + +void PrintAST::visitFunctionConversionExpr(FunctionConversionExpr *expr) { +} + +void PrintAST::visitInjectIntoOptionalExpr(InjectIntoOptionalExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr) { +} + +void PrintAST::visitMetatypeConversionExpr(MetatypeConversionExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitUnderlyingToOpaqueExpr(UnderlyingToOpaqueExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitUnevaluatedInstanceExpr(UnevaluatedInstanceExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *expr) { +} + +void PrintAST::visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitClassMetatypeToObjectExpr(ClassMetatypeToObjectExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) { +} + +void PrintAST::visitDifferentiableFunctionExpr(DifferentiableFunctionExpr *expr) { + visit(expr->getSubExpr()); +} + +void PrintAST::visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) { +} + +void PrintAST::visitForeignObjectConversionExpr(ForeignObjectConversionExpr *expr) { +} + +void PrintAST::visitOtherConstructorDeclRefExpr(OtherConstructorDeclRefExpr *expr) { +} + +void PrintAST::visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) { +} + +void PrintAST::visitMakeTemporarilyEscapableExpr(MakeTemporarilyEscapableExpr *expr) { +} + +void PrintAST::visitProtocolMetatypeToObjectExpr(ProtocolMetatypeToObjectExpr *expr) { +} + +void PrintAST::visitUnresolvedTypeConversionExpr(UnresolvedTypeConversionExpr *expr) { +} + +void PrintAST::visitConditionalBridgeFromObjCExpr(ConditionalBridgeFromObjCExpr *expr) { +} + +void PrintAST::visitCovariantReturnConversionExpr(CovariantReturnConversionExpr *expr) { +} + +void PrintAST::visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *expr) { +} + +void PrintAST::visitCollectionUpcastConversionExpr(CollectionUpcastConversionExpr *expr) { +} + +void PrintAST::visitCovariantFunctionConversionExpr(CovariantFunctionConversionExpr *expr) { +} + +void PrintAST::visitExistentialMetatypeToObjectExpr(ExistentialMetatypeToObjectExpr *expr) { +} + +void PrintAST::visitUnresolvedMemberChainResultExpr(swift::UnresolvedMemberChainResultExpr *expr) { +} + +void PrintAST::visitLinearFunctionExtractOriginalExpr(swift::LinearFunctionExtractOriginalExpr *expr) { +} + +void PrintAST::visitLinearToDifferentiableFunctionExpr(swift::LinearToDifferentiableFunctionExpr *expr) { +} + +void PrintAST::visitPropertyWrapperValuePlaceholderExpr(swift::PropertyWrapperValuePlaceholderExpr *expr) { +} + +void PrintAST::visitDifferentiableFunctionExtractOriginalExpr(swift::DifferentiableFunctionExtractOriginalExpr *expr) { +} + void PrintAST::visitBraceStmt(BraceStmt *stmt) { printBraceStmt(stmt); } @@ -4055,7 +4547,7 @@ void PrintAST::visitReturnStmt(ReturnStmt *stmt) { Printer << tok::kw_return; if (stmt->hasResult()) { Printer << " "; - // FIXME: print expression. + visit(stmt->getResult()); } } @@ -4080,7 +4572,7 @@ void PrintAST::visitYieldStmt(YieldStmt *stmt) { void PrintAST::visitThrowStmt(ThrowStmt *stmt) { Printer << tok::kw_throw << " "; - // FIXME: print expression. + visit(stmt->getSubExpr()); } void PrintAST::visitPoundAssertStmt(PoundAssertStmt *stmt) { @@ -4095,7 +4587,7 @@ void PrintAST::visitDeferStmt(DeferStmt *stmt) { void PrintAST::visitIfStmt(IfStmt *stmt) { Printer << tok::kw_if << " "; - // FIXME: print condition + printStmtCondition(stmt->getCond()); Printer << " "; visit(stmt->getThenStmt()); if (auto elseStmt = stmt->getElseStmt()) { @@ -4105,23 +4597,39 @@ void PrintAST::visitIfStmt(IfStmt *stmt) { } void PrintAST::visitGuardStmt(GuardStmt *stmt) { Printer << tok::kw_guard << " "; - // FIXME: print condition - Printer << " "; + printStmtCondition(stmt->getCond()); + Printer << " else "; visit(stmt->getBody()); } void PrintAST::visitWhileStmt(WhileStmt *stmt) { Printer << tok::kw_while << " "; - // FIXME: print condition + printStmtCondition(stmt->getCond()); Printer << " "; visit(stmt->getBody()); } void PrintAST::visitRepeatWhileStmt(RepeatWhileStmt *stmt) { - Printer << tok::kw_do << " "; + Printer << tok::kw_repeat << " "; visit(stmt->getBody()); Printer << " " << tok::kw_while << " "; - // FIXME: print condition + visit(stmt->getCond()); +} + +void PrintAST::printStmtCondition(StmtCondition stmt) { + for (auto elt : stmt) { + if (auto pattern = elt.getPatternOrNull()) { + printPattern(pattern); + auto initializer = elt.getInitializer(); + if (initializer) { + Printer << " = "; + visit(initializer); + } + } + else if (auto boolean = elt.getBooleanOrNull()) { + visit(boolean); + } + } } void PrintAST::visitDoStmt(DoStmt *stmt) { @@ -4160,16 +4668,16 @@ void PrintAST::visitFallthroughStmt(FallthroughStmt *stmt) { void PrintAST::visitSwitchStmt(SwitchStmt *stmt) { Printer << tok::kw_switch << " "; - // FIXME: print subject - Printer << "{"; + visit(stmt->getSubjectExpr()); + Printer << " {"; Printer.printNewline(); for (auto N : stmt->getRawCases()) { if (N.is()) visit(cast(N.get())); else visit(cast(N.get())); + Printer.printNewline(); } - Printer.printNewline(); indent(); Printer << "}"; } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 857da2175045d..7d8895e373afb 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -356,7 +356,7 @@ DeclAttributes::getSoftDeprecated(const ASTContext &ctx) const { void DeclAttributes::dump(const Decl *D) const { StreamPrinter P(llvm::errs()); - PrintOptions PO = PrintOptions::printEverything(); + PrintOptions PO = PrintOptions::printDeclarations(); print(P, PO, D); } diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 182bbc7dc0a3b..8bfa62a6c328f 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -851,8 +851,8 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal, // Look up the inherited conformance. ModuleDecl *module = entry->getDeclContext()->getParentModule(); - auto inheritedConformance = module->lookupConformance(superclassTy, - protocol); + auto inheritedConformance = module->lookupConformance( + superclassTy, protocol, /*allowMissing=*/true); // Form the inherited conformance. entry->Conformance = diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 8a8cf1d0a388c..691c3b7b48166 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -570,6 +570,10 @@ static bool isMainActor(Type type) { void swift::printClangDeclName(const clang::NamedDecl *ND, llvm::raw_ostream &os) { +#if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. +#endif + ND->getNameForDiagnostic(os, ND->getASTContext().getPrintingPolicy(), false); } diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 5ebc241db807d..fd9ed704cdca5 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1287,7 +1287,8 @@ LookupConformanceInModuleRequest::evaluate( auto superclassTy = type->getSuperclassForDecl(conformingClass); // Compute the conformance for the inherited type. - auto inheritedConformance = mod->lookupConformance(superclassTy, protocol); + auto inheritedConformance = mod->lookupConformance( + superclassTy, protocol, /*allowMissing=*/true); assert(inheritedConformance && "We already found the inherited conformance"); diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 90a5b5bcd2a6f..b69ad7e230506 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -478,6 +478,8 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) { return FrontendOptions::ActionType::DumpTypeInfo; if (Opt.matches(OPT_print_ast)) return FrontendOptions::ActionType::PrintAST; + if (Opt.matches(OPT_print_ast_decl)) + return FrontendOptions::ActionType::PrintASTDecl; if (Opt.matches(OPT_emit_pcm)) return FrontendOptions::ActionType::EmitPCM; if (Opt.matches(OPT_dump_pcm)) diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 61d351a48a460..9b6e9adad3c88 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -39,6 +39,7 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { case ActionType::EmitSyntax: case ActionType::DumpInterfaceHash: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpPCM: @@ -109,6 +110,7 @@ bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { case ActionType::Typecheck: case ActionType::DumpAST: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::EmitSILGen: @@ -154,6 +156,7 @@ bool FrontendOptions::doesActionRequireInputs(ActionType action) { case ActionType::Typecheck: case ActionType::DumpAST: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::EmitSILGen: @@ -196,6 +199,7 @@ bool FrontendOptions::doesActionPerformEndOfPipelineActions(ActionType action) { case ActionType::Typecheck: case ActionType::DumpAST: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::EmitSILGen: @@ -256,6 +260,7 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: @@ -324,6 +329,7 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: @@ -367,6 +373,7 @@ bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: @@ -409,6 +416,7 @@ bool FrontendOptions::canActionEmitModuleSummary(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::EmitImportedModules: case ActionType::EmitPCH: case ActionType::DumpScopeMaps: @@ -451,6 +459,7 @@ bool FrontendOptions::canActionEmitObjCHeader(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::EmitPCH: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: @@ -492,6 +501,7 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: @@ -540,6 +550,7 @@ bool FrontendOptions::canActionEmitModuleSemanticInfo(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::EmitPCH: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: @@ -584,6 +595,7 @@ bool FrontendOptions::canActionEmitModule(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::EmitPCH: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: @@ -629,6 +641,7 @@ bool FrontendOptions::canActionEmitInterface(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::EmitImportedModules: case ActionType::EmitPCH: case ActionType::DumpScopeMaps: @@ -671,6 +684,7 @@ bool FrontendOptions::doesActionProduceOutput(ActionType action) { case ActionType::EmitSyntax: case ActionType::DumpInterfaceHash: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::EmitPCH: @@ -729,6 +743,7 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::EmitImportedModules: @@ -758,6 +773,7 @@ bool FrontendOptions::doesActionGenerateSIL(ActionType action) { case ActionType::EmitSyntax: case ActionType::DumpAST: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::EmitImportedModules: @@ -798,6 +814,7 @@ bool FrontendOptions::doesActionGenerateIR(ActionType action) { case ActionType::DumpAST: case ActionType::EmitSyntax: case ActionType::PrintAST: + case ActionType::PrintASTDecl: case ActionType::DumpScopeMaps: case ActionType::DumpTypeRefinementContexts: case ActionType::DumpTypeInfo: diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 2676926945a0a..4bcb8029e93fa 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1197,6 +1197,13 @@ static bool performAction(CompilerInstance &Instance, llvm::outs(), PrintOptions::printEverything()); return Instance.getASTContext().hadError(); }); + case FrontendOptions::ActionType::PrintASTDecl: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance).print( + llvm::outs(), PrintOptions::printDeclarations()); + return Instance.getASTContext().hadError(); + }); case FrontendOptions::ActionType::DumpScopeMaps: return withSemanticAnalysis( Instance, observer, [](CompilerInstance &Instance) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 12580d9b95ed7..24d0fc220b07b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6226,7 +6226,7 @@ ParserStatus Parser::parseGetEffectSpecifier(ParsedAccessors &accessors, ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, GenericParamList *GenericParams, - ParameterList *Indices, + ParameterList *Indices, TypeRepr *ResultType, ParsedAccessors &accessors, AbstractStorageDecl *storage, SourceLoc StaticLoc) { @@ -6258,8 +6258,22 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, if (parsingLimitedSyntax) return makeParserSuccess(); - diagnose(accessors.RBLoc, diag::computed_property_no_accessors, - /*subscript*/ Indices != nullptr); + if (ResultType != nullptr) { + // An error type at this point means we couldn't parse + // the result type for subscript correctly which will be + // already diagnosed as missing result type in declaration. + if (ResultType->getKind() == TypeReprKind::Error) + return makeParserError(); + + diagnose(accessors.RBLoc, diag::missing_accessor_return_decl, + /*subscript*/ Indices != nullptr, ResultType); + } else { + // This is supposed to be a computed property, but we don't + // have a result type representation which indicates this is probably not + // a well-formed computed property. So we can assume that empty braces + // are unexpected at this position for this declaration. + diagnose(accessors.LBLoc, diag::unexpected_curly_braces_in_decl); + } return makeParserError(); } @@ -6488,9 +6502,11 @@ Parser::parseDeclVarGetSet(PatternBindingEntry &entry, ParseDeclOptions Flags, // Parse getter and setter. ParsedAccessors accessors; + auto typedPattern = dyn_cast(pattern); + auto *resultTypeRepr = typedPattern ? typedPattern->getTypeRepr() : nullptr; auto AccessorStatus = parseGetSet(Flags, /*GenericParams=*/nullptr, - /*Indices=*/nullptr, accessors, - storage, StaticLoc); + /*Indices=*/nullptr, resultTypeRepr, + accessors, storage, StaticLoc); if (AccessorStatus.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (AccessorStatus.isErrorOrHasCompletion()) @@ -6500,7 +6516,7 @@ Parser::parseDeclVarGetSet(PatternBindingEntry &entry, ParseDeclOptions Flags, if (!PrimaryVar) return nullptr; - if (!isa(pattern)) { + if (!typedPattern) { if (accessors.Get || accessors.Set || accessors.Address || accessors.MutableAddress) { SourceLoc locAfterPattern = pattern->getLoc().getAdvancedLoc( @@ -8029,7 +8045,7 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, Status.setIsParseError(); } } else if (!Status.hasCodeCompletion()) { - Status |= parseGetSet(Flags, GenericParams, Indices.get(), + Status |= parseGetSet(Flags, GenericParams, Indices.get(), ElementTy.get(), accessors, Subscript, StaticLoc); } diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 06fc7d14e676f..c36ed7bc84d05 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -296,11 +296,27 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { SGF.LocalAuxiliaryDecls.clear(); } + bool didDiagnoseUnreachableElements = false; for (auto &ESD : S->getElements()) { - if (auto D = ESD.dyn_cast()) + if (auto D = ESD.dyn_cast()) { if (isa(D)) continue; + + // Hoisted declarations are emitted at the top level by emitSourceFile(). + if (D->isHoisted()) + continue; + + // PatternBindingBecls represent local variable bindings that execute + // as part of the function's execution. + if (!isa(D)) { + // Other decls define entities that may be used by the program, such as + // local function declarations. So handle them here, before checking for + // reachability, and then continue looping. + SGF.visit(D); + continue; + } + } // If we ever reach an unreachable point, stop emitting statements and issue // an unreachable code diagnostic. @@ -350,6 +366,10 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { continue; } + if (didDiagnoseUnreachableElements) + continue; + didDiagnoseUnreachableElements = true; + if (StmtType != UnknownStmtType) { diagnose(getASTContext(), ESD.getStartLoc(), diag::unreachable_code_after_stmt, StmtType); @@ -376,7 +396,7 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { } } } - return; + continue; } // Process children. @@ -395,11 +415,11 @@ void StmtEmitter::visitBraceStmt(BraceStmt *S) { } else { auto *D = ESD.get(); - // Hoisted declarations are emitted at the top level by emitSourceFile(). - if (D->isHoisted()) - continue; + // Only PatternBindingDecls should be emitted here. + // Other decls were handled above. + auto PBD = cast(D); - SGF.visit(D); + SGF.visit(PBD); } } } diff --git a/lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp b/lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp index ee383ccb25366..357e4ed3af9c8 100644 --- a/lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp @@ -917,12 +917,26 @@ bool GatherClosureUseVisitor::visitUse(Operand *op, AccessUseType useTy) { return true; if (memInstMustInitialize(op)) { + if (stripAccessMarkers(op->get()) != useState.address) { + LLVM_DEBUG(llvm::dbgs() + << "!!! Error! Found init use not on base address: " + << *op->getUser()); + return false; + } + LLVM_DEBUG(llvm::dbgs() << "ClosureUse: Found init: " << *op->getUser()); useState.inits.insert(op->getUser()); return true; } if (memInstMustReinitialize(op)) { + if (stripAccessMarkers(op->get()) != useState.address) { + LLVM_DEBUG(llvm::dbgs() + << "!!! Error! Found reinit use not on base address: " + << *op->getUser()); + return false; + } + LLVM_DEBUG(llvm::dbgs() << "ClosureUse: Found reinit: " << *op->getUser()); useState.insertReinit(op->getUser()); return true; @@ -1279,12 +1293,26 @@ bool GatherLexicalLifetimeUseVisitor::visitUse(Operand *op, } if (memInstMustInitialize(op)) { + if (stripAccessMarkers(op->get()) != useState.address) { + LLVM_DEBUG(llvm::dbgs() + << "!!! Error! Found init use not on base address: " + << *op->getUser()); + return false; + } + LLVM_DEBUG(llvm::dbgs() << "Found init: " << *op->getUser()); useState.inits.insert(op->getUser()); return true; } if (memInstMustReinitialize(op)) { + if (stripAccessMarkers(op->get()) != useState.address) { + LLVM_DEBUG(llvm::dbgs() + << "!!! Error! Found reinit use not on base address: " + << *op->getUser()); + return false; + } + LLVM_DEBUG(llvm::dbgs() << "Found reinit: " << *op->getUser()); useState.insertReinit(op->getUser()); return true; @@ -1308,6 +1336,14 @@ bool GatherLexicalLifetimeUseVisitor::visitUse(Operand *op, // are going to try and extend move checking into the partial apply using // cloning to eliminate destroys or reinits. if (auto fas = FullApplySite::isa(op->getUser())) { + if (stripAccessMarkers(op->get()) != useState.address) { + LLVM_DEBUG( + llvm::dbgs() + << "!!! Error! Found consuming closure use not on base address: " + << *op->getUser()); + return false; + } + if (fas.getArgumentOperandConvention(*op) == SILArgumentConvention::Indirect_InoutAliasable) { // If we don't find the function, we can't handle this, so bail. diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 6e6115376129b..9824e6bd3825d 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8035,7 +8035,6 @@ namespace { // Note that in this mode `ClosuresToTypeCheck` acts // as a stack because multi-statement closures could // have other multi-statement closures in the body. - auto &ctx = closure->getASTContext(); if (cs.participatesInInference(closure)) { hadError |= cs.applySolutionToBody( solution, closure, Rewriter.dc, @@ -8420,6 +8419,30 @@ static Optional applySolutionToInitialization( cs, resultTarget.getAsExpr(), resultTarget.getDeclContext())); } + // If this property has an opaque result type, set the underlying type + // substitutions based on the initializer. + if (auto var = resultTarget.getInitializationPattern()->getSingleVar()) { + SubstitutionMap substitutions; + if (auto opaque = var->getOpaqueResultTypeDecl()) { + resultTarget.getAsExpr()->forEachChildExpr([&](Expr *expr) -> Expr * { + if (auto coercionExpr = dyn_cast(expr)) { + auto newSubstitutions = + coercionExpr->substitutions.mapReplacementTypesOutOfContext(); + if (substitutions.empty()) { + substitutions = newSubstitutions; + } else { + assert(substitutions.getCanonical() == + newSubstitutions.getCanonical()); + } + } + return expr; + }); + + opaque->setUnderlyingTypeSubstitutions(substitutions); + } + } + + return resultTarget; } diff --git a/lib/Sema/ConstantnessSemaDiagnostics.cpp b/lib/Sema/ConstantnessSemaDiagnostics.cpp index ce786b1b517d8..79f7d0f0a8d87 100644 --- a/lib/Sema/ConstantnessSemaDiagnostics.cpp +++ b/lib/Sema/ConstantnessSemaDiagnostics.cpp @@ -334,9 +334,10 @@ void swift::diagnoseConstantArgumentRequirement( const Expr *expr, const DeclContext *declContext) { class ConstantReqCallWalker : public ASTWalker { DeclContext *DC; + bool insideClosure; public: - ConstantReqCallWalker(DeclContext *DC) : DC(DC) {} + ConstantReqCallWalker(DeclContext *DC) : DC(DC), insideClosure(false) {} // Descend until we find a call expressions. Note that the input expression // could be an assign expression or another expression that contains the @@ -347,10 +348,15 @@ void swift::diagnoseConstantArgumentRequirement( if (auto *closureExpr = dyn_cast(expr)) { return walkToClosureExprPre(closureExpr); } + // Interpolated expressions' bodies will be type checked // separately so exit early to avoid duplicate diagnostics. + // The caveat is that they won't be checked inside closure + // bodies because we manually check all closures to avoid + // duplicate diagnostics. Therefore we must still descend into + // interpolated expressions if we are inside of a closure. if (!expr || isa(expr) || !expr->getType() || - isa(expr)) + (isa(expr) && !insideClosure)) return {false, expr}; if (auto *callExpr = dyn_cast(expr)) { diagnoseConstantArgumentRequirementOfCall(callExpr, DC->getASTContext()); @@ -359,33 +365,30 @@ void swift::diagnoseConstantArgumentRequirement( } std::pair walkToClosureExprPre(ClosureExpr *closure) { - auto &ctx = DC->getASTContext(); - - if (closure->hasSingleExpressionBody() || - ctx.TypeCheckerOpts.EnableMultiStatementClosureInference) { - // Closure bodies are not visited directly by the ASTVisitor, - // so we must descend into the body manuall and set the - // DeclContext to that of the closure. - DC = closure; - return {true, closure}; - } - return {false, closure}; + DC = closure; + insideClosure = true; + return {true, closure}; } Expr *walkToExprPost(Expr *expr) override { if (auto *closureExpr = dyn_cast(expr)) { // Reset the DeclContext to the outer scope if we descended - // into a closure expr. + // into a closure expr and check whether or not we are still + // within a closure context. DC = closureExpr->getParent(); + insideClosure = isa(DC); } return expr; } - - std::pair walkToStmtPre(Stmt *stmt) override { - return {true, stmt}; - } }; + // We manually check closure bodies from their outer contexts, + // so bail early if we are being called directly on expressions + // inside of a closure body. + if (isa(declContext)) { + return; + } + ConstantReqCallWalker walker(const_cast(declContext)); const_cast(expr)->walk(walker); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 4eb8ae52d8d46..1224ebd7bce03 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -538,29 +538,6 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, PBD->setPattern(patternNumber, pattern, initContext); PBD->setInit(patternNumber, init); - // Bind a property with an opaque return type to the underlying type - // given by the initializer. - if (auto var = pattern->getSingleVar()) { - SubstitutionMap substitutions; - if (auto opaque = var->getOpaqueResultTypeDecl()) { - init->forEachChildExpr([&](Expr *expr) -> Expr * { - if (auto coercionExpr = dyn_cast(expr)) { - auto newSubstitutions = - coercionExpr->substitutions.mapReplacementTypesOutOfContext(); - if (substitutions.empty()) { - substitutions = newSubstitutions; - } else { - assert(substitutions.getCanonical() == - newSubstitutions.getCanonical()); - } - } - return expr; - }); - - opaque->setUnderlyingTypeSubstitutions(substitutions); - } - } - if (hadError) PBD->setInvalid(); diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 73e0d5af90a45..6dda3bad7d078 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1477,7 +1477,7 @@ static StringRef prettyPrintAttrs(const ValueDecl *VD, llvm::raw_svector_ostream os(out); StreamPrinter printer(os); - PrintOptions opts = PrintOptions::printEverything(); + PrintOptions opts = PrintOptions::printDeclarations(); VD->getAttrs().print(printer, opts, attrs, VD); return StringRef(out.begin(), out.size()).drop_back(); } @@ -1769,10 +1769,15 @@ class DeclChecker : public DeclVisitor { "@_implementationOnly "); } - static bool treatAsError = getenv("ENABLE_PUBLIC_IMPORT_OF_PRIVATE_AS_ERROR"); #ifndef NDEBUG - treatAsError = true; + static bool enableTreatAsError = true; +#else + static bool enableTreatAsError = getenv("ENABLE_PUBLIC_IMPORT_OF_PRIVATE_AS_ERROR"); #endif + + bool isImportOfUnderlying = importer->getName() == target->getName(); + bool treatAsError = enableTreatAsError && + !isImportOfUnderlying; if (!treatAsError) inFlight.limitBehavior(DiagnosticBehavior::Warning); } diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 390b24c07ad4b..1503b529be22a 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3326,6 +3326,7 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, bool hasModify = storage->getParsedAccessor(AccessorKind::Modify); bool hasMutableAddress = storage->getParsedAccessor(AccessorKind::MutableAddress); + auto *DC = storage->getDeclContext(); // 'get', 'read', and a non-mutable addressor are all exclusive. ReadImplKind readImpl; if (storage->getParsedAccessor(AccessorKind::Get)) { @@ -3354,10 +3355,10 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, readImpl = ReadImplKind::Stored; } - // Extensions can't have stored properties. If there are braces, assume - // this is an incomplete computed property. This avoids an "extensions - // must not contain stored properties" error later on. - } else if (isa(storage->getDeclContext()) && + // Extensions and enums can't have stored properties. If there are braces, + // assume this is an incomplete computed property. This avoids an + // "extensions|enums must not contain stored properties" error later on. + } else if ((isa(DC) || isa(DC)) && storage->getBracesRange().isValid()) { readImpl = ReadImplKind::Get; diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 74d57d3f3657b..2bbd91fdabbf1 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -758,7 +758,9 @@ function(add_swift_target_library_single target name) # Include LLVM Bitcode slices for iOS, Watch OS, and Apple TV OS device libraries. set(embed_bitcode_arg) if(SWIFT_EMBED_BITCODE_SECTION AND NOT SWIFTLIB_SINGLE_DONT_EMBED_BITCODE) - if("${SWIFTLIB_SINGLE_SDK}" STREQUAL "IOS" OR "${SWIFTLIB_SINGLE_SDK}" STREQUAL "TVOS" OR "${SWIFTLIB_SINGLE_SDK}" STREQUAL "WATCHOS") + if("${SWIFTLIB_SINGLE_SDK}" STREQUAL "IOS" OR + "${SWIFTLIB_SINGLE_SDK}" STREQUAL "TVOS" OR + "${SWIFTLIB_SINGLE_SDK}" STREQUAL "WATCHOS") list(APPEND SWIFTLIB_SINGLE_C_COMPILE_FLAGS "-fembed-bitcode") set(embed_bitcode_arg EMBED_BITCODE) endif() @@ -1318,9 +1320,9 @@ function(add_swift_target_library_single target name) endif() # Include LLVM Bitcode slices for iOS, Watch OS, and Apple TV OS device libraries. if(SWIFT_EMBED_BITCODE_SECTION AND NOT SWIFTLIB_SINGLE_DONT_EMBED_BITCODE) - if(${SWIFTLIB_SINGLE_SDK} STREQUAL "IOS" OR - ${SWIFTLIB_SINGLE_SDK} STREQUAL "TVOS" OR - ${SWIFTLIB_SINGLE_SDK} STREQUAL "WATCHOS") + if("${SWIFTLIB_SINGLE_SDK}" STREQUAL "IOS" OR + "${SWIFTLIB_SINGLE_SDK}" STREQUAL "TVOS" OR + "${SWIFTLIB_SINGLE_SDK}" STREQUAL "WATCHOS") # Please note that using a generator expression to fit # this in a single target_link_options does not work # (at least in CMake 3.15 and 3.16), diff --git a/stdlib/cmake/modules/StdlibOptions.cmake b/stdlib/cmake/modules/StdlibOptions.cmake index d4bcf6ac25e89..ff16f755835de 100644 --- a/stdlib/cmake/modules/StdlibOptions.cmake +++ b/stdlib/cmake/modules/StdlibOptions.cmake @@ -2,6 +2,7 @@ include_guard(GLOBAL) include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/modules/SwiftUtils.cmake) precondition(SWIFT_HOST_VARIANT_SDK) +precondition(SWIFT_DARWIN_PLATFORMS) if("${SWIFT_HOST_VARIANT_SDK}" MATCHES "CYGWIN") set(SWIFT_STDLIB_SUPPORTS_BACKTRACE_REPORTING_default FALSE) diff --git a/stdlib/private/StdlibUnittest/RaceTest.swift b/stdlib/private/StdlibUnittest/RaceTest.swift index 80bfaef3af815..438d5ccd91a9b 100644 --- a/stdlib/private/StdlibUnittest/RaceTest.swift +++ b/stdlib/private/StdlibUnittest/RaceTest.swift @@ -339,7 +339,7 @@ public func evaluateObservationsAllEqual(_ observations: [T]) return .pass } -// Notes: WebAssembly/WASI doesn't support multi thread yet +// WebAssembly/WASI doesn't support multi-threading yet #if os(WASI) public func runRaceTest( _: RT.Type, @@ -790,4 +790,4 @@ public func runRaceTest( timeoutInSeconds: timeoutInSeconds, threads: threads) } -#endif +#endif // os(WASI) diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 62a634cc3eb84..b5813fa5ebf92 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -135,8 +135,8 @@ public func _stdlib_thread_join( } return (CInt(result), value) #elseif os(WASI) - // WASI environment has a only single thread - return (0, nil) + // WASI environment is single-threaded + return (0, nil) #else var threadResultRawPtr: UnsafeMutableRawPointer? let result = pthread_join(thread, &threadResultRawPtr) diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 9334814072f75..7133d1b885008 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -39,7 +39,7 @@ public struct _stdlib_thread_barrier_t { var mutex: UnsafeMutablePointer? var cond: UnsafeMutablePointer? #elseif os(WASI) - // No pthread for WASI + // pthread is currently not available on WASI #else var mutex: UnsafeMutablePointer? var cond: UnsafeMutablePointer? @@ -72,7 +72,7 @@ public func _stdlib_thread_barrier_init( barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) InitializeConditionVariable(barrier.pointee.cond!) #elseif os(WASI) - // WASI environment has a only single thread + // WASI environment has only a single thread #else barrier.pointee.mutex = UnsafeMutablePointer.allocate(capacity: 1) barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) @@ -108,7 +108,7 @@ public func _stdlib_thread_barrier_destroy( // Condition Variables do not need to be explicitly destroyed // Mutexes do not need to be explicitly destroyed #elseif os(WASI) - // WASI environment has a only single thread + // WASI environment has only a single thread #else guard pthread_cond_destroy(barrier.pointee.cond!) == 0 && pthread_mutex_destroy(barrier.pointee.mutex!) == 0 else { @@ -133,7 +133,7 @@ public func _stdlib_thread_barrier_wait( #if os(Windows) AcquireSRWLockExclusive(barrier.pointee.mutex!) #elseif os(WASI) - // WASI environment has a only single thread + // WASI environment has only a single thread #else if pthread_mutex_lock(barrier.pointee.mutex!) != 0 { return -1 diff --git a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def index 4e27eeac2be25..9660d74f18bee 100644 --- a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def +++ b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def @@ -308,30 +308,10 @@ OVERRIDE_TASK_LOCAL(task_localsCopyTo, void, (AsyncTask *target), (target)) -OVERRIDE_TASK_STATUS(task_addStatusRecord, bool, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (TaskStatusRecord *newRecord), (newRecord)) - -OVERRIDE_TASK_STATUS(task_tryAddStatusRecord, bool, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (TaskStatusRecord *newRecord), (newRecord)) - -OVERRIDE_TASK_STATUS(task_removeStatusRecord, bool, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (TaskStatusRecord *record), (record)) - OVERRIDE_TASK_STATUS(task_hasTaskGroupStatusRecord, bool, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, , ) -OVERRIDE_TASK_STATUS(task_attachChild, ChildTaskStatusRecord *, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (AsyncTask *child), (child)) - -OVERRIDE_TASK_STATUS(task_detachChild, void, - SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), - swift::, (ChildTaskStatusRecord *record), (record)) - OVERRIDE_TASK_STATUS(task_cancel, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, (AsyncTask *task), (task)) diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index ff17f4fbf1522..1462009998147 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -525,6 +525,9 @@ class JobRef { return reinterpret_cast(Value); } + /// Get the Job pointer with no preconditions on its type, for tracing. + Job *getRawJob() const { return reinterpret_cast(Value & JobMask); } + bool operator==(JobRef other) const { return Value == other.Value; } @@ -687,6 +690,7 @@ class DefaultActorImpl : public HeapObject { flags.setIsDistributedRemote(isDistributedRemote); new (&CurrentState) swift::atomic(State{JobRef(), flags}); JobStorageHeapObject.metadata = nullptr; + concurrency::trace::actor_create(this); } /// Properly destruct an actor, except for the heap header. @@ -768,8 +772,10 @@ void DefaultActorImpl::destroy() { newState.Flags.setStatus(Status::Zombie_Latching); if (CurrentState.compare_exchange_weak(oldState, newState, std::memory_order_relaxed, - std::memory_order_relaxed)) + std::memory_order_relaxed)) { + concurrency::trace::actor_destroy(this); return; + } } } @@ -795,6 +801,8 @@ void DefaultActorImpl::deallocate() { } void DefaultActorImpl::deallocateUnconditional() { + concurrency::trace::actor_deallocate(this); + if (JobStorageHeapObject.metadata != nullptr) JobStorage.~ProcessInlineJob(); auto metadata = cast(this->metadata); @@ -926,12 +934,19 @@ static Job *preprocessQueue(JobRef first, return firstNewJob; } +static void traceJobQueue(DefaultActorImpl *actor, Job *first) { + concurrency::trace::actor_note_job_queue(actor, first, [](Job *job) { + return getNextJobInQueue(job).getAsPreprocessedJob(); + }); +} + void DefaultActorImpl::giveUpThread(RunningJobInfo runner) { SWIFT_TASK_DEBUG_LOG("giving up thread for actor %p", this); auto oldState = CurrentState.load(std::memory_order_acquire); assert(oldState.Flags.isAnyRunningStatus()); auto firstNewJob = preprocessQueue(oldState.FirstJob, JobRef(), nullptr); + traceJobQueue(this, firstNewJob); _swift_tsan_release(this); while (true) { @@ -978,16 +993,23 @@ void DefaultActorImpl::giveUpThread(RunningJobInfo runner) { firstNewJob = preprocessQueue(oldState.FirstJob, firstPreprocessed, firstNewJob); + traceJobQueue(this, firstNewJob); // Try again. continue; } -#define LOG_STATE_TRANSITION \ - SWIFT_TASK_DEBUG_LOG("actor %p transitioned from %zx to %zx (%s)\n", this, \ - oldState.Flags.getOpaqueValue(), \ - newState.Flags.getOpaqueValue(), __FUNCTION__) - LOG_STATE_TRANSITION; +#define ACTOR_STATE_TRANSITION \ + do { \ + SWIFT_TASK_DEBUG_LOG("actor %p transitioned from %zx to %zx (%s)\n", this, \ + oldState.Flags.getOpaqueValue(), \ + newState.Flags.getOpaqueValue(), __FUNCTION__); \ + concurrency::trace::actor_state_changed( \ + this, newState.FirstJob.getRawJob(), \ + newState.FirstJob.needsPreprocessing(), \ + newState.Flags.getOpaqueValue()); \ + } while (0) + ACTOR_STATE_TRANSITION; // The priority of the remaining work. auto newPriority = newState.Flags.getMaxPriority(); @@ -1040,7 +1062,8 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, auto success = CurrentState.compare_exchange_weak(oldState, newState, /*success*/ std::memory_order_relaxed, /*failure*/ std::memory_order_acquire); - if (success) LOG_STATE_TRANSITION; + if (success) + ACTOR_STATE_TRANSITION; return success; }; @@ -1082,7 +1105,7 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, /*success*/ std::memory_order_relaxed, /*failure*/ std::memory_order_acquire)) continue; - LOG_STATE_TRANSITION; + ACTOR_STATE_TRANSITION; _swift_tsan_acquire(this); // If that succeeded, we can proceed to the main body. @@ -1100,6 +1123,7 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, // Okay, now it's safe to look at queue state. // Preprocess any queue items at the front of the queue. auto newFirstJob = preprocessQueue(oldState.FirstJob, JobRef(), nullptr); + traceJobQueue(this, newFirstJob); _swift_tsan_release(this); while (true) { @@ -1144,11 +1168,12 @@ Job *DefaultActorImpl::claimNextJobOrGiveUp(bool actorIsOwned, newFirstJob = preprocessQueue(oldState.FirstJob, firstPreprocessed, newFirstJob); + traceJobQueue(this, newFirstJob); // Loop to retry updating the state. continue; } - LOG_STATE_TRANSITION; + ACTOR_STATE_TRANSITION; // We successfully updated the state. @@ -1180,10 +1205,12 @@ static void swift_job_runImpl(Job *job, ExecutorRef executor) { if (!executor.isGeneric()) trackingInfo.disallowSwitching(); trackingInfo.enterAndShadow(executor); + auto traceHandle = concurrency::trace::job_run_begin(job, &executor); SWIFT_TASK_DEBUG_LOG("%s(%p)", __func__, job); runJobInEstablishedExecutorContext(job); + concurrency::trace::job_run_end(job, &executor, traceHandle); trackingInfo.leave(); // Give up the current executor if this is a switching context @@ -1237,6 +1264,7 @@ static void processDefaultActor(DefaultActorImpl *currentActor, SWIFT_TASK_DEBUG_LOG("processDefaultActor %p claimed job %p", currentActor, job); + concurrency::trace::actor_dequeue(currentActor, job); // If we failed to claim a job, we have nothing to do. if (!job) { @@ -1351,7 +1379,8 @@ void DefaultActorImpl::enqueue(Job *job) { /*success*/ std::memory_order_release, /*failure*/ std::memory_order_relaxed)) continue; - LOG_STATE_TRANSITION; + ACTOR_STATE_TRANSITION; + concurrency::trace::actor_enqueue(this, job); // Okay, we successfully updated the status. Schedule a job to // process the actor if necessary. @@ -1381,7 +1410,7 @@ bool DefaultActorImpl::tryAssumeThread(RunningJobInfo runner) { if (CurrentState.compare_exchange_weak(oldState, newState, /*success*/ std::memory_order_relaxed, /*failure*/ std::memory_order_acquire)) { - LOG_STATE_TRANSITION; + ACTOR_STATE_TRANSITION; _swift_tsan_acquire(this); return true; } @@ -1687,7 +1716,12 @@ findDistributedAccessor(const char *targetNameStart, size_t targetNameLength) { /// argumentBuffer: Builtin.RawPointer, /// resultBuffer: Builtin.RawPointer) async throws using TargetExecutorSignature = - AsyncSignature; SWIFT_CC(swiftasync) @@ -1716,7 +1750,9 @@ static void ::swift_distributed_execute_target_resume( reinterpret_cast( parentCtx->ResumeParent); swift_task_dealloc(context); - return resumeInParent(parentCtx, error); + // See `swift_distributed_execute_target` - `parentCtx` in this case + // is `callContext` which should be completely transparent on resume. + return resumeInParent(parentCtx->Parent, error); } SWIFT_CC(swiftasync) @@ -1725,7 +1761,9 @@ void ::swift_distributed_execute_target( DefaultActor *actor, const char *targetNameStart, size_t targetNameLength, void *argumentBuffer, - void *resultBuffer) { + void *resultBuffer, + TaskContinuationFunction *resumeFunc, + AsyncContext *callContext) { auto *accessor = findDistributedAccessor(targetNameStart, targetNameLength); if (!accessor) { assert(false && "no distributed accessor accessor"); @@ -1743,7 +1781,17 @@ void ::swift_distributed_execute_target( AsyncContext *calleeContext = reinterpret_cast( swift_task_alloc(asyncFnPtr->ExpectedContextSize)); - calleeContext->Parent = callerContext; + // TODO(concurrency): Special functions like this one are currently set-up + // to pass "caller" context and resume function as extra parameters due to + // how they are declared in C. But this particular function behaves exactly + // like a regular `async throws`, which means that we need to initialize + // intermediate `callContext` using parent `callerContext`. A better fix for + // this situation would be to adjust IRGen and handle function like this + // like regular `async` functions even though they are classified as special. + callContext->Parent = callerContext; + callContext->ResumeParent = resumeFunc; + + calleeContext->Parent = callContext; calleeContext->ResumeParent = reinterpret_cast( swift_distributed_execute_target_resume); diff --git a/stdlib/public/Concurrency/AsyncLet.cpp b/stdlib/public/Concurrency/AsyncLet.cpp index e890f4ae09fff..c0dbb65b40712 100644 --- a/stdlib/public/Concurrency/AsyncLet.cpp +++ b/stdlib/public/Concurrency/AsyncLet.cpp @@ -142,8 +142,14 @@ void swift::asyncLet_addImpl(AsyncTask *task, AsyncLet *asyncLet, auto record = impl->getTaskRecord(); assert(impl == record && "the async-let IS the task record"); - // ok, now that the group actually is initialized: attach it to the task - swift_task_addStatusRecord(record); + // ok, now that the async let task actually is initialized: attach it to the + // current task + bool addedRecord = + addStatusRecord(record, [&](ActiveTaskStatus parentStatus) { + updateNewChildWithParentAndGroupState(task, parentStatus, NULL); + return true; + }); + assert(addedRecord); } // ============================================================================= @@ -309,7 +315,7 @@ static void swift_asyncLet_endImpl(AsyncLet *alet) { // Remove the child record from the parent task auto record = asImpl(alet)->getTaskRecord(); - swift_task_removeStatusRecord(record); + removeStatusRecord(record); // TODO: we need to implicitly await either before the end or here somehow. @@ -337,7 +343,7 @@ static void asyncLet_finish_after_task_completion(SWIFT_ASYNC_CONTEXT AsyncConte // Remove the child record from the parent task auto record = asImpl(alet)->getTaskRecord(); - swift_task_removeStatusRecord(record); + removeStatusRecord(record); // and finally, release the task and destroy the async-let assert(swift_task_getCurrent() && "async-let must have a parent task"); diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 1afbc6f88cc64..5eb23d0768aac 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -103,6 +103,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I TaskLocal.swift TaskSleep.swift ThreadSanitizer.cpp + TracingSignpost.cpp Mutex.cpp AsyncStreamBuffer.swift AsyncStream.swift diff --git a/stdlib/public/Concurrency/GlobalExecutor.cpp b/stdlib/public/Concurrency/GlobalExecutor.cpp index ecf81a1f82122..1dd4324f61b25 100644 --- a/stdlib/public/Concurrency/GlobalExecutor.cpp +++ b/stdlib/public/Concurrency/GlobalExecutor.cpp @@ -85,6 +85,8 @@ void (*swift::swift_task_enqueueMainExecutor_hook)( void swift::swift_task_enqueueGlobal(Job *job) { _swift_tsan_release(job); + concurrency::trace::job_enqueue_global(job); + if (swift_task_enqueueGlobal_hook) swift_task_enqueueGlobal_hook(job, swift_task_enqueueGlobalImpl); else @@ -92,6 +94,8 @@ void swift::swift_task_enqueueGlobal(Job *job) { } void swift::swift_task_enqueueGlobalWithDelay(JobDelay delay, Job *job) { + concurrency::trace::job_enqueue_global_with_delay(delay, job); + if (swift_task_enqueueGlobalWithDelay_hook) swift_task_enqueueGlobalWithDelay_hook( delay, job, swift_task_enqueueGlobalWithDelayImpl); @@ -100,6 +104,7 @@ void swift::swift_task_enqueueGlobalWithDelay(JobDelay delay, Job *job) { } void swift::swift_task_enqueueMainExecutor(Job *job) { + concurrency::trace::job_enqueue_main_executor(job); if (swift_task_enqueueMainExecutor_hook) swift_task_enqueueMainExecutor_hook(job, swift_task_enqueueMainExecutorImpl); diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 3936b32db398b..2a64ae4722613 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -24,6 +24,7 @@ #include "swift/Runtime/HeapObject.h" #include "TaskGroupPrivate.h" #include "TaskPrivate.h" +#include "Tracing.h" #include "Debug.h" #include "Error.h" @@ -102,6 +103,8 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask, auto queueHead = fragment->waitQueue.load(std::memory_order_acquire); bool contextIntialized = false; while (true) { + concurrency::trace::task_wait( + waitingTask, this, static_cast(queueHead.getStatus())); switch (queueHead.getStatus()) { case Status::Error: case Status::Success: @@ -245,6 +248,8 @@ AsyncTask::~AsyncTask() { } Private.destroy(); + + concurrency::trace::task_destroy(this); } void AsyncTask::setTaskId() { @@ -260,6 +265,12 @@ void AsyncTask::setTaskId() { _private().Id = (Fetched >> 32) & 0xffffffff; } +uint64_t AsyncTask::getTaskId() { + // Reconstitute a full 64-bit task ID from the 32-bit job ID and the upper + // 32 bits held in _private(). + return (uint64_t)Id << _private().Id; +} + SWIFT_CC(swift) static void destroyTask(SWIFT_CONTEXT HeapObject *obj) { auto task = static_cast(obj); @@ -439,6 +450,26 @@ task_future_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) { return _context->ResumeParent(_context->Parent); } +const void *AsyncTask::getResumeFunctionForLogging() { + if (ResumeTask == non_future_adapter) { + auto asyncContextPrefix = reinterpret_cast( + reinterpret_cast(ResumeContext) - sizeof(AsyncContextPrefix)); + return reinterpret_cast(asyncContextPrefix->asyncEntryPoint); + } else if (ResumeTask == future_adapter) { + auto asyncContextPrefix = reinterpret_cast( + reinterpret_cast(ResumeContext) - + sizeof(FutureAsyncContextPrefix)); + return reinterpret_cast(asyncContextPrefix->asyncEntryPoint); + } else if (ResumeTask == task_wait_throwing_resume_adapter) { + auto context = static_cast(ResumeContext); + return reinterpret_cast(context->ResumeParent); + } else if (ResumeTask == task_future_wait_resume_adapter) { + return reinterpret_cast(ResumeContext->ResumeParent); + } + + return reinterpret_cast(ResumeTask); +} + /// Implementation of task creation. SWIFT_CC(swift) static AsyncTaskAndContext swift_task_create_commonImpl( @@ -702,6 +733,8 @@ static AsyncTaskAndContext swift_task_create_commonImpl( initialContext->Parent = nullptr; initialContext->Flags = AsyncContextKind::Ordinary; + concurrency::trace::task_create(task, parent, group, asyncLet); + // Attach to the group, if needed. if (group) { swift_taskGroup_attachChild(group, task); @@ -1056,19 +1089,27 @@ swift_task_addCancellationHandlerImpl( auto *record = new (allocation) CancellationNotificationStatusRecord(unsigned_handler, context); - if (swift_task_addStatusRecord(record)) - return record; + bool fireHandlerNow = false; - // else, the task was already cancelled, so while the record was added, - // we must run it immediately here since no other task will trigger it. - record->run(); + addStatusRecord(record, [&](ActiveTaskStatus parentStatus) { + if (parentStatus.isCancelled()) { + fireHandlerNow = true; + // We don't fire the cancellation handler here since this function needs + // to be idempotent + } + return true; + }); + + if (fireHandlerNow) { + record->run(); + } return record; } SWIFT_CC(swift) static void swift_task_removeCancellationHandlerImpl( CancellationNotificationStatusRecord *record) { - swift_task_removeStatusRecord(record); + removeStatusRecord(record); swift_task_dealloc(record); } diff --git a/stdlib/public/Concurrency/TaskGroup.cpp b/stdlib/public/Concurrency/TaskGroup.cpp index f2a8bf07f9aba..bbf1785ffff29 100644 --- a/stdlib/public/Concurrency/TaskGroup.cpp +++ b/stdlib/public/Concurrency/TaskGroup.cpp @@ -474,11 +474,14 @@ static void swift_taskGroup_initializeImpl(TaskGroup *group, const Metadata *T) assert(impl == record && "the group IS the task record"); // ok, now that the group actually is initialized: attach it to the task - bool notCancelled = swift_task_addStatusRecord(record); - - // If the task has already been cancelled, reflect that immediately in - // the group status. - if (!notCancelled) impl->statusCancel(); + addStatusRecord(record, [&](ActiveTaskStatus parentStatus) { + // If the task has already been cancelled, reflect that immediately in + // the group's status. + if (parentStatus.isCancelled()) { + impl->statusCancel(); + } + return true; + }); } // ============================================================================= @@ -505,7 +508,7 @@ void TaskGroupImpl::destroy() { SWIFT_TASK_DEBUG_LOG("destroying task group = %p", this); // First, remove the group from the task and deallocate the record - swift_task_removeStatusRecord(getTaskRecord()); + removeStatusRecord(getTaskRecord()); // No need to drain our queue here, as by the time we call destroy, // all tasks inside the group must have been awaited on already. diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index 256ba1d9f20e0..3fb090370874d 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -18,6 +18,7 @@ #define SWIFT_CONCURRENCY_TASKPRIVATE_H #include "Error.h" +#include "Tracing.h" #include "swift/ABI/Metadata.h" #include "swift/ABI/Task.h" #include "swift/Runtime/Atomic.h" @@ -238,12 +239,23 @@ class alignas(sizeof(void*) * 2) ActiveTaskStatus { bool isStoredPriorityEscalated() const { return Flags & IsEscalated; } + + /// Creates a new active task status for a task with the specified priority + /// and masks away any existing priority related flags on the task status. All + /// other flags about the task are unmodified. This is only safe to use to + /// generate an initial task status for a new task that is not yet running. + ActiveTaskStatus withNewPriority(JobPriority priority) const { + return ActiveTaskStatus(Record, + (Flags & ~PriorityMask) | uintptr_t(priority)); + } + ActiveTaskStatus withEscalatedPriority(JobPriority priority) const { assert(priority > getStoredPriority()); return ActiveTaskStatus(Record, (Flags & ~PriorityMask) | IsEscalated | uintptr_t(priority)); } + ActiveTaskStatus withoutStoredPriorityEscalation() const { assert(isStoredPriorityEscalated()); return ActiveTaskStatus(Record, Flags & ~IsEscalated); @@ -266,6 +278,10 @@ class alignas(sizeof(void*) * 2) ActiveTaskStatus { llvm::iterator_range records() const { return record_iterator::rangeBeginning(getInnermostRecord()); } + + void traceStatusChanged(AsyncTask *task) { + concurrency::trace::task_status_changed(task, Flags); + } }; /// The size of an allocator slab. @@ -373,11 +389,13 @@ inline void AsyncTask::flagAsRunning() { if (newStatus.isStoredPriorityEscalated()) { newStatus = newStatus.withoutStoredPriorityEscalation(); Flags.setPriority(oldStatus.getStoredPriority()); + concurrency::trace::task_flags_changed(this, Flags.getOpaqueValue()); } if (_private().Status.compare_exchange_weak(oldStatus, newStatus, std::memory_order_relaxed, std::memory_order_relaxed)) { + newStatus.traceStatusChanged(this); adoptTaskVoucher(this); swift_task_enterThreadLocalContext( (char *)&_private().ExclusivityAccessSet[0]); @@ -403,11 +421,13 @@ inline void AsyncTask::flagAsSuspended() { if (newStatus.isStoredPriorityEscalated()) { newStatus = newStatus.withoutStoredPriorityEscalation(); Flags.setPriority(oldStatus.getStoredPriority()); + concurrency::trace::task_flags_changed(this, Flags.getOpaqueValue()); } if (_private().Status.compare_exchange_weak(oldStatus, newStatus, std::memory_order_relaxed, std::memory_order_relaxed)) { + newStatus.traceStatusChanged(this); swift_task_exitThreadLocalContext( (char *)&_private().ExclusivityAccessSet[0]); restoreTaskVoucher(this); @@ -438,6 +458,39 @@ inline bool AsyncTask::localValuePop() { return _private().Local.popValue(this); } +/*************** Methods for Status records manipulation ******************/ + +/// Remove a status record from a task. After this call returns, +/// the record's memory can be freely modified or deallocated. +/// +/// This must be called synchronously with the task. The record must +/// be registered with the task or else this may crash. +/// +/// The given record need not be the last record added to +/// the task, but the operation may be less efficient if not. +/// +/// Returns false if the task has been cancelled. +SWIFT_CC(swift) +bool removeStatusRecord(TaskStatusRecord *record); + +/// Add a status record to a task. This must be called synchronously with the +/// task. +/// +/// This function also takes in a function_ref which is given the task status of +/// the task we're adding the record to, to determine if the current status of +/// the task permits adding the status record. This function_ref may be called +/// multiple times and must be idempotent. +SWIFT_CC(swift) +bool addStatusRecord(TaskStatusRecord *record, + llvm::function_ref testAddRecord); + +/// A helper function for updating a new child task that is created with +/// information from the parent or the group that it was going to be added to. +SWIFT_CC(swift) +void updateNewChildWithParentAndGroupState(AsyncTask *child, + ActiveTaskStatus parentStatus, + TaskGroup *group); + } // end namespace swift #endif diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp index 571675d4c7633..793aa911fdaca 100644 --- a/stdlib/public/Concurrency/TaskStatus.cpp +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -184,6 +184,7 @@ static bool withStatusRecordLock(AsyncTask *task, if (task->_private().Status.compare_exchange_weak(status, newStatus, /*success*/ std::memory_order_relaxed, /*failure*/ loadOrdering)) { + newStatus.traceStatusChanged(task); status = newStatus; return false; } @@ -203,6 +204,7 @@ static bool withStatusRecordLock(AsyncTask *task, if (task->_private().Status.compare_exchange_weak(status, newStatus, /*success*/ std::memory_order_release, /*failure*/ loadOrdering)) { + newStatus.traceStatusChanged(task); // Update `status` for the purposes of the callback function. // Note that we don't include the record lock, but do need to @@ -230,6 +232,7 @@ static bool withStatusRecordLock(AsyncTask *task, // atomic objects in the task status records. Because of this, we can // actually unpublish the lock with a relaxed store. assert(!status.isLocked()); + status.traceStatusChanged(task); task->_private().Status.store(status, /*success*/ std::memory_order_relaxed); @@ -257,10 +260,12 @@ static bool withStatusRecordLock(AsyncTask *task, /**************************************************************************/ SWIFT_CC(swift) -static bool swift_task_addStatusRecordImpl(TaskStatusRecord *newRecord) { - auto task = swift_task_getCurrent(); +bool swift::addStatusRecord( + TaskStatusRecord *newRecord, + llvm::function_ref shouldAddRecord) { - // Load the current state. We can use a relaxed load because we're + auto task = swift_task_getCurrent(); + // Load the current state. We can use a relaxed load because we're // synchronous with the task. auto oldStatus = task->_private().Status.load(std::memory_order_relaxed); @@ -273,53 +278,27 @@ static bool swift_task_addStatusRecordImpl(TaskStatusRecord *newRecord) { newRecord->resetParent(oldStatus.getInnermostRecord()); // Set the record as the new innermost record. - // We have to use a release on success to make the initialization of - // the new record visible to the cancelling thread. ActiveTaskStatus newStatus = oldStatus.withInnermostRecord(newRecord); - if (task->_private().Status.compare_exchange_weak(oldStatus, newStatus, - /*success*/ std::memory_order_release, - /*failure*/ std::memory_order_relaxed)) - return !oldStatus.isCancelled(); - } -} - -SWIFT_CC(swift) -static bool swift_task_tryAddStatusRecordImpl(TaskStatusRecord *newRecord) { - auto task = swift_task_getCurrent(); - - // Load the current state. We can use a relaxed load because we're - // synchronous with the task. - auto oldStatus = task->_private().Status.load(std::memory_order_relaxed); - while (true) { - // If the old info is already cancelled, do nothing. - if (oldStatus.isCancelled()) + if (shouldAddRecord(newStatus)) { + // We have to use a release on success to make the initialization of + // the new record visible to the cancelling thread. + if (task->_private().Status.compare_exchange_weak( + oldStatus, newStatus, + /*success*/ std::memory_order_release, + /*failure*/ std::memory_order_relaxed)) { + return true; + } else { + // Retry + } + } else { return false; - - // Wait for any active lock to be released. - if (oldStatus.isLocked()) { - waitForStatusRecordUnlock(task, oldStatus); - - if (oldStatus.isCancelled()) - return false; } - - // Reset the parent of the new record. - newRecord->resetParent(oldStatus.getInnermostRecord()); - - // Set the record as the new innermost record. - // We have to use a release on success to make the initialization of - // the new record visible to the cancelling thread. - ActiveTaskStatus newStatus = oldStatus.withInnermostRecord(newRecord); - if (task->_private().Status.compare_exchange_weak(oldStatus, newStatus, - /*success*/ std::memory_order_release, - /*failure*/ std::memory_order_relaxed)) - return true; } } SWIFT_CC(swift) -static bool swift_task_removeStatusRecordImpl(TaskStatusRecord *record) { +bool swift::removeStatusRecord(TaskStatusRecord *record) { auto task = swift_task_getCurrent(); SWIFT_TASK_DEBUG_LOG("remove status record = %p, from current task = %p", record, task); @@ -404,21 +383,38 @@ static bool swift_task_hasTaskGroupStatusRecordImpl() { /**************************************************************************/ // ==== Child tasks ------------------------------------------------------------ -SWIFT_CC(swift) -static ChildTaskStatusRecord* -swift_task_attachChildImpl(AsyncTask *child) { - void *allocation = malloc(sizeof(swift::ChildTaskStatusRecord)); - auto record = new (allocation) swift::ChildTaskStatusRecord(child); - SWIFT_TASK_DEBUG_LOG("attach child task = %p, record = %p, to current task = %p", - child, record, swift_task_getCurrent()); - swift_task_addStatusRecord(record); - return record; -} +/// Called in the path of linking a child into a parent/group synchronously with +/// the parent task. +// +/// When called to link a child into a parent directly, this does not hold the +/// parent's task status record lock. When called to link a child into a task +/// group, this holds the parent's task status record lock. SWIFT_CC(swift) -static void -swift_task_detachChildImpl(ChildTaskStatusRecord *record) { - swift_task_removeStatusRecord(record); +void swift::updateNewChildWithParentAndGroupState(AsyncTask *child, + ActiveTaskStatus parentStatus, + TaskGroup *group) { + // We can take the fast path of just modifying the ActiveTaskStatus in the + // child task since we know that it won't have any task status records and + // cannot be accessed by anyone else since it hasn't been linked in yet. + // Avoids the extra logic in `swift_task_cancel` and `swift_task_escalate` + auto oldChildTaskStatus = + child->_private().Status.load(std::memory_order_relaxed); + assert(oldChildTaskStatus.getInnermostRecord() == NULL); + + auto newChildTaskStatus = oldChildTaskStatus; + + if (parentStatus.isCancelled() || (group && group->isCancelled())) { + newChildTaskStatus = newChildTaskStatus.withCancelled(); + } + + // Propagate max priority of parent to child task's active status and the Job + // header + JobPriority pri = parentStatus.getStoredPriority(); + newChildTaskStatus = newChildTaskStatus.withNewPriority(pri); + child->Flags.setPriority(pri); + + child->_private().Status.store(newChildTaskStatus, std::memory_order_relaxed); } SWIFT_CC(swift) @@ -430,11 +426,19 @@ static void swift_taskGroup_attachChildImpl(TaskGroup *group, // Acquire the status record lock of parent - we want to synchronize with // concurrent cancellation or escalation as we're adding new tasks to the // group. - auto parent = swift_task_getCurrent(); - withStatusRecordLock(parent, LockContext::OnTask, - [&](ActiveTaskStatus &status) { + withStatusRecordLock(parent, LockContext::OnTask, [&](ActiveTaskStatus &parentStatus) { group->addChildTask(child); + + // After getting parent's status record lock, do some sanity checks to + // see if parent task or group has state changes that need to be + // propagated to the child. + // + // This is the same logic that we would do if we were adding a child + // task status record - see also asyncLet_addImpl. Since we attach a + // child task to a TaskGroupRecord instead, we synchronize on the + // parent's task status and then update the child. + updateNewChildWithParentAndGroupState(child, parentStatus, group); }); } @@ -606,6 +610,7 @@ void AsyncTask::flagAsRunning_slow() { if (status.isStoredPriorityEscalated()) { status = status.withoutStoredPriorityEscalation(); Flags.setPriority(status.getStoredPriority()); + concurrency::trace::task_flags_changed(this, Flags.getOpaqueValue()); } }); } @@ -619,6 +624,7 @@ void AsyncTask::flagAsSuspended_slow() { if (status.isStoredPriorityEscalated()) { status = status.withoutStoredPriorityEscalation(); Flags.setPriority(status.getStoredPriority()); + concurrency::trace::task_flags_changed(this, Flags.getOpaqueValue()); } }); } diff --git a/stdlib/public/Concurrency/ThreadSanitizer.cpp b/stdlib/public/Concurrency/ThreadSanitizer.cpp index 969b51bd22053..0deab070ca583 100644 --- a/stdlib/public/Concurrency/ThreadSanitizer.cpp +++ b/stdlib/public/Concurrency/ThreadSanitizer.cpp @@ -16,7 +16,7 @@ #include "TaskPrivate.h" -// Thread Sanitizer is not supported on Windows. +// Thread Sanitizer is not supported on Windows or WASI. #if defined(_WIN32) || defined(__wasi__) void swift::_swift_tsan_acquire(void *addr) {} void swift::_swift_tsan_release(void *addr) {} diff --git a/stdlib/public/Concurrency/Tracing.h b/stdlib/public/Concurrency/Tracing.h new file mode 100644 index 0000000000000..0edda49feda59 --- /dev/null +++ b/stdlib/public/Concurrency/Tracing.h @@ -0,0 +1,94 @@ +//===--- Tracing.h - Support code for concurrency tracing ----------*- C++ -*-// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +// +// Support code for tracing events in the concurrency runtime +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CONCURRENCY_TRACING_H +#define SWIFT_CONCURRENCY_TRACING_H + +#include + +namespace swift { +class AsyncLet; +class AsyncTask; +class ExecutorRef; +struct HeapObject; +class Job; +class TaskGroup; +class TaskStatusRecord; + +namespace concurrency { +namespace trace { + +// Actor trace calls. + +void actor_create(HeapObject *actor); + +void actor_destroy(HeapObject *actor); + +void actor_deallocate(HeapObject *actor); + +void actor_enqueue(HeapObject *actor, Job *job); + +void actor_dequeue(HeapObject *actor, Job *job); + +// The `flags` parameter is the raw values of the actor's +// DefaultActorImpl::State::Flags. +void actor_state_changed(HeapObject *actor, Job *firstJob, + bool needsPreprocessing, uintptr_t flags); + +void actor_note_job_queue(HeapObject *actor, Job *first, + Job *(*getNext)(Job *)); + +// Task trace calls. + +void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, + AsyncLet *asyncLet); + +void task_destroy(AsyncTask *task); + +// The `flags` parameter is the raw value of the ActiveTaskStatus::Flags field +// in the task. +void task_status_changed(AsyncTask *task, uintptr_t flags); + +// The `flags` parameter is the raw value of Job::Flags. +void task_flags_changed(AsyncTask *task, uint32_t flags); + +// The `status` parameter is the value of the corresponding +// FutureFragment::Status. +void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status); + +void job_enqueue_global(Job *job); + +void job_enqueue_global_with_delay(unsigned long long delay, Job *job); + +void job_enqueue_main_executor(Job *job); + +// This returns a handle that must be passed to the corresponding call to +// task_run_end. +uint64_t job_run_begin(Job *job, ExecutorRef *executor); + +void job_run_end(Job *job, ExecutorRef *executor, uint64_t beginHandle); + +} // namespace trace +} // namespace concurrency +} // namespace swift + +#if __has_include() +#include "TracingSignpost.h" +#else +#include "TracingStubs.h" +#endif + +#endif diff --git a/stdlib/public/Concurrency/TracingSignpost.cpp b/stdlib/public/Concurrency/TracingSignpost.cpp new file mode 100644 index 0000000000000..b761c7142cb74 --- /dev/null +++ b/stdlib/public/Concurrency/TracingSignpost.cpp @@ -0,0 +1,51 @@ +//===--- TracingSignpost.cpp - Tracing with the signpost API -------*- C++ -*-// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +// +// Concurrency tracing implemented with the os_signpost API. +// +//===----------------------------------------------------------------------===// + +#if __has_include() + +#include "TracingSignpost.h" +#include + +// Temporary until we work out what categories we should really use. +#ifndef OS_LOG_CATEGORY_DYNAMIC_STACK_TRACING +#define OS_LOG_CATEGORY_DYNAMIC_STACK_TRACING "DynamicStackTracing" +#endif + +#define SWIFT_LOG_CONCURRENCY_ACTOR_SUBSYSTEM "com.apple.swift.actor" +#define SWIFT_LOG_CONCURRENCY_TASK_SUBSYSTEM "com.apple.swift.task" +#define SWIFT_LOG_ACTOR_CATEGORY OS_LOG_CATEGORY_DYNAMIC_STACK_TRACING +#define SWIFT_LOG_TASK_CATEGORY OS_LOG_CATEGORY_DYNAMIC_STACK_TRACING + +namespace swift { +namespace concurrency { +namespace trace { + +os_log_t ActorLog; +os_log_t TaskLog; +OnceToken_t LogsToken; + +void setupLogs(void *unused) { + ActorLog = os_log_create(SWIFT_LOG_CONCURRENCY_ACTOR_SUBSYSTEM, + SWIFT_LOG_ACTOR_CATEGORY); + TaskLog = os_log_create(SWIFT_LOG_CONCURRENCY_TASK_SUBSYSTEM, + SWIFT_LOG_TASK_CATEGORY); +} + +} // namespace trace +} // namespace concurrency +} // namespace swift + +#endif diff --git a/stdlib/public/Concurrency/TracingSignpost.h b/stdlib/public/Concurrency/TracingSignpost.h new file mode 100644 index 0000000000000..322f75cdbcaed --- /dev/null +++ b/stdlib/public/Concurrency/TracingSignpost.h @@ -0,0 +1,278 @@ +//===--- TracingSignpost.h - Tracing with the signpost API ---------*- C++ -*-// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +// +// Concurrency tracing implemented with the os_signpost API. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CONCURRENCY_TRACINGSIGNPOST_H +#define SWIFT_CONCURRENCY_TRACINGSIGNPOST_H + +#include "TaskPrivate.h" +#include "Tracing.h" +#include "swift/ABI/Task.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/Casting.h" +#include "swift/Runtime/HeapObject.h" +#include +#include +#include + +// Compatibility notes: +// +// These signposts can be read by external software that isn't synced with the +// Swift runtime build. Changes here must be considered carefully to avoid +// breaking users of these signposts. +// +// We may: +// * Add new signpost calls with new names. (Keeping in mind that they won't be +// picked up by software that doesn't know about them.) +// * Remove existing calls if the given event is somehow no longer relevant. +// * Change format strings. +// * Add format string arguments. +// +// We may NOT: +// * Change the order of existing format string arguments. +// * Change event names. +// * Change subsystem names. + +#define SWIFT_LOG_ACTOR_LIFETIME_NAME "actor_lifetime" +#define SWIFT_LOG_ACTOR_DEALLOCATE_NAME "actor_deallocate" +#define SWIFT_LOG_ACTOR_ENQUEUE_NAME "actor_enqueue" +#define SWIFT_LOG_ACTOR_DEQUEUE_NAME "actor_dequeue" +#define SWIFT_LOG_ACTOR_STATE_CHANGED_NAME "actor_state_changed" +#define SWIFT_LOG_ACTOR_JOB_QUEUE_NAME "actor_job_queue" +#define SWIFT_LOG_TASK_LIFETIME_NAME "task_lifetime" +#define SWIFT_LOG_TASK_STATUS_CHANGED_NAME "task_status_changed" +#define SWIFT_LOG_TASK_WAIT_NAME "task_wait" +#define SWIFT_LOG_JOB_ENQUEUE_GLOBAL_NAME "job_enqueue_global" +#define SWIFT_LOG_JOB_ENQUEUE_GLOBAL_WITH_DELAY_NAME \ + "job_enqueue_global_with_delay" +#define SWIFT_LOG_JOB_ENQUEUE_MAIN_EXECUTOR_NAME "job_enqueue_main_executor" +#define SWIFT_LOG_JOB_RUN_NAME "job_run" + +namespace swift { +namespace concurrency { +namespace trace { + +extern os_log_t ActorLog; +extern os_log_t TaskLog; +extern OnceToken_t LogsToken; + +void setupLogs(void *unused); + +// Check a representative os_signpost function for NULL rather than doing a +// standard availability check, for better performance if the check doesn't get +// optimized out. +#define ENSURE_LOGS(...) \ + do { \ + if (!SWIFT_RUNTIME_WEAK_CHECK(os_signpost_enabled)) \ + return __VA_ARGS__; \ + SWIFT_ONCE_F(LogsToken, setupLogs, nullptr); \ + } while (0) + +// Every function does ENSURE_LOGS() before making any os_signpost calls, so +// we can skip availability checking on all the individual calls. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + +// Actor trace calls. + +inline void actor_create(HeapObject *actor) { + ENSURE_LOGS(); + + // Do an explicit enabled check here to avoid the cost of swift_getTypeName in + // the common case. + if (!os_signpost_enabled(ActorLog)) + return; + + auto typeName = swift_getTypeName(swift_getObjectType(actor), true); + + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_interval_begin(ActorLog, id, SWIFT_LOG_ACTOR_LIFETIME_NAME, + "actor=%p typeName:%.*s", actor, + (int)typeName.length, typeName.data); +} + +inline void actor_destroy(HeapObject *actor) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_interval_end(ActorLog, id, SWIFT_LOG_ACTOR_LIFETIME_NAME, + "actor=%p", actor); +} + +inline void actor_deallocate(HeapObject *actor) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_event_emit(ActorLog, id, SWIFT_LOG_ACTOR_DEALLOCATE_NAME, + "actor=%p", actor); +} + +inline void actor_enqueue(HeapObject *actor, Job *job) { + if (AsyncTask *task = dyn_cast(job)) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_event_emit(ActorLog, id, SWIFT_LOG_ACTOR_ENQUEUE_NAME, + "actor=%p task=%" PRIx64, actor, task->getTaskId()); + } +} + +inline void actor_dequeue(HeapObject *actor, Job *job) { + if (AsyncTask *task = dyn_cast_or_null(job)) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_event_emit(ActorLog, id, SWIFT_LOG_ACTOR_DEQUEUE_NAME, + "actor=%p task=%" PRIx64, actor, task->getTaskId()); + } +} + +inline void actor_state_changed(HeapObject *actor, Job *firstJob, + bool needsPreprocessing, uintptr_t flags) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_event_emit(ActorLog, id, SWIFT_LOG_ACTOR_STATE_CHANGED_NAME, + "actor=%p needsPreprocessing=%d " + "flags=0x%" PRIxPTR, + actor, needsPreprocessing, flags); +} + +inline void actor_note_job_queue(HeapObject *actor, Job *first, + Job *(*getNext)(Job *)) { + ENSURE_LOGS(); + + // Do an explicit enabled check here, since the loop is potentially expensive. + if (!os_signpost_enabled(ActorLog)) + return; + + // Count the number of pending jobs. We may want to track this separately + // rather than looping to count, but this gets the job done for now. + + unsigned jobCount = 0; + for (Job *job = first; job; job = getNext(job)) + if (isa(job)) + jobCount++; + + auto id = os_signpost_id_make_with_pointer(ActorLog, actor); + os_signpost_event_emit(ActorLog, id, SWIFT_LOG_ACTOR_JOB_QUEUE_NAME, + "actor=%p jobCount=%u", actor, jobCount); +} + +// Task trace calls. + +inline void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, + AsyncLet *asyncLet) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, task); + os_signpost_interval_begin( + TaskLog, id, SWIFT_LOG_TASK_LIFETIME_NAME, + "task=%" PRIx64 " resumefn=%p flags=0x%" PRIx32 + " parent=%p group=%p asyncLet=%p", + task->getTaskId(), task->getResumeFunctionForLogging(), + task->Flags.getOpaqueValue(), parent, group, asyncLet); +} + +inline void task_destroy(AsyncTask *task) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, task); + os_signpost_interval_end(TaskLog, id, SWIFT_LOG_TASK_LIFETIME_NAME, + "task=%" PRIx64 "", task->getTaskId()); +} + +inline void task_status_changed(AsyncTask *task, uintptr_t flags) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, task); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_STATUS_CHANGED_NAME, + "task=%" PRIx64 " resumefn=%p flags=0x%" PRIxPTR, + task->getTaskId(), task->getResumeFunctionForLogging(), + flags); +} + +inline void task_flags_changed(AsyncTask *task, uint32_t flags) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, task); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_STATUS_CHANGED_NAME, + "task=%" PRIx64 " flags=0x%" PRIx32, task->getTaskId(), + flags); +} + +inline void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, task); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_WAIT_NAME, + "task=%" PRIx64 " waitingOnTask=%p status=0x%" PRIxPTR, + task->getTaskId(), waitingOn, status); +} + +inline void job_enqueue_global(Job *job) { + if (AsyncTask *task = dyn_cast(job)) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, job); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_JOB_ENQUEUE_GLOBAL_NAME, + "task=%" PRIx64, task->getTaskId()); + } +} + +inline void job_enqueue_global_with_delay(unsigned long long delay, Job *job) { + if (AsyncTask *task = dyn_cast(job)) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, job); + os_signpost_event_emit( + TaskLog, id, SWIFT_LOG_JOB_ENQUEUE_GLOBAL_WITH_DELAY_NAME, + "task=%" PRIx64 " delay=%llu", task->getTaskId(), delay); + } +} + +inline void job_enqueue_main_executor(Job *job) { + if (AsyncTask *task = dyn_cast(job)) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, job); + os_signpost_event_emit(TaskLog, id, + SWIFT_LOG_JOB_ENQUEUE_MAIN_EXECUTOR_NAME, + "task=%" PRIx64, task->getTaskId()); + } +} + +inline uint64_t job_run_begin(Job *job, ExecutorRef *executor) { + if (AsyncTask *task = dyn_cast(job)) { + ENSURE_LOGS(0); + auto id = os_signpost_id_generate(TaskLog); + os_signpost_interval_begin( + TaskLog, id, SWIFT_LOG_JOB_RUN_NAME, + "task=%" PRIx64 + " executorIdentity=%p executorImplementation=0x%" PRIxPTR, + task->getTaskId(), executor->getIdentity(), + executor->getRawImplementation()); + return id; + } + return 0; +} + +inline void job_run_end(Job *job, ExecutorRef *executor, uint64_t beginHandle) { + if (AsyncTask *task = dyn_cast(job)) { + ENSURE_LOGS(); + os_signpost_interval_end( + TaskLog, beginHandle, SWIFT_LOG_JOB_RUN_NAME, + "task=%" PRIx64 + " executorIdentity=%p executorImplementation=0x%" PRIxPTR, + task->getTaskId(), executor->getIdentity(), + executor->getRawImplementation()); + } +} + +#pragma clang diagnostic pop + +} // namespace trace +} // namespace concurrency +} // namespace swift + +#endif diff --git a/stdlib/public/Concurrency/TracingStubs.h b/stdlib/public/Concurrency/TracingStubs.h new file mode 100644 index 0000000000000..d62c2430b5113 --- /dev/null +++ b/stdlib/public/Concurrency/TracingStubs.h @@ -0,0 +1,72 @@ +//===--- TracingStubs.h - Default stub implementation of tracing. --*- C++ -*-// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2021 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 +// +//===----------------------------------------------------------------------===// +// +// Concurrency tracing stubs for OSes without tracing support. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CONCURRENCY_TRACINGSIGNPOST_H +#define SWIFT_CONCURRENCY_TRACINGSIGNPOST_H + +#include "Tracing.h" + +namespace swift { +namespace concurrency { +namespace trace { + +inline void actor_create(HeapObject *actor) {} + +inline void actor_destroy(HeapObject *actor) {} + +inline void actor_deallocate(HeapObject *actor) {} + +inline void actor_enqueue(HeapObject *actor, Job *job) {} + +inline void actor_dequeue(HeapObject *actor, Job *job) {} + +inline void actor_state_changed(HeapObject *actor, Job *firstJob, + bool needsPreprocessing, uintptr_t flags) {} + +inline void actor_note_job_queue(HeapObject *actor, Job *first, + Job *(*getNext)(Job *)) {} + +inline void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, + AsyncLet *asyncLet) {} + +inline void task_destroy(AsyncTask *task) {} + +inline void task_status_changed(AsyncTask *task, TaskStatusRecord *record, + uintptr_t flags) {} + +inline void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status) { +} + +inline void task_status_changed(AsyncTask *task, uintptr_t flags) {} + +inline void task_flags_changed(AsyncTask *task, uint32_t flags) {} + +inline void job_enqueue_global(Job *job) {} + +inline void job_enqueue_global_with_delay(unsigned long long delay, Job *job) {} + +inline void job_enqueue_main_executor(Job *job) {} + +inline uint64_t job_run_begin(Job *job, ExecutorRef *executor) { return 0; } + +inline void job_run_end(Job *job, ExecutorRef *executor, uint64_t beginHandle) { +} + +} // namespace trace +} // namespace concurrency +} // namespace swift + +#endif diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index a21a66c6900b6..fda009f72a8ff 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -222,18 +222,20 @@ extension DistributedActorSystem { // Decode the return type func allocateReturnTypeBuffer(_: R.Type) -> UnsafeRawPointer? { - if R.self == Void.self { - return nil - } return UnsafeRawPointer(UnsafeMutablePointer.allocate(capacity: 1)) } guard let returnType: Any.Type = _getReturnTypeInfo(mangledMethodName: mangledTargetName) else { throw ExecuteDistributedTargetError( message: "Failed to decode distributed target return type") } - let resultBuffer = _openExistential(returnType, do: allocateReturnTypeBuffer) + + guard let resultBuffer = _openExistential(returnType, do: allocateReturnTypeBuffer) else { + throw ExecuteDistributedTargetError( + message: "Failed to allocate buffer for distributed target return type") + } + func destroyReturnTypeBuffer(_: R.Type) { - resultBuffer?.assumingMemoryBound(to: R.self).deallocate() + resultBuffer.assumingMemoryBound(to: R.self).deallocate() } defer { _openExistential(returnType, do: destroyReturnTypeBuffer) @@ -252,16 +254,11 @@ extension DistributedActorSystem { on: actor, mangledTargetName, UInt(mangledTargetName.count), argumentBuffer: hargs.buffer._rawValue, - resultBuffer: resultBuffer?._rawValue + resultBuffer: resultBuffer._rawValue ) - // Get the result out of the buffer and invoke onReturn with the right type - guard let resultBuffer = resultBuffer else { - try await handler.onReturn(value: ()) - return - } - func onReturn(_: R.Type) async throws { - try await handler.onReturn/**/(value: resultBuffer) + func onReturn(_ resultTy: R.Type) async throws { + try await handler.onReturn/**/(value: resultBuffer.load(as: resultTy)) } try await _openExistential(returnType, do: onReturn) @@ -277,7 +274,7 @@ func _executeDistributedTarget( on actor: AnyObject, // DistributedActor _ targetName: UnsafePointer, _ targetNameLength: UInt, argumentBuffer: Builtin.RawPointer, // HeterogeneousBuffer of arguments - resultBuffer: Builtin.RawPointer? + resultBuffer: Builtin.RawPointer ) async throws // ==== ---------------------------------------------------------------------------------------------------------------- diff --git a/stdlib/public/SwiftShims/UnicodeData.h b/stdlib/public/SwiftShims/UnicodeData.h index 6256c36a22367..87c6bb7a92e79 100644 --- a/stdlib/public/SwiftShims/UnicodeData.h +++ b/stdlib/public/SwiftShims/UnicodeData.h @@ -62,6 +62,9 @@ __swift_uint32_t _swift_stdlib_getComposition(__swift_uint32_t x, SWIFT_RUNTIME_STDLIB_INTERNAL __swift_uint8_t _swift_stdlib_getGraphemeBreakProperty(__swift_uint32_t scalar); +SWIFT_RUNTIME_STDLIB_INTERNAL +__swift_bool _swift_stdlib_isLinkingConsonant(__swift_uint32_t scalar); + //===----------------------------------------------------------------------===// // Unicode.Scalar.Properties //===----------------------------------------------------------------------===// diff --git a/stdlib/public/core/AnyHashable.swift b/stdlib/public/core/AnyHashable.swift index a76d2de22e414..e2377ab0d02bc 100644 --- a/stdlib/public/core/AnyHashable.swift +++ b/stdlib/public/core/AnyHashable.swift @@ -145,7 +145,13 @@ public struct AnyHashable { /// Creates a type-erased hashable value that wraps the given instance. /// /// - Parameter base: A hashable value to wrap. + @_specialize(where H == String) public init(_ base: H) { + if H.self == String.self { + self.init(_box: _ConcreteHashableBox(base)) + return + } + if let custom = (base as? _HasCustomAnyHashableRepresentation)?._toCustomAnyHashable() { self = custom diff --git a/stdlib/public/core/StringGraphemeBreaking.swift b/stdlib/public/core/StringGraphemeBreaking.swift index 04ee06a3781c6..b85d5b7aaefe2 100644 --- a/stdlib/public/core/StringGraphemeBreaking.swift +++ b/stdlib/public/core/StringGraphemeBreaking.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import SwiftShims + /// CR and LF are common special cases in grapheme breaking logic private var _CR: UInt8 { return 0x0d } private var _LF: UInt8 { return 0x0a } @@ -175,13 +177,56 @@ extension _StringGuts { } } +extension Unicode.Scalar { + fileprivate var _isLinkingConsonant: Bool { + _swift_stdlib_isLinkingConsonant(value) + } + + fileprivate var _isVirama: Bool { + switch value { + // Devanagari + case 0x94D: + return true + // Bengali + case 0x9CD: + return true + // Gujarati + case 0xACD: + return true + // Oriya + case 0xB4D: + return true + // Telugu + case 0xC4D: + return true + // Malayalam + case 0xD4D: + return true + + default: + return false + } + } +} + internal struct _GraphemeBreakingState { + // When we're looking through an indic sequence, one of the requirements is + // that there is at LEAST 1 Virama present between two linking consonants. + // This value helps ensure that when we ultimately need to decide whether or + // not to break that we've at least seen 1 when walking. + var hasSeenVirama = false + // When walking forwards in a string, we need to know whether or not we've // entered an emoji sequence to be able to eventually break after all of the // emoji's various extenders and zero width joiners. This bit allows us to // keep track of whether or not we're still in an emoji sequence when deciding // to break. - var isInEmojiSequence: Bool = false + var isInEmojiSequence = false + + // Similar to emoji sequences, we need to know not to break an Indic grapheme + // sequence. This sequence is (potentially) composed of many scalars and isn't + // as trivial as comparing two grapheme properties. + var isInIndicSequence = false // When walking forward in a string, we need to not break on emoji flag // sequences. Emoji flag sequences are composed of 2 regional indicators, so @@ -190,7 +235,7 @@ internal struct _GraphemeBreakingState { // is another regional indicator, we reach the same decision rule, but in this // case we actually need to break there's a boundary between emoji flag // sequences. - var shouldBreakRI: Bool = false + var shouldBreakRI = false } extension _StringGuts { @@ -288,8 +333,12 @@ extension _StringGuts { // continue treating the current grapheme cluster as an emoji sequence. var enterEmojiSequence = false + // Very similar to emoji sequences, but for Indic grapheme sequences. + var enterIndicSequence = false + defer { state.isInEmojiSequence = enterEmojiSequence + state.isInIndicSequence = enterIndicSequence } switch (x, y) { @@ -338,6 +387,26 @@ extension _StringGuts { enterEmojiSequence = true } + // If we're currently in an indic sequence (or if our lhs is a linking + // consonant), then this check and everything underneath ensures that + // we continue being in one and may check if this extend is a Virama. + if state.isInIndicSequence || scalar1._isLinkingConsonant { + if y == .extend { + let extendNormData = Unicode._NormData(scalar2, fastUpperbound: 0x300) + + // If our extend's CCC is 0, then this rule does not apply. + guard extendNormData.ccc != 0 else { + return false + } + } + + enterIndicSequence = true + + if scalar2._isVirama { + state.hasSeenVirama = true + } + } + return false // GB9a @@ -370,6 +439,32 @@ extension _StringGuts { // GB999 default: + // GB9c + if state.isInIndicSequence, state.hasSeenVirama, scalar2._isLinkingConsonant { + state.hasSeenVirama = false + return false + } + + // Handle GB9c when walking backwards. + if isBackwards { + switch (x, scalar2._isLinkingConsonant) { + case (.extend, true): + let extendNormData = Unicode._NormData(scalar1, fastUpperbound: 0x300) + + guard extendNormData.ccc != 0 else { + return true + } + + return !checkIfInIndicSequence(index) + + case (.zwj, true): + return !checkIfInIndicSequence(index) + + default: + return true + } + } + return true } } @@ -417,9 +512,7 @@ extension _StringGuts { // | = We found our starting .extendedPictographic letting us // know that we are in an emoji sequence so our initial // break question is answered as NO. - internal func checkIfInEmojiSequence( - _ index: Int - ) -> Bool { + internal func checkIfInEmojiSequence(_ index: Int) -> Bool { var emojiIdx = String.Index(_encodedOffset: index) guard emojiIdx != startIndex else { @@ -447,7 +540,91 @@ extension _StringGuts { return false } - + + // When walking backwards, it's impossible to know whether we break when we + // see our first ((.extend|.zwj), .linkingConsonant) without walking + // further backwards. This walks the string backwards enough until we figure + // out whether or not to break this indic sequence. For example: + // + // Scalar view #1: + // + // [.virama, .extend, .linkingConsonant] + // ^ + // | = To be able to know whether or not to break these + // two, we need to walk backwards to determine if + // this is a legitimate indic sequence. + // ^ + // | = The scalar sequence ends without a starting linking consonant, + // so this is in fact not an indic sequence, so we can break the two. + // + // Scalar view #2: + // + // [.linkingConsonant, .virama, .extend, .linkingConsonant] + // ^ + // | = Same as above + // ^ + // | = This is a virama, so we at least have seen + // 1 to be able to return true if we see a + // linking consonant later. + // ^ + // | = Is a linking consonant and we've seen a virama, so this is a + // legitimate indic sequence, so do NOT break the initial question. + internal func checkIfInIndicSequence(_ index: Int) -> Bool { + var indicIdx = String.Index(_encodedOffset: index) + + guard indicIdx != startIndex else { + return false + } + + let scalars = String.UnicodeScalarView(self) + scalars.formIndex(before: &indicIdx) + + var hasSeenVirama = false + + // Check if the first extend was the Virama. + let scalar = scalars[indicIdx] + + if scalar._isVirama { + hasSeenVirama = true + } + + while indicIdx != startIndex { + scalars.formIndex(before: &indicIdx) + let scalar = scalars[indicIdx] + + let gbp = Unicode._GraphemeBreakProperty(from: scalar) + + switch (gbp, scalar._isLinkingConsonant) { + case (.extend, false): + let extendNormData = Unicode._NormData(scalar, fastUpperbound: 0x300) + + guard extendNormData.ccc != 0 else { + return false + } + + if scalar._isVirama { + hasSeenVirama = true + } + + case (.zwj, false): + continue + + // LinkingConsonant + case (_, true): + guard hasSeenVirama else { + return false + } + + return true + + default: + return false + } + } + + return false + } + // When walking backwards, it's impossible to know whether we break when we // see our first (.regionalIndicator, .regionalIndicator) without walking // further backwards. This walks the string backwards enough until we figure diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp index 31946972161e4..89a8064fdeaea 100644 --- a/stdlib/public/runtime/DynamicCast.cpp +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -764,14 +764,21 @@ tryCastToAnyHashable( assert(cast(destType)->Description == &STRUCT_TYPE_DESCR_SYM(s11AnyHashable)); + const HashableWitnessTable *hashableConformance = nullptr; + switch (srcType->getKind()) { case MetadataKind::ForeignClass: // CF -> String case MetadataKind::ObjCClassWrapper: { // Obj-C -> String #if SWIFT_OBJC_INTEROP - // TODO: Implement a fast path for NSString->AnyHashable casts. - // These are incredibly common because an NSDictionary with - // NSString keys is bridged by default to [AnyHashable:Any]. - // Until this is implemented, fall through to the general case + auto cls = srcType; + auto nsString = getNSStringMetadata(); + do { + if (cls == nsString) { + hashableConformance = getNSStringHashableConformance(); + break; + } + cls = _swift_class_getSuperclass(cls); + } while (cls != nullptr); break; #else // If no Obj-C interop, just fall through to the general case. @@ -805,8 +812,11 @@ tryCastToAnyHashable( // General case: If it conforms to Hashable, we cast it - auto hashableConformance = reinterpret_cast( - swift_conformsToProtocol(srcType, &HashableProtocolDescriptor)); + if (hashableConformance == nullptr) { + hashableConformance = reinterpret_cast( + swift_conformsToProtocol(srcType, &HashableProtocolDescriptor) + ); + } if (hashableConformance) { _swift_convertToAnyHashableIndirect(srcValue, destLocation, srcType, hashableConformance); diff --git a/stdlib/public/runtime/SwiftObject.h b/stdlib/public/runtime/SwiftObject.h index afd291ccf09ed..2265eab0d3cfe 100644 --- a/stdlib/public/runtime/SwiftObject.h +++ b/stdlib/public/runtime/SwiftObject.h @@ -22,6 +22,7 @@ #include #include #include "swift/Runtime/HeapObject.h" +#include "../runtime/SwiftHashableSupport.h" #if SWIFT_OBJC_INTEROP #include #endif @@ -89,7 +90,11 @@ namespace swift { /// Get the NSObject metadata. const Metadata *getNSObjectMetadata(); +const Metadata *getNSStringMetadata(); +namespace hashable_support { +const HashableWitnessTable *getNSStringHashableConformance(); +} } #endif diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 5ec475d81c6e0..8ee9d97051952 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -56,6 +56,7 @@ #endif using namespace swift; +using namespace hashable_support; #if SWIFT_HAS_ISA_MASKING OBJC_EXPORT __attribute__((__weak_import__)) @@ -1600,6 +1601,24 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector, swift_getObjCClassMetadata((const ClassMetadata *)[NSObject class])); } +const Metadata *swift::getNSStringMetadata() { + return SWIFT_LAZY_CONSTANT(swift_getObjCClassMetadata( + (const ClassMetadata *)objc_lookUpClass("NSString") + )); +} + +const HashableWitnessTable * +swift::hashable_support::getNSStringHashableConformance() { + return SWIFT_LAZY_CONSTANT( + reinterpret_cast( + swift_conformsToProtocol( + getNSStringMetadata(), + &HashableProtocolDescriptor + ) + ) + ); +} + #endif const ClassMetadata *swift::getRootSuperclass() { diff --git a/stdlib/public/stubs/Unicode/Common/GraphemeData.h b/stdlib/public/stubs/Unicode/Common/GraphemeData.h index 5c7b3fa81ac51..06af914d99550 100644 --- a/stdlib/public/stubs/Unicode/Common/GraphemeData.h +++ b/stdlib/public/stubs/Unicode/Common/GraphemeData.h @@ -20,7 +20,7 @@ #define GRAPHEME_BREAK_DATA_COUNT 621 -static __swift_uint32_t _swift_stdlib_graphemeBreakProperties[621] = { +static const __swift_uint32_t _swift_stdlib_graphemeBreakProperties[621] = { 0x3E00000, 0x400007F, 0x800000A9, 0xAD, 0x800000AE, 0x2DE00300, 0x20C00483, 0x25800591, 0x200005BF, 0x202005C1, 0x202005C4, 0x200005C7, 0x40A00600, 0x21400610, 0x61C, 0x2280064B, 0x20000670, 0x20C006D6, 0x400006DD, 0x20A006DF, 0x202006E7, 0x206006EA, 0x4000070F, 0x20000711, @@ -101,4 +101,41 @@ static __swift_uint32_t _swift_stdlib_graphemeBreakProperties[621] = { 0xB701F947, 0x3EE0000, 0x2BEE0020, 0xFEE0080, 0x3DEE0100, }; +static const __swift_uint16_t _swift_stdlib_linkingConsonant_ranks[165] = { + 0x0, 0xE, 0xE, 0x12, 0x13, 0x0, 0x0, 0x0, 0x25, 0x35, 0x0, 0x20, 0x25, 0x46, 0x4B, 0x0, 0x17, + 0x23, 0x3D, 0x44, 0x0, 0xA, 0x24, 0x31, 0x4B, 0x0, 0x1, 0x27, 0x27, 0x49, 0x0, 0xF, 0x2E, 0x3A, + 0x57, 0x0, 0x0, 0x1F, 0x2C, 0x2C, 0x0, 0x21, 0x2D, 0x3C, 0x3C, 0x0, 0x0, 0x0, 0xD, 0x2C, 0x0, + 0x2D, 0x30, 0x30, 0x30, 0x0, 0x0, 0x0, 0x1E, 0x31, 0x0, 0x2C, 0x2C, 0x63, 0x72, 0x0, 0x0, 0x0, + 0x29, 0x2F, 0x0, 0x26, 0x4A, 0x51, 0x51, 0x0, 0x18, 0x39, 0x54, 0x68, 0x0, 0x18, 0x2F, 0x53, 0x64, + 0x0, 0x23, 0x39, 0x69, 0x72, 0x0, 0x0, 0x0, 0xE, 0x18, 0x0, 0x0, 0xF, 0x25, 0x25, 0x0, 0x25, 0x26, + 0x49, 0x49, 0x0, 0x19, 0x37, 0x59, 0x61, 0x0, 0xC, 0x24, 0x52, 0x5D, 0x0, 0x8, 0x8, 0x8, 0x2A, + 0x0, 0x0, 0x21, 0x21, 0x21, 0x0, 0x2, 0x21, 0x23, 0x43, 0x0, 0x16, 0x22, 0x3D, 0x44, 0x0, 0x0, + 0x0, 0x22, 0x22, 0x0, 0x0, 0x0, 0x22, 0x22, 0x0, 0x22, 0x28, 0x4B, 0x73, 0x0, 0x0, 0x21, 0x21, + 0x3F, 0x0, 0x0, 0x25, 0x39, 0x43, 0x0, 0x12, 0x12, 0x12, 0x12, +}; + +static const __swift_uint64_t _swift_stdlib_linkingConsonant[166] = { + 0x5, 0x7E0FF00, 0x0, 0x3C0000000, 0x400000000000000, 0x5BFF, 0x0, 0x0, 0x3FFFFFFFFE00000, + 0xFF000000FF000000, 0x0, 0x3C5FDFFFFE0, 0x30000B000, 0x36DFDFFFFE0, 0x5E00, 0xFFE0, 0x3EDFDFF, + 0xFFE0000002000000, 0xB000000003EDFDFF, 0xD620000000020000, 0xC718, 0x3FF, 0xFDFFFFE000000000, + 0x700000003FF, 0xFDFFFFE000000000, 0x3EF, 0x40000000, 0x7FFFFFFFFE00000, 0x0, 0x2FFBFFFFFC000000, + 0x7F, 0xFFFE000000000000, 0x7FFFFFFF, 0xF7D6000000000000, 0x7FAFFFFF, 0xF000, 0x0, + 0xFFFFFEFF00000000, 0x1FFF, 0x0, 0x0, 0x1FFFFFFFF0000, 0xC0623C0300008000, 0x4003FFE1, 0x0, 0x0, + 0x0, 0x0, 0xFFF8000000000000, 0xFFF80003FFF88003, 0x3, 0xFFFFFFFF0001DFF8, 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7FFFFFFE0000, 0x7FFFF00000000, 0x0, 0xFFFFFFFFFFF, 0x0, 0xFFFFFFFF007FFFFF, 0x181FFF, + 0x0, 0x0, 0x0, 0x1FE0000FFFFFFFF8, 0xFC00000000000000, 0xFFFF, 0xFFFFFFFF3800C001, + 0xFFFFFFFF0000000F, 0xE0000000000F, 0x0, 0x0, 0xFFFFF78000000000, 0x3FFFFFFF00000007, + 0xFFFC00000005FE3C, 0xFFFFF, 0x0, 0x3FFFFFC000000, 0x7FFFFF, 0xFFFFFFFF8E000000, + 0xFF9F000000000007, 0x7C00, 0x1FFFFFFFFC0, 0xC40EFFFF00000000, 0xFFFFFFFFFFFF, 0x7FC00000000, 0x0, + 0x0, 0x0, 0x3FFF000000000000, 0x7FD, 0x0, 0x0, 0xFEEF000100000000, 0x3FFFFF, 0x0, 0x0, + 0xFFFFFFFFF80000, 0x20000000000000, 0xFFFFFFFFE000, 0x0, 0xFF80, 0x900000007FFFFF, 0x7FFFFFFE0, + 0x7FFFFFFFE, 0xFF00000000000000, 0xFFFB, 0xFFF, 0xBFFFBD7000000000, 0x7FFFFFFFFC0001FF, + 0xFFE0000000000000, 0xFDFF, 0x3ED, 0x0, 0x0, 0xFFFFFFFFC0000000, 0x1F, 0x0, 0xFFFFFFFF8000, 0x0, + 0x0, 0x0, 0xC000000000000000, 0x7FFFFFFF, 0xC000000000000000, 0xFFFFFFFF, 0x0, 0xFFFFFC0000000000, + 0x10007FF, 0x7FFFFFF00000000, 0x7F00000000, 0x0, 0x0, 0x0, 0xFFFFFFFFC000000, 0x0, 0x0, 0x0, 0x0, + 0xFFFFFF6FF000, 0x0, 0x0, 0xFFFFFFFFC0000000, 0xF800000000000001, 0x7FFFFFFFF, 0xFFFFFFFFFF000, + 0x0, 0x0, 0x7FFFFFFFC0000000, 0x0, 0xFFFFFFFC, 0x0, 0x0, 0x1FFFFFFFFF000, 0xFFFFF00000000000, + 0x3FF, 0x0, 0x3FFFF, 0x0, 0x0, 0x0, 0x0, +}; + #endif // #ifndef GRAPHEME_DATA_H diff --git a/stdlib/public/stubs/Unicode/UnicodeGrapheme.cpp b/stdlib/public/stubs/Unicode/UnicodeGrapheme.cpp index 1a16b1a7fc783..294978dae06d7 100644 --- a/stdlib/public/stubs/Unicode/UnicodeGrapheme.cpp +++ b/stdlib/public/stubs/Unicode/UnicodeGrapheme.cpp @@ -16,6 +16,7 @@ #include "swift/Runtime/Debug.h" #endif #include "../SwiftShims/UnicodeData.h" +#include SWIFT_RUNTIME_STDLIB_INTERNAL __swift_uint8_t _swift_stdlib_getGraphemeBreakProperty(__swift_uint32_t scalar) { @@ -65,3 +66,16 @@ __swift_uint8_t _swift_stdlib_getGraphemeBreakProperty(__swift_uint32_t scalar) return 0xFF; #endif } + +SWIFT_RUNTIME_STDLIB_INTERNAL +__swift_bool _swift_stdlib_isLinkingConsonant(__swift_uint32_t scalar) { + auto idx = _swift_stdlib_getScalarBitArrayIdx(scalar, + _swift_stdlib_linkingConsonant, + _swift_stdlib_linkingConsonant_ranks); + + if (idx == std::numeric_limits<__swift_intptr_t>::max()) { + return false; + } + + return true; +} diff --git a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift index 1cf8bd0a04c90..38a261775ee5d 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -requirement-machine=off -verify %s +// RUN: %target-swift-frontend -emit-sil -verify %s // Test differentiation transform diagnostics. @@ -773,27 +773,31 @@ public func fragileDifferentiable(_ x: Float) -> Float { implicitlyDifferentiableFromFragile(x) } + +// FIXME(rdar://87429620): Differentiable curry thunk RequirementMachine error. +#if false // TF-1208: Test curry thunk differentiation regression. -public struct TF_1208_Struct { +public struct SR_14228_Struct { var x: Scalar } -extension TF_1208_Struct: Differentiable where Scalar: Differentiable { +extension SR_14228_Struct: Differentiable where Scalar: Differentiable { @differentiable(reverse) public static func id(x: Self) -> Self { return x } } @differentiable(reverse, wrt: x) -public func TF_1208( - _ x: TF_1208_Struct, - // NOTE(TF-1208): This diagnostic is unexpected because `TF_1208_Struct.id` is marked `@differentiable`. - // expected-error @+3 2 {{function is not differentiable}} - // expected-note @+2 {{differentiated functions in '@inlinable' functions must be marked '@differentiable' or have a public '@derivative'; this is not possible with a closure, make a top-level function instead}} - // expected-note @+1 {{opaque non-'@differentiable' function is not differentiable}} - reduction: @differentiable(reverse) (TF_1208_Struct) -> TF_1208_Struct = TF_1208_Struct.id -) -> TF_1208_Struct { +public func SR_14228( + _ x: SR_14228_Struct, + // NOTE(TF-1208): This diagnostic is unexpected because `SR_14228_Struct.id` is marked `@differentiable`. + // xpected-error @+3 2 {{function is not differentiable}} + // xpected-note @+2 {{differentiated functions in '@inlinable' functions must be marked '@differentiable' or have a public '@derivative'; this is not possible with a closure, make a top-level function instead}} + // xpected-note @+1 {{opaque non-'@differentiable' function is not differentiable}} + reduction: @differentiable(reverse) (SR_14228_Struct) -> SR_14228_Struct = SR_14228_Struct.id +) -> SR_14228_Struct { reduction(x) } +#endif //===----------------------------------------------------------------------===// // Coroutines (SIL function yields, `begin_apply`) (not yet supported) diff --git a/test/AutoDiff/Sema/DerivedConformances/derived_differentiable.swift b/test/AutoDiff/Sema/DerivedConformances/derived_differentiable.swift index de14debfdf9cc..2e0d7b1f38e4f 100644 --- a/test/AutoDiff/Sema/DerivedConformances/derived_differentiable.swift +++ b/test/AutoDiff/Sema/DerivedConformances/derived_differentiable.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -print-ast %s | %FileCheck %s --check-prefix=CHECK-AST +// RUN: %target-swift-frontend -print-ast-decl %s | %FileCheck %s --check-prefix=CHECK-AST // RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s --check-prefix=CHECK-SIL import _Differentiation diff --git a/test/AutoDiff/compiler_crashers/rdar87429620-differentiable-curry-thunk-reqmachine.swift b/test/AutoDiff/compiler_crashers/rdar87429620-differentiable-curry-thunk-reqmachine.swift new file mode 100644 index 0000000000000..2d8d1798410ab --- /dev/null +++ b/test/AutoDiff/compiler_crashers/rdar87429620-differentiable-curry-thunk-reqmachine.swift @@ -0,0 +1,43 @@ +// RUN: %target-swift-frontend -emit-sil -verify %s +// XFAIL: * + +// rdar://87429620 (Differentiable curry thunk RequirementMachine error) + +import _Differentiation + +public struct SR_14228_Struct { + var x: Scalar +} + +extension SR_14228_Struct: Differentiable where Scalar: Differentiable { + @differentiable(reverse) + public static func id(x: Self) -> Self { + return x + } +} + +@differentiable(reverse, wrt: x) +public func SR_14228( + _ x: SR_14228_Struct, + reduction: @differentiable(reverse) (SR_14228_Struct) -> SR_14228_Struct = SR_14228_Struct.id +) -> SR_14228_Struct { + reduction(x) +} + +// Invalid type parameter in getCanonicalTypeInContext() +// Original type: τ_0_0.TangentVector +// Simplified term: τ_0_0.[Differentiable:TangentVector] +// Longest valid prefix: τ_0_0 +// Prefix type: τ_0_0 +// +// Requirement machine for <τ_0_0, τ_0_1, τ_0_2, τ_0_3 where τ_0_0 == τ_0_1, τ_0_2 == τ_0_3> +// Rewrite system: { +// - τ_0_1 => τ_0_0 [explicit] +// - τ_0_3 => τ_0_2 [explicit] +// } +// Rewrite loops: { +// } +// Property map: { +// } +// Conformance access paths: { +// } diff --git a/test/Concurrency/sendable_conformance_checking.swift b/test/Concurrency/sendable_conformance_checking.swift index e3d92f6681f61..5d7349f546f27 100644 --- a/test/Concurrency/sendable_conformance_checking.swift +++ b/test/Concurrency/sendable_conformance_checking.swift @@ -145,3 +145,8 @@ actor A10: AsyncThrowingProtocolWithNotSendable { } } } + +// rdar://86653457 - Crash due to missing Sendable conformances. +class Klass: Sendable {} +final class SubKlass: Klass<[S]> {} +public struct S {} diff --git a/test/Constraints/bridging.swift b/test/Constraints/bridging.swift index aa81c84539699..3825993193dc6 100644 --- a/test/Constraints/bridging.swift +++ b/test/Constraints/bridging.swift @@ -401,7 +401,7 @@ func SR15161_as(e: Error?) { } func SR15161_is(e: Error?) { - _ = e is NSError // expected-warning{{checking a value with optional type 'Error?' against dynamic type 'NSError' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} + _ = e is NSError // expected-warning{{checking a value with optional type 'Error?' against type 'NSError' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} } func SR15161_as_1(e: Error?) { diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 288d9fe65657e..42616c02e36bc 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -95,8 +95,8 @@ func r21544303() { inSubcall = false // This is a problem, but isn't clear what was intended. - var somethingElse = true { - } // expected-error {{computed property must have accessors specified}} + var somethingElse = true { // expected-error {{unexpected '{' in declaration}} + } inSubcall = false var v2 : Bool = false diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall.swift b/test/Distributed/Runtime/distributed_actor_remoteCall.swift index 0ccf737e33b67..c289369452671 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall.swift @@ -18,12 +18,37 @@ import _Distributed final class Obj: @unchecked Sendable, Codable {} + struct LargeStruct: Sendable, Codable { + var q: String + var a: Int + var b: Int64 + var c: Double + var d: String +} + +enum E : Sendable, Codable { + case foo, bar } distributed actor Greeter { - distributed func hello() { - print("EXECUTING HELLO") + distributed func empty() { + } + + distributed func hello() -> String { + return "Hello, World!" + } + + distributed func answer() -> Int { + return 42 + } + + distributed func largeResult() -> LargeStruct { + .init(q: "question", a: 42, b: 1, c: 2.0, d: "Lorum ipsum") + } + + distributed func enumResult() -> E { + .bar } } @@ -102,7 +127,11 @@ struct FakeResultHandler: DistributedTargetInvocationResultHandler { typealias DefaultDistributedActorSystem = FakeActorSystem // actual mangled name: -let helloName = "$s4main7GreeterC5helloyyFTE" +let emptyName = "$s4main7GreeterC5emptyyyFTE" +let helloName = "$s4main7GreeterC5helloSSyFTE" +let answerName = "$s4main7GreeterC6answerSiyFTE" +let largeResultName = "$s4main7GreeterC11largeResultAA11LargeStructVyFTE" +let enumResult = "$s4main7GreeterC10enumResultAA1EOyFTE" func test() async throws { let system = FakeActorSystem() @@ -112,6 +141,15 @@ func test() async throws { // act as if we decoded an Invocation: let invocation = FakeInvocation() + try await system.executeDistributedTarget( + on: local, + mangledTargetName: emptyName, + invocation: invocation, + handler: FakeResultHandler() + ) + + // CHECK: RETURN: () + try await system.executeDistributedTarget( on: local, mangledTargetName: helloName, @@ -119,7 +157,35 @@ func test() async throws { handler: FakeResultHandler() ) - // CHECK: done + // CHECK: RETURN: Hello, World! + + try await system.executeDistributedTarget( + on: local, + mangledTargetName: answerName, + invocation: invocation, + handler: FakeResultHandler() + ) + + // CHECK: RETURN: 42 + + try await system.executeDistributedTarget( + on: local, + mangledTargetName: largeResultName, + invocation: invocation, + handler: FakeResultHandler() + ) + + // CHECK: RETURN: LargeStruct(q: "question", a: 42, b: 1, c: 2.0, d: "Lorum ipsum") + + try await system.executeDistributedTarget( + on: local, + mangledTargetName: enumResult, + invocation: invocation, + handler: FakeResultHandler() + ) + // CHECK: RETURN: bar + + // CHECK-NEXT: done print("done") } diff --git a/test/IRGen/opaque_result_type.swift b/test/IRGen/opaque_result_type.swift index 30067b50a4635..adb5cf9e6a682 100644 --- a/test/IRGen/opaque_result_type.swift +++ b/test/IRGen/opaque_result_type.swift @@ -253,3 +253,25 @@ public enum EnumWithTupleWithOpaqueField { case a(Int) case b((OpaqueProps, String)) } + +// rdar://86800325 - Make sure we don't crash on result builders. +@resultBuilder +struct Builder { + static func buildBlock(_: Any...) -> Int { 5 } +} + +protocol P2 { + associatedtype A: P2 + + @Builder var builder: A { get } +} + +extension Int: P2 { + var builder: some P2 { 5 } +} + +struct UseBuilder: P2 { + var builder: some P2 { + let extractedExpr: some P2 = 5 + } +} diff --git a/test/Interop/lit.local.cfg b/test/Interop/lit.local.cfg new file mode 100644 index 0000000000000..9f409a91b9574 --- /dev/null +++ b/test/Interop/lit.local.cfg @@ -0,0 +1,14 @@ +# Make a local copy of the substitutions. +config.substitutions = list(config.substitutions) + +def get_target_os(): + import re + (run_cpu, run_vendor, run_os, run_version) = re.match('([^-]+)-([^-]+)-([^0-9]+)(.*)', config.variant_triple).groups() + return run_os + +if get_target_os() in ['windows-msvc']: + config.substitutions.insert(0, ('%target-abi', 'WIN')) +else: + # FIXME(compnerd) do all the targets we currently support use SysV ABI? + config.substitutions.insert(0, ('%target-abi', 'SYSV')) + diff --git a/test/Parse/omit_return.swift b/test/Parse/omit_return.swift index 870dbfc78d39d..8a84b33bcfe0b 100644 --- a/test/Parse/omit_return.swift +++ b/test/Parse/omit_return.swift @@ -545,10 +545,10 @@ func ff_implicitMemberAccessEnumCase() -> Unit { var fv_nop: () { -} // expected-error {{computed property must have accessors specified}} +} // expected-error {{missing return in accessor expected to return '()'}} var fv_missing: String { -} // expected-error {{computed property must have accessors specified}} +} // expected-error {{missing return in accessor expected to return 'String'}} var fv_implicit: String { "hello" @@ -1054,12 +1054,12 @@ var fvs_optionalTryImplicit: String? { enum S_nop { subscript() -> () { - } // expected-error {{subscript must have accessors specified}} + } // expected-error {{missing return in subscript expected to return '()'}} } enum S_missing { subscript() -> String { - } // expected-error {{subscript must have accessors specified}} + } // expected-error {{missing return in subscript expected to return 'String'}} } enum S_implicit { diff --git a/test/SILGen/local_decl_after_unreachable.swift b/test/SILGen/local_decl_after_unreachable.swift new file mode 100644 index 0000000000000..61c37ebff7a08 --- /dev/null +++ b/test/SILGen/local_decl_after_unreachable.swift @@ -0,0 +1,16 @@ + +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// CHECK-LABEL: sil {{.*}} @${{.*}}3foo{{.*}}F : {{.*}} { +func foo() { + return bar(Baz()) + + struct Baz { + // CHECK-LABEL: sil {{.*}} @{{.*}}3foo{{.*}}3Baz{{.*}}C : {{.*}} { + init() {} + } + + // CHECK-LABEL: sil {{.*}} @{{.*}}3foo{{.*}}3bar{{.*}}F : {{.*}} { + func bar(_: Any) {} + +} diff --git a/test/SILGen/moveonly_builtin.swift b/test/SILGen/moveonly_builtin.swift index 1cc0e55fde36b..11068328d4333 100644 --- a/test/SILGen/moveonly_builtin.swift +++ b/test/SILGen/moveonly_builtin.swift @@ -1,17 +1,18 @@ // RUN: %target-swift-emit-silgen -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-experimental-move-only | %FileCheck %s // RUN: %target-swift-emit-sil -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-experimental-move-only | %FileCheck -check-prefix=CHECK-SIL %s -// REQUIRES: rdar84780237 +// REQUIRES: optimized_stdlib import Swift class Klass {} -// CHECK-LABEL: sil hidden [ossa] @$s8moveonly7useMoveyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass { +// CHECK-LABEL: sil hidden [ossa] @$s8moveonly7useMoveyAA5KlassCADnF : $@convention(thin) (@owned Klass) -> @owned Klass { // CHECK: bb0([[ARG:%.*]] : +// CHECK-NEXT: [[BORROWED_ARG:%.*]] = begin_borrow [lexical] [[ARG]] // CHECK-NEXT: debug_value // CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $Klass -// CHECK-NEXT: [[ARC_COPY:%.*]] = copy_value [[ARG]] +// CHECK-NEXT: [[ARC_COPY:%.*]] = copy_value [[BORROWED_ARG]] // CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $Klass // CHECK-NEXT: store [[ARC_COPY]] to [init] [[INPUT_ADDR]] // CHECK-NEXT: // function_ref _move(_:) @@ -20,26 +21,31 @@ class Klass {} // CHECK-NEXT: dealloc_stack [[INPUT_ADDR]] // CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]] // CHECK-NEXT: dealloc_stack [[RESULT_ADDR]] +// CHECK-NEXT: end_borrow [[BORROWED_ARG]] +// CHECK-NEXT: destroy_value [[ARG]] // CHECK-NEXT: return [[RELOADED_VALUE]] -// CHECK: } // end sil function '$s8moveonly7useMoveyAA5KlassCADF' +// CHECK: } // end sil function '$s8moveonly7useMoveyAA5KlassCADnF' -// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyAA5KlassCADF : $@convention(thin) (@guaranteed Klass) -> @owned Klass { +// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyAA5KlassCADnF : $@convention(thin) (@owned Klass) -> @owned Klass { // CHECK-SIL: bb0([[ARG:%.*]] : // CHECK-SIL-NEXT: debug_value // CHECK-SIL-NEXT: strong_retain -// CHECK-SIL-NEXT: [allows_diagnostics] move_value +// CHECK-SIL-NEXT: move_value // CHECK-SIL-NEXT: tuple +// CHECK-SIL-NEXT: tuple +// CHECK-SIL-NEXT: strong_release // CHECK-SIL-NEXT: return -// CHECK-SIL: } // end sil function '$s8moveonly7useMoveyAA5KlassCADF' -func useMove(_ k: Klass) -> Klass { +// CHECK-SIL: } // end sil function '$s8moveonly7useMoveyAA5KlassCADnF' +func useMove(_ k: __owned Klass) -> Klass { _move(k) } -// CHECK-LABEL: sil hidden [ossa] @$s8moveonly7useMoveyxxRlzClF : $@convention(thin) (@guaranteed T) -> @owned T { +// CHECK-LABEL: sil hidden [ossa] @$s8moveonly7useMoveyxxnRlzClF : $@convention(thin) (@owned T) -> @owned T { // CHECK: bb0([[ARG:%.*]] : +// CHECK-NEXT: [[BORROWED_ARG:%.*]] = begin_borrow [lexical] [[ARG]] // CHECK-NEXT: debug_value // CHECK-NEXT: [[RESULT_ADDR:%.*]] = alloc_stack $T -// CHECK-NEXT: [[ARC_COPY:%.*]] = copy_value [[ARG]] +// CHECK-NEXT: [[ARC_COPY:%.*]] = copy_value [[BORROWED_ARG]] // CHECK-NEXT: [[INPUT_ADDR:%.*]] = alloc_stack $T // CHECK-NEXT: store [[ARC_COPY]] to [init] [[INPUT_ADDR]] // CHECK-NEXT: // function_ref _move(_:) @@ -48,17 +54,21 @@ func useMove(_ k: Klass) -> Klass { // CHECK-NEXT: dealloc_stack [[INPUT_ADDR]] // CHECK-NEXT: [[RELOADED_VALUE:%.*]] = load [take] [[RESULT_ADDR]] // CHECK-NEXT: dealloc_stack [[RESULT_ADDR]] +// CHECK-NEXT: end_borrow [[BORROWED_ARG]] +// CHECK-NEXT: destroy_value [[ARG]] // CHECK-NEXT: return [[RELOADED_VALUE]] -// CHECK: } // end sil function '$s8moveonly7useMoveyxxRlzClF' +// CHECK: } // end sil function '$s8moveonly7useMoveyxxnRlzClF' -// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyxxRlzClF : $@convention(thin) (@guaranteed T) -> @owned T { +// CHECK-SIL-LABEL: sil hidden @$s8moveonly7useMoveyxxnRlzClF : $@convention(thin) (@owned T) -> @owned T { // CHECK-SIL: bb0([[ARG:%.*]] : // CHECK-SIL-NEXT: debug_value // CHECK-SIL-NEXT: strong_retain -// CHECK-SIL-NEXT: [allows_diagnostics] move_value +// CHECK-SIL-NEXT: move_value +// CHECK-SIL-NEXT: tuple // CHECK-SIL-NEXT: tuple +// CHECK-SIL-NEXT: strong_release // CHECK-SIL-NEXT: return -// CHECK-SIL: } // end sil function '$s8moveonly7useMoveyxxRlzClF' -func useMove(_ k: T) -> T { +// CHECK-SIL: } // end sil function '$s8moveonly7useMoveyxxnRlzClF' +func useMove(_ k: __owned T) -> T { _move(k) } diff --git a/test/SILOptimizer/move_function_kills_copyable_addressonly_vars.swift b/test/SILOptimizer/move_function_kills_copyable_addressonly_vars.swift index c750ea8c5e445..4b311ec14ed51 100644 --- a/test/SILOptimizer/move_function_kills_copyable_addressonly_vars.swift +++ b/test/SILOptimizer/move_function_kills_copyable_addressonly_vars.swift @@ -631,3 +631,62 @@ extension MiscTests { } } +////////////////////////////////// +// Multiple Captures from Defer // +////////////////////////////////// + +func multipleCapture1(_ k: T) -> () { + let kType = type(of: k) + var k2 = k + var k3 = k + let _ = _move(k2) + let _ = _move(k3) + var k4 = k + k4 = k + defer { + k2 = kType.getP() + print(k4) + k3 = kType.getP() + } + print("foo bar") +} + +func multipleCapture2(_ k: T) -> () { + let kType = type(of: k) + var k2 = k // expected-error {{'k2' used after being moved}} + k2 = k + var k3 = k + let _ = _move(k2) // expected-note {{move here}} + let _ = _move(k3) + var k4 = k + k4 = k + defer { + print(k2) // expected-note {{use here}} + print(k4) + k3 = kType.getP() + } + print("foo bar") +} + +////////////////////// +// Reinit in pieces // +////////////////////// + +// These tests exercise the diagnostic to see how we error if we re-initialize a +// var in pieces. Eventually we should teach either this diagnostic pass how to +// handle this or teach DI how to combine the initializations into one large +// reinit. +struct ProtPair { + var lhs: T + var rhs: T +} + +func reinitInPieces1(_ k: ProtPair) { + let selfType = type(of: k.lhs) + var k2 = k + k2 = k + + let _ = _move(k2) // expected-error {{_move applied to value that the compiler does not support checking}} + k2.lhs = selfType.getP() + k2.rhs = selfType.getP() +} diff --git a/test/SILOptimizer/move_function_kills_copyable_loadable_vars.swift b/test/SILOptimizer/move_function_kills_copyable_loadable_vars.swift index 13de90e6306fe..942037b8f66ba 100644 --- a/test/SILOptimizer/move_function_kills_copyable_loadable_vars.swift +++ b/test/SILOptimizer/move_function_kills_copyable_loadable_vars.swift @@ -674,3 +674,60 @@ extension KlassWrapper { print("foo bar") } } + +////////////////////////////////// +// Multiple Captures from Defer // +////////////////////////////////// + +func multipleCapture1(_ k: Klass) -> () { + var k2 = k + var k3 = k + let _ = _move(k2) + let _ = _move(k3) + var k4 = k + k4 = k + defer { + k2 = Klass() + print(k4) + k3 = Klass() + } + print("foo bar") +} + +func multipleCapture2(_ k: Klass) -> () { + var k2 = k // expected-error {{'k2' used after being moved}} + k2 = k + var k3 = k + let _ = _move(k2) // expected-note {{move here}} + let _ = _move(k3) + var k4 = k + k4 = k + defer { + print(k2) // expected-note {{use here}} + print(k4) + k3 = Klass() + } + print("foo bar") +} + +////////////////////// +// Reinit in pieces // +////////////////////// + +// These tests exercise the diagnostic to see how we error if we re-initialize a +// var in pieces. Eventually we should teach either this diagnostic pass how to +// handle this or teach DI how to combine the initializations into one large +// reinit. +struct KlassPair { + var lhs: Klass + var rhs: Klass +} + +func reinitInPieces1(_ k: KlassPair) { + var k2 = k + k2 = k + + let _ = _move(k2) // expected-error {{_move applied to value that the compiler does not support checking}} + k2.lhs = Klass() + k2.rhs = Klass() +} diff --git a/test/Sema/Inputs/public-private-sdk/System/Library/PrivateFrameworks/MainLib.framework/Headers/MainLib.h b/test/Sema/Inputs/public-private-sdk/System/Library/PrivateFrameworks/MainLib.framework/Headers/MainLib.h new file mode 100644 index 0000000000000..e42599986781f --- /dev/null +++ b/test/Sema/Inputs/public-private-sdk/System/Library/PrivateFrameworks/MainLib.framework/Headers/MainLib.h @@ -0,0 +1 @@ +void bar() {} diff --git a/test/Sema/Inputs/public-private-sdk/System/Library/PrivateFrameworks/MainLib.framework/Modules/module.modulemap b/test/Sema/Inputs/public-private-sdk/System/Library/PrivateFrameworks/MainLib.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..bca8788930dee --- /dev/null +++ b/test/Sema/Inputs/public-private-sdk/System/Library/PrivateFrameworks/MainLib.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module MainLib { + umbrella header "MainLib.h" + + export * + module * { export * } +} diff --git a/test/Sema/diag_constantness_check.swift b/test/Sema/diag_constantness_check.swift index f03830d7adaa5..2969467d80c99 100644 --- a/test/Sema/diag_constantness_check.swift +++ b/test/Sema/diag_constantness_check.swift @@ -424,3 +424,32 @@ func testCallsWithinClosures(s: String, x: Int) { constantArgumentFunction("string with a single interpolation \(x)") } } + +@resultBuilder +struct MyArrayBuilder { + typealias Component = [Int] + typealias Expression = Int + static func buildExpression(_ element: Expression) -> Component { + return [element] + } + static func buildBlock(_ components: Component...) -> Component { + return Array(components.joined()) + } +} + +struct MyArray { + public init(@MyArrayBuilder arr: () -> [Int]) {} +} + +func testResultBuilder(x: Int, y: Int) -> MyArray { + let _: MyArray = MyArray { + constantArgumentFunctionReturningInt(x) + // expected-error@-1 {{argument must be an integer literal}} + constantArgumentFunctionReturningInt(y) + // expected-error@-1 {{argument must be an integer literal}} + } + let _: MyArray = MyArray { + constantArgumentFunctionReturningInt(x) + // expected-error@-1 {{argument must be an integer literal}} + } +} diff --git a/test/Sema/implementation-only-import-suggestion-as-error.swift b/test/Sema/implementation-only-import-suggestion-as-error.swift index b7675bf054194..d75eaa031e7af 100644 --- a/test/Sema/implementation-only-import-suggestion-as-error.swift +++ b/test/Sema/implementation-only-import-suggestion-as-error.swift @@ -17,12 +17,13 @@ // RUN: env ENABLE_PUBLIC_IMPORT_OF_PRIVATE_AS_ERROR=1 \ // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ -// RUN: -library-level api -verify +// RUN: -library-level api -verify -module-name MainLib import PublicSwift -import PrivateSwift // expected-error{{private module 'PrivateSwift' is imported publicly from the public module 'main'}} +import PrivateSwift // expected-error{{private module 'PrivateSwift' is imported publicly from the public module 'MainLib'}} import PublicClang -import PublicClang_Private // expected-error{{private module 'PublicClang_Private' is imported publicly from the public module 'main'}} -import FullyPrivateClang // expected-error{{private module 'FullyPrivateClang' is imported publicly from the public module 'main'}} -import main // expected-warning{{'implementation-only-import-suggestion-as-error.swift' is part of module 'main'; ignoring import}} +import PublicClang_Private // expected-error{{private module 'PublicClang_Private' is imported publicly from the public module 'MainLib'}} +import FullyPrivateClang // expected-error{{private module 'FullyPrivateClang' is imported publicly from the public module 'MainLib'}} + +@_exported import MainLib // expected-warning{{private module 'MainLib' is imported publicly from the public module 'MainLib'}} diff --git a/test/Sema/implementation-only-import-suggestion.swift b/test/Sema/implementation-only-import-suggestion.swift index 09240e1a50b7a..890e736700a3d 100644 --- a/test/Sema/implementation-only-import-suggestion.swift +++ b/test/Sema/implementation-only-import-suggestion.swift @@ -14,33 +14,33 @@ /// Expect warnings when building a public client. // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ -// RUN: -library-level api -verify -D PUBLIC_IMPORTS +// RUN: -library-level api -verify -D PUBLIC_IMPORTS -module-name MainLib /// Expect no warnings when building an SPI client. // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ -// RUN: -library-level spi -D PUBLIC_IMPORTS +// RUN: -library-level spi -D PUBLIC_IMPORTS -module-name MainLib /// The driver should also accept the flag and pass it along. // RUN: %target-swiftc_driver -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ -// RUN: -library-level spi -D PUBLIC_IMPORTS +// RUN: -library-level spi -D PUBLIC_IMPORTS -module-name MainLib /// Expect no warnings when building a client with some other library level. // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ -// RUN: -D PUBLIC_IMPORTS +// RUN: -D PUBLIC_IMPORTS -module-name MainLib // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ // RUN: -F %t/sdk/System/Library/PrivateFrameworks/ \ -// RUN: -library-level other -D PUBLIC_IMPORTS +// RUN: -library-level other -D PUBLIC_IMPORTS -module-name MainLib #if PUBLIC_IMPORTS import PublicSwift -import PrivateSwift // expected-error{{private module 'PrivateSwift' is imported publicly from the public module 'main'}} +import PrivateSwift // expected-error{{private module 'PrivateSwift' is imported publicly from the public module 'MainLib'}} import PublicClang -import PublicClang_Private // expected-error{{private module 'PublicClang_Private' is imported publicly from the public module 'main'}} -import FullyPrivateClang // expected-error{{private module 'FullyPrivateClang' is imported publicly from the public module 'main'}} -import main // expected-warning{{'implementation-only-import-suggestion.swift' is part of module 'main'; ignoring import}} +import PublicClang_Private // expected-error{{private module 'PublicClang_Private' is imported publicly from the public module 'MainLib'}} +import FullyPrivateClang // expected-error{{private module 'FullyPrivateClang' is imported publicly from the public module 'MainLib'}} +@_exported import MainLib // expected-warning{{private module 'MainLib' is imported publicly from the public module 'MainLib'}} /// Expect no warnings with implementation-only imports. // RUN: %target-swift-frontend -typecheck -sdk %t/sdk -module-cache-path %t %s \ diff --git a/test/Sema/struct_equatable_hashable_access.swift b/test/Sema/struct_equatable_hashable_access.swift index 8672c985f475d..4658abfbae3ef 100644 --- a/test/Sema/struct_equatable_hashable_access.swift +++ b/test/Sema/struct_equatable_hashable_access.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s +// RUN: %target-swift-frontend -print-ast-decl %s 2>&1 | %FileCheck %s // Check that synthesized members show up as 'fileprivate', not 'private. diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index 6689695102b62..89a12f2f7cf1f 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -443,8 +443,7 @@ func h(_ x : T) { } func i(_ x : T?) -> Bool { return x is P1 - // FIXME: Bogus diagnostic. See SR-11920. - // expected-warning@-2 {{checking a value with optional type 'T?' against dynamic type 'P1' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} + // expected-warning@-1 {{checking a value with optional type 'T?' against type 'P1' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} } func j(_ x : C1) -> Bool { return x is P1 diff --git a/test/decl/subscript/subscripting.swift b/test/decl/subscript/subscripting.swift index 667f8e511ca42..eaca1b180fb8f 100644 --- a/test/decl/subscript/subscripting.swift +++ b/test/decl/subscript/subscripting.swift @@ -222,7 +222,7 @@ struct RetOverloadedSubscript { struct MissingGetterSubscript1 { subscript (i : Int) -> Int { - } // expected-error {{subscript must have accessors specified}} + } // expected-error {{missing return in subscript expected to return 'Int'}} } struct MissingGetterSubscript2 { subscript (i : Int, j : Int) -> Int { @@ -230,6 +230,12 @@ struct MissingGetterSubscript2 { } } +struct MissingReturnTypeAndEmptyBodySubscript { + subscript(i: Int) { // expected-error{{expected '->' for subscript element type}} + // expected-error@-1{{expected subscripting element type}} + } +} + func test_subscript(_ x2: inout X2, i: Int, j: Int, value: inout Int, no: NoSubscript, ovl: inout OverloadedSubscript, ret: inout RetOverloadedSubscript) { no[i] = value // expected-error{{value of type 'NoSubscript' has no subscripts}} diff --git a/test/decl/var/properties.swift b/test/decl/var/properties.swift index 185168cb953d4..1e3907251f100 100644 --- a/test/decl/var/properties.swift +++ b/test/decl/var/properties.swift @@ -378,12 +378,12 @@ var x12: X { } } -var x13: X {} // expected-error {{computed property must have accessors specified}} +var x13: X {} // expected-error {{missing return in accessor expected to return 'X'}} struct X14 {} extension X14 { var x14: X { - } // expected-error {{computed property must have accessors specified}} + } // expected-error {{missing return in accessor expected to return 'X'}} } // Type checking problems @@ -1340,3 +1340,12 @@ class LazyPropInClass { lazy var foo: Int = { return 0 } // expected-error {{function produces expected type 'Int'; did you mean to call it with '()'?}} // expected-note@-1 {{Remove '=' to make 'foo' a computed property}}{{21-23=}}{{3-8=}} } + +// SR-15657 +enum SR15657 { + var foo: Int {} // expected-error{{missing return in accessor expected to return 'Int'}} +} + +enum SR15657_G { + var foo: T {} // expected-error{{missing return in accessor expected to return 'T'}} +} diff --git a/test/decl/var/result_builders.swift b/test/decl/var/result_builders.swift index bb74051ca867c..f34fe5f5549ee 100644 --- a/test/decl/var/result_builders.swift +++ b/test/decl/var/result_builders.swift @@ -21,7 +21,7 @@ var global: Int // FIXME: should this be allowed? @Maker var globalWithEmptyImplicitGetter: Int {} -// expected-error@-1 {{computed property must have accessors specified}} +// expected-error@-1 {{missing return in accessor expected to return 'Int'}} // expected-error@-3 {{result builder attribute 'Maker' can only be applied to a variable if it defines a getter}} @Maker diff --git a/test/expr/cast/bridged.swift b/test/expr/cast/bridged.swift index 6fc0329ba4f53..3d255c1686359 100644 --- a/test/expr/cast/bridged.swift +++ b/test/expr/cast/bridged.swift @@ -85,8 +85,8 @@ func testBridgeDowncastExact(_ obj: BridgedClass, objOpt: BridgedClass?, _ = objImplicitOpt as! BridgedStruct // expected-warning{{forced cast from 'BridgedClass?' to 'BridgedStruct' only unwraps and bridges; did you mean to use '!' with 'as'?}}{{21-21=!}}{{22-25=as}} _ = obj is BridgedStruct // expected-warning{{'is' test is always true}} - _ = objOpt is BridgedStruct // expected-warning{{checking a value with optional type 'BridgedClass?' against dynamic type 'BridgedStruct' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{14-30=!= nil}} - _ = objImplicitOpt is BridgedStruct // expected-warning{{checking a value with optional type 'BridgedClass?' against dynamic type 'BridgedStruct' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{22-38=!= nil}} + _ = objOpt is BridgedStruct // expected-warning{{checking a value with optional type 'BridgedClass?' against type 'BridgedStruct' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{14-30=!= nil}} + _ = objImplicitOpt is BridgedStruct // expected-warning{{checking a value with optional type 'BridgedClass?' against type 'BridgedStruct' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{22-38=!= nil}} } func testExplicitBridging(_ object: BridgedClass, value: BridgedStruct) { diff --git a/test/expr/cast/optional.swift b/test/expr/cast/optional.swift index 3e1571b8e1688..e5b9236720b86 100644 --- a/test/expr/cast/optional.swift +++ b/test/expr/cast/optional.swift @@ -26,8 +26,8 @@ func f1(i: Int?, ii: Int??, a: [Base]?, d: [Base : Base]?, de: Derived?) { _ = de as? Base // expected-warning{{conditional downcast from 'Derived?' to 'Base' is equivalent to an implicit conversion to an optional 'Base'}}{{9-18=}} // "is" checks - _ = i is Int // expected-warning{{checking a value with optional type 'Int?' against dynamic type 'Int' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{9-15=!= nil}} - _ = i..<1 is Int // expected-warning{{checking a value with optional type 'Int?' against dynamic type 'Int' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{7-7=(}}{{12-12=)}}{{13-19=!= nil}} + _ = i is Int // expected-warning{{checking a value with optional type 'Int?' against type 'Int' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{9-15=!= nil}} + _ = i..<1 is Int // expected-warning{{checking a value with optional type 'Int?' against type 'Int' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}}{{7-7=(}}{{12-12=)}}{{13-19=!= nil}} _ = ii is Int _ = i..<1 is Bool // expected-warning{{cast from 'Int?' to unrelated type 'Bool' always fails}} diff --git a/test/expr/print/callexpr.swift b/test/expr/print/callexpr.swift new file mode 100644 index 0000000000000..71b80c63dadcb --- /dev/null +++ b/test/expr/print/callexpr.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s + +func test(a: Int, b: Int = 0, c: Int = 1) { } + +test(a: 0) +test(a: 0, b: 0) +test(a: 0, b: 0, c: 0) +// CHECK: test(a: 0) +// CHECK: test(a: 0, b: 0) +// CHECK: test(a: 0, b: 0, c: 0) diff --git a/test/expr/print/conditions.swift b/test/expr/print/conditions.swift new file mode 100644 index 0000000000000..8352e0aae68e0 --- /dev/null +++ b/test/expr/print/conditions.swift @@ -0,0 +1,38 @@ +// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s + +if (5 + 5) == 10 { +} +// CHECK: if (5 + 5) == 10 { +// CHECK: } + +if (5 + 5) == 9 { +} else if (5 + 5) == 10 { +} else { +} +// CHECK: if (5 + 5) == 9 { +// CHECK: } else if (5 + 5) == 10 { +// CHECK: } else { +// CHECK: } + +guard (5 + 5) == 10 else { +} +// CHECK: guard (5 + 5) == 10 else { +// CHECK: } + +var a = 0 +// CHECK: @_hasInitialValue internal var a: Int = 0 +// Note: the AST doesn't store whitespace, +// so the output doesn't always match the input. +while a < 10 { a += 1 } +// CHECK: while a < 10 { +// CHECK: a += 1 +// CHECK: } + +var b = 0 +repeat { + b += 1 +} while b < 10 +// CHECK: @_hasInitialValue internal var b: Int = 0 +// CHECK: repeat { +// CHECK: b += 1 +// CHECK: } while b < 10 diff --git a/test/expr/print/literals.swift b/test/expr/print/literals.swift new file mode 100644 index 0000000000000..272d0ff79128a --- /dev/null +++ b/test/expr/print/literals.swift @@ -0,0 +1,18 @@ +// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s + +// CHECK-LABEL: func test() { +func test() { + log(1) + log(1.0) + log(true) + log([1, 2, 3]) + log([1: true, 2: false]) +} +// CHECK: log(1) +// CHECK: log(1.0) +// CHECK: log(true) +// CHECK: log([1, 2, 3]) +// CHECK: log([1: true, 2: false]) + +func log(_ a: Any) { +} diff --git a/test/expr/print/protocol.swift b/test/expr/print/protocol.swift new file mode 100644 index 0000000000000..f8b9fdb8a5ac7 --- /dev/null +++ b/test/expr/print/protocol.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s + +protocol Archivable { + func archive(version: String) +} +// CHECK: internal protocol Archivable { +// CHECK: func archive(version: String) +// CHECK: } + +func archive(_ a: Archivable) { + a.archive(version: "1") +} +// CHECK: internal func archive(_ a: Archivable) { +// CHECK: a.archive(version: "1") +// CHECK: } + +func cast(_ a: Any) { + let conditional = a as? Archivable + let forced = a as! Archivable +} +// CHECK: internal func cast(_ a: Any) { +// CHECK: @_hasInitialValue private let conditional: Archivable? = a as? Archivable +// CHECK: @_hasInitialValue private let forced: Archivable = a as! Archivable +// CHECK: } diff --git a/test/expr/print/switch.swift b/test/expr/print/switch.swift new file mode 100644 index 0000000000000..7d2d69f874930 --- /dev/null +++ b/test/expr/print/switch.swift @@ -0,0 +1,58 @@ +// RUN: %target-swift-frontend -print-ast %s 2>&1 | %FileCheck %s + +enum Payload { + case int(Int) + case keyValue(String, Int) + case empty +} + +// CHECK-LABEL: internal func test(payload: Payload) -> Int? { +func test(payload: Payload) -> Int? { + switch payload { + case .int(let int): + return int + case .keyValue(_, let int): + return int + case .empty: + return nil + } +} +// CHECK-LABEL: switch payload { +// CHECK-LABEL: case .int(let int): +// CHECK-LABEL: return int +// CHECK-LABEL: case .keyValue(_, let int): +// CHECK-LABEL: return int +// CHECK-LABEL: case .empty: +// CHECK-LABEL: return nil +// CHECK-LABEL: } +// CHECK-LABEL:} + +func process(payload: Payload) { + if case .empty = payload { + return + } + _ = test(payload: payload) +} +// CHECK-LABEL: internal func process(payload: Payload) { +// CHECK-LABEL: if .empty = payload { +// CHECK-LABEL: return +// CHECK-LABEL: } +// CHECK-LABEL: _ = test(payload: payload) +// CHECK-LABEL: } + +func foo(_ x: Int?) { + switch x { + case let x?: + break + case nil: + break + } +} +// CHECK-LABEL: internal func foo(_ x: Int?) { +// CHECK-LABEL: switch x { +// CHECK-LABEL: case let x?: +// CHECK-LABEL: break +// CHECK-LABEL: case .none: +// CHECK-LABEL: break +// CHECK-LABEL: } +// CHECK-LABEL: } diff --git a/test/stdlib/Character.swift b/test/stdlib/Character.swift index 9c5b47d5b25b2..15fe827e10ded 100644 --- a/test/stdlib/Character.swift +++ b/test/stdlib/Character.swift @@ -359,6 +359,7 @@ UnicodeScalarTests.test("UInt8(ascii: UnicodeScalar)") { } #if !os(WASI) +// Trap tests aren't available on WASI. UnicodeScalarTests.test("UInt8(ascii: UnicodeScalar)/non-ASCII should trap") .skip(.custom( { _isFastAssertConfiguration() }, diff --git a/test/stdlib/Error.swift b/test/stdlib/Error.swift index 885c90b32398e..7cb3295315e02 100644 --- a/test/stdlib/Error.swift +++ b/test/stdlib/Error.swift @@ -119,6 +119,7 @@ ErrorTests.test("default domain and code") { enum SillyError: Error { case JazzHands } #if !os(WASI) +// Trap tests aren't available on WASI. ErrorTests.test("try!") .skip(.custom({ _isFastAssertConfiguration() }, reason: "trap is not guaranteed to happen in -Ounchecked")) diff --git a/test/stdlib/FloatingPoint.swift.gyb b/test/stdlib/FloatingPoint.swift.gyb index 7f03083768d2e..84953ac355c6c 100644 --- a/test/stdlib/FloatingPoint.swift.gyb +++ b/test/stdlib/FloatingPoint.swift.gyb @@ -332,6 +332,7 @@ FloatingPoint.test("Float/Int16") { } #if !os(WASI) +// Trap tests aren't available on WASI. FloatingPoint.test("Float/UInt32") { expectEqual(UInt32.min, UInt32(Float(UInt32.min))) expectCrashLater() @@ -422,6 +423,7 @@ FloatingPoint.test("Double/Int32") { } #if !os(WASI) +// Trap tests aren't available on WASI. FloatingPoint.test("Double/UInt64") { expectEqual(UInt64.min, UInt64(Double(UInt64.min))) expectCrashLater() @@ -1179,8 +1181,8 @@ FloatingPoint.test("Float32/quietNaN") { expectTrue(f.isNaN && !f.isSignalingNaN) expectEqual(0x7fdf_ffff, f.bitPattern) } - #if !os(WASI) +// Trap tests aren't available on WASI. do { // Payload overflow expectCrashLater() @@ -1212,6 +1214,7 @@ FloatingPoint.test("Float64/quietNaN") { expectEqual(0x7ffb_ffff_ffff_ffff, f.bitPattern) } #if !os(WASI) +// Trap tests aren't available on WASI. do { // Payload overflow expectCrashLater() @@ -1245,6 +1248,7 @@ FloatingPoint.test("Float80/quietNaN") { expectEqual(Float80Bits(0x7fff, 0xdfff_ffff_ffff_ffff), f.bitPattern) } #if !os(WASI) +// Trap tests aren't available on WASI. do { // Payload overflow expectCrashLater() @@ -1280,6 +1284,7 @@ FloatingPoint.test("Float32/signalingNaN") { expectEqual(0x7fbf_ffff, f.bitPattern) } #if !os(WASI) +// Trap tests aren't available on WASI. do { // payload overflow expectCrashLater() @@ -1311,6 +1316,7 @@ FloatingPoint.test("Float64/signalingNaN") { expectEqual(0x7ff7_ffff_ffff_ffff, f.bitPattern) } #if !os(WASI) +// Trap tests aren't available on WASI. do { // payload overflow expectCrashLater() @@ -1346,6 +1352,7 @@ FloatingPoint.test("Float80/signalingNaN") { expectEqual(Float80Bits(0x7fff, 0xbfff_ffff_ffff_ffff), f.bitPattern) } #if !os(WASI) +// Trap tests aren't available on WASI. do { // payload overflow expectCrashLater() diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift index a554d5f8ba89b..ce5e0e15bace1 100644 --- a/test/stdlib/KeyPath.swift +++ b/test/stdlib/KeyPath.swift @@ -431,6 +431,7 @@ keyPath.test("optional force-unwrapping") { } #if !os(WASI) +// Trap tests aren't available on WASI. keyPath.test("optional force-unwrapping trap") { let origin_x = \TestOptional.origin!.x var value = TestOptional(origin: nil) diff --git a/test/stdlib/Mirror.swift b/test/stdlib/Mirror.swift index b11b224dc2e48..f1a85532a029b 100644 --- a/test/stdlib/Mirror.swift +++ b/test/stdlib/Mirror.swift @@ -959,6 +959,7 @@ mirrors.test("Addressing") { } #if !os(WASI) +// Trap tests aren't available on WASI. mirrors.test("Invalid Path Type") .skip(.custom( { _isFastAssertConfiguration() }, diff --git a/test/stdlib/NumericParsing.swift.gyb b/test/stdlib/NumericParsing.swift.gyb index 977d472cdfad5..80bbc6a6c4fe2 100644 --- a/test/stdlib/NumericParsing.swift.gyb +++ b/test/stdlib/NumericParsing.swift.gyb @@ -109,6 +109,7 @@ tests.test("${Self}/success") { } #if !os(WASI) +// Trap tests aren't available on WASI. tests.test("${Self}/radixTooLow") { ${Self}("0", radix: 2) expectCrashLater() diff --git a/test/stdlib/Optional.swift b/test/stdlib/Optional.swift index 9ac3cd5282df1..a5eacfac62a54 100644 --- a/test/stdlib/Optional.swift +++ b/test/stdlib/Optional.swift @@ -340,6 +340,7 @@ OptionalTests.test("Casting Optional") { } #if !os(WASI) +// Trap tests aren't available on WASI. OptionalTests.test("Casting Optional Traps") { let nx: C? = nil expectCrash { _blackHole(anyToAny(nx, Int.self)) } @@ -411,6 +412,7 @@ OptionalTests.test("unsafelyUnwrapped") { } #if !os(WASI) +// Trap tests aren't available on WASI. OptionalTests.test("unsafelyUnwrapped nil") .xfail(.custom( { !_isDebugAssertConfiguration() }, diff --git a/test/stdlib/Repeat.swift b/test/stdlib/Repeat.swift index 9c58a4baee913..a406447e490e9 100644 --- a/test/stdlib/Repeat.swift +++ b/test/stdlib/Repeat.swift @@ -24,6 +24,7 @@ RepeatTests.test("associated-types") { } #if !os(WASI) +// Trap tests aren't available on WASI. RepeatTests.test("out-of-bounds") { let sequence = repeatElement(0, count: 1) expectCrashLater() @@ -32,4 +33,3 @@ RepeatTests.test("out-of-bounds") { #endif runAllTests() - diff --git a/test/stdlib/StaticString.swift b/test/stdlib/StaticString.swift index f8dcdb946e996..8b365faf41f23 100644 --- a/test/stdlib/StaticString.swift +++ b/test/stdlib/StaticString.swift @@ -74,6 +74,7 @@ StaticStringTestSuite.test("PointerRepresentation/NonASCII") { } #if !os(WASI) +// Trap tests aren't available on WASI. StaticStringTestSuite.test("PointerRepresentation/unicodeScalar") .skip(.custom( { _isFastAssertConfiguration() }, @@ -126,6 +127,7 @@ StaticStringTestSuite.test("UnicodeScalarRepresentation/NonASCII") { } #if !os(WASI) +// Trap tests aren't available on WASI. StaticStringTestSuite.test("UnicodeScalarRepresentation/utf8Start") .skip(.custom( { _isFastAssertConfiguration() }, diff --git a/test/stdlib/UnsafePointer.swift.gyb b/test/stdlib/UnsafePointer.swift.gyb index 92c08ed500a27..6a3d957582ade 100644 --- a/test/stdlib/UnsafePointer.swift.gyb +++ b/test/stdlib/UnsafePointer.swift.gyb @@ -306,6 +306,7 @@ func checkPtr( } #if !os(WASI) +// Trap tests aren't available on WASI. UnsafeMutablePointerTestSuite.test("moveAssign:from:") { let check = checkPtr(UnsafeMutablePointer.moveAssign, true) check(Check.Disjoint) @@ -341,6 +342,7 @@ UnsafeMutablePointerTestSuite.test("moveInitialize:from:") { } #if !os(WASI) +// Trap tests aren't available on WASI. UnsafeMutablePointerTestSuite.test("initialize:from:") { let check = checkPtr(UnsafeMutablePointer.initialize(from:count:), false) check(Check.Disjoint) diff --git a/test/stdlib/UnsafeRawBufferPointer.swift b/test/stdlib/UnsafeRawBufferPointer.swift index 95608eac78223..47bb466341c61 100644 --- a/test/stdlib/UnsafeRawBufferPointer.swift +++ b/test/stdlib/UnsafeRawBufferPointer.swift @@ -129,6 +129,7 @@ UnsafeRawBufferPointerTestSuite.test("initFromArray") { } #if !os(WASI) +// Trap tests aren't available on WASI. UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).underflow") { let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 30, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } @@ -175,6 +176,7 @@ UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).exact") { } #if !os(WASI) +// Trap tests aren't available on WASI. UnsafeRawBufferPointerTestSuite.test("initializeMemory(as:from:).invalidNilPtr") { let buffer = UnsafeMutableRawBufferPointer(start: nil, count: 0) let source: [Int64] = [5, 4, 3, 2, 1] @@ -285,6 +287,7 @@ UnsafeRawBufferPointerTestSuite.test("inBounds") { } #if !os(WASI) +// Trap tests aren't available on WASI. UnsafeRawBufferPointerTestSuite.test("subscript.get.underflow") { let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 2, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } @@ -411,6 +414,7 @@ UnsafeRawBufferPointerTestSuite.test("subscript.range.wide") { // Performs a valid byte-wise copy but triggers a debug bounds check. buffer[0..<2] = buffer[0..<3] } +#endif UnsafeRawBufferPointerTestSuite.test("_copyContents") { let a = Array(0..<20) @@ -424,6 +428,8 @@ UnsafeRawBufferPointerTestSuite.test("_copyContents") { expectEqual(written, a.count) } +#if !os(WASI) +// Trap tests aren't available on WASI. UnsafeRawBufferPointerTestSuite.test("copyMemory.overflow") { var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 3, alignment: MemoryLayout.alignment) defer { buffer.deallocate() } @@ -437,6 +443,7 @@ UnsafeRawBufferPointerTestSuite.test("copyMemory.overflow") { UnsafeMutableRawBufferPointer(rebasing: bytes).copyMemory( from: UnsafeRawBufferPointer(buffer)) } +#endif // Use copyBytes without contiguous storage UnsafeRawBufferPointerTestSuite.test("copyBytes.withoutContiguouseStorage") { @@ -451,6 +458,8 @@ 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) defer { buffer.deallocate() } diff --git a/test/stdlib/UnsafeRawPointer.swift b/test/stdlib/UnsafeRawPointer.swift index a71e5595ca14d..11c890af6bd62 100644 --- a/test/stdlib/UnsafeRawPointer.swift +++ b/test/stdlib/UnsafeRawPointer.swift @@ -201,6 +201,7 @@ func checkPtr( } #if !os(WASI) +// Trap tests aren't available on WASI. UnsafeMutableRawPointerExtraTestSuite.test("initializeMemory:as:from:count:") { let check = checkPtr(UnsafeMutableRawPointer.initializeMemory(as:from:count:)) check(Check.Disjoint) diff --git a/tools/swift-ide-test/ModuleAPIDiff.cpp b/tools/swift-ide-test/ModuleAPIDiff.cpp index 61415dfd138cc..18336629d77b1 100644 --- a/tools/swift-ide-test/ModuleAPIDiff.cpp +++ b/tools/swift-ide-test/ModuleAPIDiff.cpp @@ -932,7 +932,7 @@ int swift::doGenerateModuleAPIDescription(StringRef MainExecutablePath, } CI.performSema(); - PrintOptions Options = PrintOptions::printEverything(); + PrintOptions Options = PrintOptions::printDeclarations(); ModuleDecl *M = CI.getMainModule(); M->getMainSourceFile().print(llvm::outs(), Options); diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 92369ed72a7fa..fc541c1fbba4e 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -526,6 +526,12 @@ FunctionDefinitions("function-definitions", llvm::cl::cat(Category), llvm::cl::init(true)); +static llvm::cl::opt +Expressions("expressions", + llvm::cl::desc("Print expressions"), + llvm::cl::cat(Category), + llvm::cl::init(false)); + static llvm::cl::opt AbstractAccessors("abstract-accessors", llvm::cl::desc("Hide the concrete accessors used to " @@ -2595,7 +2601,7 @@ static int doPrintLocalTypes(const CompilerInvocation &InitInvok, int ExitCode = 0; - PrintOptions Options = PrintOptions::printEverything(); + PrintOptions Options = PrintOptions::printDeclarations(); for (StringRef ModuleName : ModulesToPrint) { auto *M = getModuleByFullName(Context, ModuleName); @@ -2687,7 +2693,7 @@ static int doPrintLocalTypes(const CompilerInvocation &InitInvok, llvm::outs() << remangled << "\n"; - auto Options = PrintOptions::printEverything(); + auto Options = PrintOptions::printDeclarations(); Options.PrintAccess = false; LTD->print(llvm::outs(), Options); llvm::outs() << "\n"; @@ -3234,7 +3240,7 @@ static int doPrintTypes(const CompilerInvocation &InitInvok, registerIDERequestFunctions(CI.getASTContext().evaluator); CI.performSema(); - PrintOptions Options = PrintOptions::printEverything(); + PrintOptions Options = PrintOptions::printDeclarations(); Options.FullyQualifiedTypes = FullyQualifiedTypes; ASTTypePrinter Printer(CI.getSourceMgr(), Options); @@ -4379,6 +4385,7 @@ int main(int argc, char *argv[]) { PrintOpts.SynthesizeSugarOnTypes = options::SynthesizeSugarOnTypes; PrintOpts.AbstractAccessors = options::AbstractAccessors; PrintOpts.FunctionDefinitions = options::FunctionDefinitions; + PrintOpts.PrintExprs = options::Expressions; PrintOpts.PreferTypeRepr = options::PreferTypeRepr; PrintOpts.ExplodePatternBindingDecls = options::ExplodePatternBindingDecls; PrintOpts.PrintImplicitAttrs = options::PrintImplicitAttrs; diff --git a/unittests/runtime/CMakeLists.txt b/unittests/runtime/CMakeLists.txt index 0633bed382f1c..9795056699383 100644 --- a/unittests/runtime/CMakeLists.txt +++ b/unittests/runtime/CMakeLists.txt @@ -140,6 +140,8 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND target_include_directories(SwiftRuntimeTests BEFORE PRIVATE ${SWIFT_SOURCE_DIR}/stdlib/include) + target_include_directories(SwiftRuntimeTests BEFORE PRIVATE + ${SWIFT_SOURCE_DIR}/stdlib/public) # FIXME: cross-compile for all variants. target_link_libraries(SwiftRuntimeTests diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index 68277115b19ba..2de97c3df6de4 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -108,12 +108,16 @@ class CompatibilityOverrideConcurrencyTest : public ::testing::Test { } }; +static Job fakeJob{{JobKind::DefaultActorInline}, + static_cast(nullptr), + nullptr}; + TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_enqueue) { - swift_task_enqueue(nullptr, ExecutorRef::generic()); + swift_task_enqueue(&fakeJob, ExecutorRef::generic()); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_job_run) { - swift_job_run(nullptr, ExecutorRef::generic()); + swift_job_run(&fakeJob, ExecutorRef::generic()); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_getCurrentExecutor) { @@ -125,17 +129,17 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_switch) { } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_enqueueGlobal) { - swift_task_enqueueGlobal(nullptr); + swift_task_enqueueGlobal(&fakeJob); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_enqueueGlobalWithDelay) { - swift_task_enqueueGlobalWithDelay(0, nullptr); + swift_task_enqueueGlobalWithDelay(0, &fakeJob); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_enqueueMainExecutor) { - swift_task_enqueueMainExecutor(nullptr); + swift_task_enqueueMainExecutor(&fakeJob); } TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_create_common) { @@ -227,30 +231,10 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_localsCopyTo) { swift_task_localsCopyTo(nullptr); } -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_addStatusRecord) { - swift_task_addStatusRecord(nullptr); -} - -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_tryAddStatusRecord) { - swift_task_tryAddStatusRecord(nullptr); -} - -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_removeStatusRecord) { - swift_task_removeStatusRecord(nullptr); -} - TEST_F(CompatibilityOverrideConcurrencyTest, task_hasTaskGroupStatusRecord) { swift_task_hasTaskGroupStatusRecord(); } -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_attachChild) { - swift_task_attachChild(nullptr); -} - -TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_detachChild) { - swift_task_detachChild(nullptr); -} - TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_cancel) { swift_task_cancel(nullptr); } diff --git a/unittests/runtime/TaskStatus.cpp b/unittests/runtime/TaskStatus.cpp index c7ba403802b0f..7bbf84837a45b 100644 --- a/unittests/runtime/TaskStatus.cpp +++ b/unittests/runtime/TaskStatus.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#include "swift/Runtime/Concurrency.h" #include "swift/Basic/STLExtras.h" +#include "swift/Runtime/Concurrency.h" #include "gtest/gtest.h" using namespace swift; @@ -137,149 +137,3 @@ TEST(TaskStatusTest, cancellation_simple) { swift_job_run(task, createFakeExecutor(1234)); }); } - -// Test basic deadline mechanics (other than actually setting up -// something to cancel the task). Also tests adding and removing -// records quite a bit. -TEST(TaskStatusTest, deadline) { - struct Storage { int value; }; - withSimpleTask(Storage{47}, - [&](ValueContext *context) { - auto task = swift_task_getCurrent(); - EXPECT_FALSE(swift_task_isCancelled(task)); - - TaskDeadline deadlineOne = { 1234 }; - TaskDeadline deadlineTwo = { 2345 }; - DeadlineStatusRecord recordOne(deadlineOne); - DeadlineStatusRecord recordTwo(deadlineTwo); - bool result; - - NearestTaskDeadline nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::None, nearest.ValueKind); - - // Add deadline 1. Check that we haven't been cancelled yet. - result = swift_task_addStatusRecord(&recordOne); - EXPECT_TRUE(result); - - // There should now be an active deadline. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineOne, nearest.Value); - - // Remove deadline 1. Check that we haven't been cancelled yet. - result = swift_task_removeStatusRecord(&recordOne); - EXPECT_TRUE(result); - - // There shouldn't be an active deadline anymore. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::None, nearest.ValueKind); - - // Add deadline 1, then 2. - result = swift_task_addStatusRecord(&recordOne); - EXPECT_TRUE(result); - result = swift_task_addStatusRecord(&recordTwo); - EXPECT_TRUE(result); - - // The nearest deadline should be deadline 1. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineOne, nearest.Value); - - // Remove the deadlines. - result = swift_task_removeStatusRecord(&recordTwo); - EXPECT_TRUE(result); - result = swift_task_removeStatusRecord(&recordOne); - EXPECT_TRUE(result); - - // Add deadline 2, then 1s. - result = swift_task_addStatusRecord(&recordTwo); - EXPECT_TRUE(result); - - // In the middle, the nearest deadline should be deadline 2. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineTwo, nearest.Value); - - result = swift_task_addStatusRecord(&recordOne); - EXPECT_TRUE(result); - - // The nearest deadline should be deadline 1. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineOne, nearest.Value); - - // Remove the deadlines. - result = swift_task_removeStatusRecord(&recordOne); - EXPECT_TRUE(result); - result = swift_task_removeStatusRecord(&recordTwo); - EXPECT_TRUE(result); - - // Do the same thing with tryAddStatus. - result = swift_task_tryAddStatusRecord(&recordTwo); - EXPECT_TRUE(result); - result = swift_task_tryAddStatusRecord(&recordOne); - EXPECT_TRUE(result); - // The nearest deadline should be deadline 1. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineOne, nearest.Value); - result = swift_task_removeStatusRecord(&recordOne); - EXPECT_TRUE(result); - result = swift_task_removeStatusRecord(&recordTwo); - EXPECT_TRUE(result); - - // Remove out of order. - result = swift_task_addStatusRecord(&recordTwo); - EXPECT_TRUE(result); - result = swift_task_addStatusRecord(&recordOne); - EXPECT_TRUE(result); - // The nearest deadline should be deadline 1. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineOne, nearest.Value); - result = swift_task_removeStatusRecord(&recordTwo); - EXPECT_TRUE(result); - result = swift_task_removeStatusRecord(&recordOne); - EXPECT_TRUE(result); - - // Add deadline 2, then cancel. - result = swift_task_addStatusRecord(&recordTwo); - EXPECT_TRUE(result); - - // The nearest deadline should be deadline 2. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::Active, nearest.ValueKind); - EXPECT_EQ(deadlineTwo, nearest.Value); - - // Cancel. - swift_task_cancel(task); - EXPECT_TRUE(swift_task_isCancelled(task)); - - // We should report already cancelled now. - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); - - // Add deadline 1. - result = swift_task_addStatusRecord(&recordOne); - EXPECT_FALSE(result); - - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); - - result = swift_task_removeStatusRecord(&recordOne); - EXPECT_FALSE(result); - - result = swift_task_tryAddStatusRecord(&recordOne); - EXPECT_FALSE(result); - - result = swift_task_removeStatusRecord(&recordTwo); - EXPECT_FALSE(result); - - nearest = swift_task_getNearestDeadline(task); - EXPECT_EQ(NearestTaskDeadline::AlreadyCancelled, nearest.ValueKind); - - EXPECT_TRUE(swift_task_isCancelled(task)); - }, [&](AsyncTask *task) { - swift_job_run(task, createFakeExecutor(1234)); - }); -} diff --git a/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/IndicRules.swift b/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/IndicRules.swift new file mode 100644 index 0000000000000..daf4f7aef26f1 --- /dev/null +++ b/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/IndicRules.swift @@ -0,0 +1,179 @@ +//===----------------------------------------------------------------------===// +// +// 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 GenUtils + +func getLinkingConsonant( + from data: String +) -> [ClosedRange] { + var unflattened: [(ClosedRange, String)] = [] + + for line in data.split(separator: "\n") { + // Skip comments + guard !line.hasPrefix("#") else { + continue + } + + let components = line.split(separator: ";") + + // Get the property first because it may be one we don't care about. + let splitProperty = components[1].split(separator: "#") + let filteredProperty = splitProperty[0].filter { !$0.isWhitespace } + + // We only care about Linking Consonant who is defined as 'Consonant'. + guard filteredProperty == "Consonant" else { + continue + } + + // This rule only applies to the following scripts, so ensure that these + // scalars are from such scripts. + for script in ["Bengali", "Devanagari", "Gujarati", "Oriya", "Telugu", "Malayalam"] { + guard line.contains(script.uppercased()) else { + continue + } + + break + } + + let scalars: ClosedRange + + let filteredScalars = components[0].filter { !$0.isWhitespace } + + // If we have . appear, it means we have a legitimate range. Otherwise, + // it's a singular scalar. + if filteredScalars.contains(".") { + let range = filteredScalars.split(separator: ".") + + scalars = UInt32(range[0], radix: 16)! ... UInt32(range[1], radix: 16)! + } else { + let scalar = UInt32(filteredScalars, radix: 16)! + + scalars = scalar ... scalar + } + + unflattened.append((scalars, "Consonant")) + } + + return flatten(unflattened).map { $0.0 } +} + +func emitLinkingConsonant( + _ data: [ClosedRange], + into result: inout String +) { + // 64 bit arrays * 8 bytes = .512 KB + var bitArrays: [BitArray] = .init(repeating: .init(size: 64), count: 64) + + let chunkSize = 0x110000 / 64 / 64 + + var chunks: [Int] = [] + + for i in 0 ..< 64 * 64 { + let lower = i * chunkSize + let upper = lower + chunkSize - 1 + + let idx = i / 64 + let bit = i % 64 + + for scalar in lower ... upper { + if data.contains(where: { $0.contains(UInt32(scalar)) }) { + chunks.append(i) + + bitArrays[idx][bit] = true + break + } + } + } + + // Remove the trailing 0s. Currently this reduces quick look size down to + // 96 bytes from 512 bytes. + var reducedBA = Array(bitArrays.reversed()) + reducedBA = Array(reducedBA.drop { + $0.words == [0x0] + }) + + bitArrays = reducedBA.reversed() + + // Keep a record of every rank for all the bitarrays. + var ranks: [UInt16] = [] + + // Record our quick look ranks. + var lastRank: UInt16 = 0 + for (i, _) in bitArrays.enumerated() { + guard i != 0 else { + ranks.append(0) + continue + } + + var rank = UInt16(bitArrays[i - 1].words[0].nonzeroBitCount) + rank += lastRank + + ranks.append(rank) + + lastRank = rank + } + + // Insert our quick look size at the beginning. + var size = BitArray(size: 64) + size.words = [UInt64(bitArrays.count)] + bitArrays.insert(size, at: 0) + + for chunk in chunks { + var chunkBA = BitArray(size: chunkSize) + + let lower = chunk * chunkSize + let upper = lower + chunkSize + + for scalar in lower ..< upper { + if data.contains(where: { $0.contains(UInt32(scalar)) }) { + chunkBA[scalar % chunkSize] = true + } + } + + // Append our chunk bit array's rank. + var lastRank: UInt16 = 0 + for (i, _) in chunkBA.words.enumerated() { + guard i != 0 else { + ranks.append(0) + continue + } + + var rank = UInt16(chunkBA.words[i - 1].nonzeroBitCount) + rank += lastRank + + ranks.append(rank) + lastRank = rank + } + + bitArrays += chunkBA.words.map { + var ba = BitArray(size: 64) + ba.words = [$0] + return ba + } + } + + emitCollection( + ranks, + name: "_swift_stdlib_linkingConsonant_ranks", + into: &result + ) + + emitCollection( + bitArrays, + name: "_swift_stdlib_linkingConsonant", + type: "__swift_uint64_t", + into: &result + ) { + assert($0.words.count == 1) + return "0x\(String($0.words[0], radix: 16, uppercase: true))" + } +} diff --git a/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/main.swift b/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/main.swift index 2d042158c621e..d8b2aae407ee2 100644 --- a/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/main.swift +++ b/utils/gen-unicode-data/Sources/GenGraphemeBreakProperty/main.swift @@ -48,37 +48,6 @@ extension Unicode { } } -// Takes an unflattened array of scalar ranges and grapheme break properties and -// attempts to merge ranges who share the same break property. E.g: -// -// 0x0 ... 0xA = .control -// 0xB ... 0xB = .control -// 0xC ... 0x1F = .control -// -// into: -// -// 0x0 ... 0x1F = .control -func flatten( - _ unflattened: [(ClosedRange, Unicode.GraphemeBreakProperty)] -) -> [(ClosedRange, Unicode.GraphemeBreakProperty)] { - var result: [(ClosedRange, Unicode.GraphemeBreakProperty)] = [] - - for elt in unflattened.sorted(by: { $0.0.lowerBound < $1.0.lowerBound }) { - guard !result.isEmpty, result.last!.1 == elt.1 else { - result.append(elt) - continue - } - - if elt.0.lowerBound == result.last!.0.upperBound + 1 { - result[result.count - 1].0 = result.last!.0.lowerBound ... elt.0.upperBound - } else { - result.append(elt) - } - } - - return result -} - // Given a path to one of the Unicode data files, reads it and returns the // unflattened list of scalar & grapheme break property. // @@ -150,7 +119,9 @@ func emit( into result: inout String ) { result += """ - static __swift_uint32_t _swift_stdlib_graphemeBreakProperties[\(data.count)] = { + #define GRAPHEME_BREAK_DATA_COUNT \(data.count) + + static const __swift_uint32_t _swift_stdlib_graphemeBreakProperties[\(data.count)] = { """ @@ -181,69 +152,20 @@ func emit( value |= 1 << 31 } - return "0x\(String(value, radix: 16))" + return "0x\(String(value, radix: 16, uppercase: true))" } - result += "\n};\n\n" -} - -// Writes the stdlib internal routine for binary searching the grapheme array. -func emitAccessor( - _ dataCount: Int, - into result: inout String -) { result += """ - SWIFT_RUNTIME_STDLIB_INTERNAL - __swift_uint8_t _swift_stdlib_getGraphemeBreakProperty(__swift_uint32_t scalar) { - auto low = 0; - auto high = \(dataCount) - 1; - - while (high >= low) { - auto idx = low + (high - low) / 2; - - auto entry = _swift_stdlib_graphemeBreakProperties[idx]; - - // Shift the enum and range count out of the value. - auto lower = (entry << 11) >> 11; - - // Shift the enum out first, then shift out the scalar value. - auto upper = lower + ((entry << 3) >> 24); - - // Shift everything out. - auto enumValue = (__swift_uint8_t)(entry >> 29); - - // Special case: extendedPictographic who used an extra bit for the range. - if (enumValue == 5) { - upper = lower + ((entry << 2) >> 23); - } - - if (scalar >= lower && scalar <= upper) { - return enumValue; - } - - if (scalar > upper) { - low = idx + 1; - continue; - } - - if (scalar < lower) { - high = idx - 1; - continue; - } - } - - // If we made it out here, then our scalar was not found in the grapheme - // array (this occurs when a scalar doesn't map to any grapheme break - // property). Return the max value here to indicate .any. - return 0xFF; - } - + + }; + + """ } // Main entry point into the grapheme break property generator. func generateGraphemeBreakProperty() { - var result = readFile("Input/UnicodeGrapheme.cpp") + var result = readFile("Input/GraphemeData.h") let baseData = getGraphemeBreakPropertyData( for: "Data/GraphemeBreakProperty.txt" @@ -267,10 +189,21 @@ func generateGraphemeBreakProperty() { } emit(data, into: &result) - - emitAccessor(data.count, into: &result) - - write(result, to: "Output/UnicodeGrapheme.cpp") + + // Handle the CLDR grapheme breaking rules: + + let indicSyllabicCategory = readFile("Data/IndicSyllabicCategory.txt") + + let consonants = getLinkingConsonant(from: indicSyllabicCategory) + + emitLinkingConsonant(consonants, into: &result) + + result += """ + #endif // #ifndef GRAPHEME_DATA_H + + """ + + write(result, to: "Output/Common/GraphemeData.h") } generateGraphemeBreakProperty() diff --git a/validation-test/SILOptimizer/lexical-lifetimes.swift b/validation-test/SILOptimizer/lexical-lifetimes.swift index 9852da8487a18..7a43b63934fe2 100644 --- a/validation-test/SILOptimizer/lexical-lifetimes.swift +++ b/validation-test/SILOptimizer/lexical-lifetimes.swift @@ -1,5 +1,6 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-copy-propagation) | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library -Xfrontend -enable-copy-propagation) | %FileCheck %s +// REQUIRES: concurrency // REQUIRES: executable_test // ============================================================================= @@ -142,19 +143,79 @@ func test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() { test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction_doit("blue") } +func do_foo(_ work: () -> ()) { + work() +} +class Fooer { + __consuming func foo() { + weak var weakSelf = self + do_foo { + weakSelf?.foo1() + weakSelf?.foo2() + } + } + func foo1() { + // CHECK: Fooer foo1 + print(type(of: self), #function) + } + func foo2() { + // CHECK: Fooer foo2 + print(type(of: self), #function) + } +} + +func test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakVar() { + Fooer().foo() +} + +func do_foo_async(_ work: @escaping () -> ()) -> Task { + Task { + work() + } +} +class FooerAsync { + var strongSelf: FooerAsync? + __consuming func foo() -> Task { + weak var weakSelf = self + strongSelf = self + return do_foo_async { + // At this point, strongSelf is keeping the object alive. + weakSelf?.foo1() + // By this point, strongSelf has been nil'd, dropping the object's retain + // count to 0, deallocating the object, so weakSelf is nil. + weakSelf?.foo2() + } + } + func foo1() { + // CHECK: FooerAsync foo1 + print(type(of: self), #function) + strongSelf = nil + } + func foo2() { + // CHECK-NOT: FooerAsync foo2 + print(type(of: self), #function) + } +} + +func test_repeatedLoadWeakSelf() -> Task { + FooerAsync().foo() +} + // ============================================================================= // = Tests }} = // ============================================================================= -func run() { - test_localLet_keepsObjectAliveBeyondCallToClassWithWeakReference() - // Reenable with rdar://86271875 - // test_localVar_keepsObjectAliveBeyondCallToClassWithWeakReference() - test_localLet_keepsObjectAliveBeyondCallToClassWithPointer() - test_localVar_keepsObjectAliveBeyondCallToClassWithPointer() - test_localLet_keepsObjectAliveBeyondCallToSynchronizationPointFunction() - test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() +@main struct Main { + static func main() async { + test_localLet_keepsObjectAliveBeyondCallToClassWithWeakReference() + // Reenable with rdar://86271875 + // test_localVar_keepsObjectAliveBeyondCallToClassWithWeakReference() + test_localLet_keepsObjectAliveBeyondCallToClassWithPointer() + test_localVar_keepsObjectAliveBeyondCallToClassWithPointer() + test_localLet_keepsObjectAliveBeyondCallToSynchronizationPointFunction() + test_localVar_keepsObjectAliveBeyondCallToSynchronizationPointFunction() + + test_self_keepsObjectAliveBeyond_callTo_functionTakingClosureCapturingWeakVar() + await test_repeatedLoadWeakSelf().value + } } - -run() - diff --git a/validation-test/stdlib/String.swift b/validation-test/stdlib/String.swift index 453c52d53a85b..bda49c26b321d 100644 --- a/validation-test/stdlib/String.swift +++ b/validation-test/stdlib/String.swift @@ -2280,4 +2280,58 @@ StringTests.test("NormalizationCheck/Opaque") #endif } +func expectBidirectionalCount(_ count: Int, _ string: String) { + var i = 0 + var index = string.endIndex + + while index != string.startIndex { + i += 1 + string.formIndex(before: &index) + } + + expectEqual(i, count) +} + +StringTests.test("GraphemeBreaking.Indic Sequences") { + let test1 = "\u{0915}\u{0924}" // 2 + expectEqual(2, test1.count) + expectBidirectionalCount(2, test1) + + let test2 = "\u{0915}\u{094D}\u{0924}" // 1 + expectEqual(1, test2.count) + expectBidirectionalCount(1, test2) + + let test3 = "\u{0915}\u{094D}\u{094D}\u{0924}" // 1 + expectEqual(1, test3.count) + expectBidirectionalCount(1, test3) + + let test4 = "\u{0915}\u{094D}\u{200D}\u{0924}" // 1 + expectEqual(1, test4.count) + expectBidirectionalCount(1, test4) + + let test5 = "\u{0915}\u{093C}\u{200D}\u{094D}\u{0924}" // 1 + expectEqual(1, test5.count) + expectBidirectionalCount(1, test5) + + let test6 = "\u{0915}\u{093C}\u{094D}\u{200D}\u{0924}" // 1 + expectEqual(1, test6.count) + expectBidirectionalCount(1, test6) + + let test7 = "\u{0915}\u{094D}\u{0924}\u{094D}\u{092F}" // 1 + expectEqual(1, test7.count) + expectBidirectionalCount(1, test7) + + let test8 = "\u{0915}\u{094D}\u{0061}" // 2 + expectEqual(2, test8.count) + expectBidirectionalCount(2, test8) + + let test9 = "\u{0061}\u{094D}\u{0924}" // 2 + expectEqual(2, test9.count) + expectBidirectionalCount(2, test9) + + let test10 = "\u{003F}\u{094D}\u{0924}" // 2 + expectEqual(2, test10.count) + expectBidirectionalCount(2, test10) +} + runAllTests()