From 878efac769392d4cdcdbd0eecefc74bd992badde Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Mon, 3 Aug 2020 18:20:36 +0900 Subject: [PATCH 001/120] [Serialization] Force destructor creation for imported class Swift class deinit decl can be implicitly synthesized when emitting swiftmodule, so swiftmodule always have deinit decl. So resolution of x-refs to deinit of swift class always success. But when x-refs points deinit of clang imported class, it always failed because clang importer doesn't force to synthesize deinit before looking up. x-refs to deinit decl appears in only deinit of its subclasses, so it's serialized only when deinit have body. And deinit has body only on SIB because deinit is always non-inlinable. It means that this missing of deinit creation can be problem only on SIB This commit changes to force to synthesize class deinit decl before looking up members. --- lib/Serialization/Deserialization.cpp | 8 ++++++++ test/Serialization/Inputs/objc-xref/module.modulemap | 4 ++++ test/Serialization/Inputs/objc-xref/objc_xref.h | 2 ++ test/Serialization/xref-deinit.swift | 9 +++++++++ 4 files changed, 23 insertions(+) create mode 100644 test/Serialization/Inputs/objc-xref/module.modulemap create mode 100644 test/Serialization/Inputs/objc-xref/objc_xref.h create mode 100644 test/Serialization/xref-deinit.swift diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 8197ba44e96e4..7dc8c247a1ab6 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1611,6 +1611,14 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { getXRefDeclNameForError()); } + if (memberName.getKind() == DeclBaseName::Kind::Destructor) { + assert(isa(nominal)); + // Force creation of an implicit destructor + auto CD = dyn_cast(nominal); + values.push_back(CD->getDestructor()); + break; + } + if (!privateDiscriminator.empty()) { ModuleDecl *searchModule = M; if (!searchModule) diff --git a/test/Serialization/Inputs/objc-xref/module.modulemap b/test/Serialization/Inputs/objc-xref/module.modulemap new file mode 100644 index 0000000000000..7c036997bed3b --- /dev/null +++ b/test/Serialization/Inputs/objc-xref/module.modulemap @@ -0,0 +1,4 @@ +module ObjCXRef { + header "objc_xref.h" + export * +} diff --git a/test/Serialization/Inputs/objc-xref/objc_xref.h b/test/Serialization/Inputs/objc-xref/objc_xref.h new file mode 100644 index 0000000000000..d3b7a87ef6439 --- /dev/null +++ b/test/Serialization/Inputs/objc-xref/objc_xref.h @@ -0,0 +1,2 @@ +@interface MyObject +@end diff --git a/test/Serialization/xref-deinit.swift b/test/Serialization/xref-deinit.swift new file mode 100644 index 0000000000000..98c43b6670a87 --- /dev/null +++ b/test/Serialization/xref-deinit.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-sib %s -o %t/xref-deinit.sib -I%t -I %S/Inputs/objc-xref +// RUN: %target-swift-frontend -emit-sil %t/xref-deinit.sib -I%t -I %S/Inputs/objc-xref + +// REQUIRES: objc_interop + +import ObjCXRef + +public class Object: MyObject {} From 17c8d2749f400610c72aad590ba4758abf77d699 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 24 Jun 2020 11:37:08 -0700 Subject: [PATCH 002/120] test: migrate tests to Python3 by default This changes the python interpreter used to invoke the tests to Python3. Python2 has been EOL'ed by the Python Software Foundation. This migration enables the Swift test suite to run with Python 3 instead. --- test/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ec8710ec76948..ede6aee79e780 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -351,7 +351,7 @@ _Block_release(void) { }\n") endif() execute_process(COMMAND - $ "-c" "import psutil" + $ "-c" "import psutil" RESULT_VARIABLE python_psutil_status TIMEOUT 1 # second ERROR_QUIET) @@ -415,7 +415,7 @@ _Block_release(void) { }\n") ${command_upload_swift_reflection_test} ${command_clean_test_results_dir} COMMAND - $ "${LIT}" + $ "${LIT}" ${LIT_ARGS} "--param" "swift_test_subset=${test_subset}" "--param" "swift_test_mode=${test_mode}" @@ -434,7 +434,7 @@ _Block_release(void) { }\n") ${command_upload_swift_reflection_test} ${command_clean_test_results_dir} COMMAND - $ "${LIT}" + $ "${LIT}" ${LIT_ARGS} "--param" "swift_test_subset=${test_subset}" "--param" "swift_test_mode=${test_mode}" From 5e3ef645e68f399ae9320ab9def0eabca99b73e6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 24 Aug 2020 22:49:22 -0400 Subject: [PATCH 003/120] AST: Fix ASTScopeLookup crash if a PatternBindingEntry's context is not a PatternBindingInitializer The function builder transform creates pattern bindings parented in other DeclContexts. If those pattern binding initializer expressions in turn contain multi-statement closures, we will try to perform unqualified lookups from those contexts when we get around to type checking the closure body. Change some unconditional casts to conditional casts in ASTScope lookup, to handle this case. The casts are only performed while checking if the initializer context is part of a 'lazy' property, which doesn't apply here. Fixes . --- lib/AST/ASTScopeLookup.cpp | 4 ++-- .../rdar67239650.swift | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 validation-test/compiler_crashers_2_fixed/rdar67239650.swift diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 250f2ad6c0678..7896e009534a2 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -519,7 +519,7 @@ bool BraceStmtScope::lookupLocalsOrMembers(ArrayRef, bool PatternEntryInitializerScope::lookupLocalsOrMembers( ArrayRef, DeclConsumer consumer) const { // 'self' is available within the pattern initializer of a 'lazy' variable. - auto *initContext = cast_or_null( + auto *initContext = dyn_cast_or_null( decl->getInitContext(0)); if (initContext) { if (auto *selfParam = initContext->getImplicitSelfDecl()) { @@ -816,7 +816,7 @@ Optional ClosureBodyScope::resolveIsCascadingUseForThisScope( Optional PatternEntryInitializerScope::resolveIsCascadingUseForThisScope( Optional isCascadingUse) const { auto *const initContext = getPatternEntry().getInitContext(); - auto *PBI = cast_or_null(initContext); + auto *PBI = dyn_cast_or_null(initContext); auto *isd = PBI ? PBI->getImplicitSelfDecl() : nullptr; // 'self' is available within the pattern initializer of a 'lazy' variable. diff --git a/validation-test/compiler_crashers_2_fixed/rdar67239650.swift b/validation-test/compiler_crashers_2_fixed/rdar67239650.swift new file mode 100644 index 0000000000000..5ec036b8b61cf --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/rdar67239650.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend -typecheck %s + +@_functionBuilder +struct SillyBuilder { + static func buildBlock() -> () {} +} + +struct SillyStruct { + init(@SillyBuilder _: () -> ()) {} +} + +struct UsesSillyStruct { + var x: Int = 0 + + func foo() { + SillyStruct { + let fn = { + if true { + _ = x + } + } + } + } +} From b4fb7d019a3eefd7fe0f51430fa6fa98afe95fc7 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Tue, 25 Aug 2020 14:15:03 +1000 Subject: [PATCH 004/120] [IDE] Skip visiting constructor references when the decl is unknown Have left in an assert since it would be nice to get a test case for when this actually occurs. Resolves rdar://67412430 --- lib/IDE/SourceEntityWalker.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 6f6f08e9daf27..dd494e03cad25 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -715,6 +715,12 @@ passReference(ValueDecl *D, Type Ty, SourceLoc BaseNameLoc, SourceRange Range, } } + if (D == nullptr) { + // FIXME: When does this happen? + assert(false && "unhandled reference"); + return true; + } + CharSourceRange CharRange = Lexer::getCharSourceRangeFromSourceRange(D->getASTContext().SourceMgr, Range); From dd9e1cf618228226ad52ce76bc6c92bb0db4b1af Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 25 Aug 2020 10:16:43 -0700 Subject: [PATCH 005/120] [CS] Don't use TMF_GenerateConstraints in simplifyConstraint Adding a new unsolved constraint is redundant, as it's already present in the inactive list. Noticed by inspection. --- lib/Sema/CSSimplify.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 1608f19e49155..5ba9345c7c555 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -10657,7 +10657,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { case ConstraintKind::OptionalObject: return simplifyOptionalObjectConstraint(constraint.getFirstType(), constraint.getSecondType(), - TMF_GenerateConstraints, + /*flags*/ None, constraint.getLocator()); case ConstraintKind::ValueMember: @@ -10669,8 +10669,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { constraint.getMemberUseDC(), constraint.getFunctionRefKind(), /*outerAlternatives=*/{}, - TMF_GenerateConstraints, - constraint.getLocator()); + /*flags*/ None, constraint.getLocator()); case ConstraintKind::ValueWitness: return simplifyValueWitnessConstraint(constraint.getKind(), @@ -10679,20 +10678,20 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { constraint.getSecondType(), constraint.getMemberUseDC(), constraint.getFunctionRefKind(), - TMF_GenerateConstraints, + /*flags*/ None, constraint.getLocator()); case ConstraintKind::Defaultable: return simplifyDefaultableConstraint(constraint.getFirstType(), constraint.getSecondType(), - TMF_GenerateConstraints, + /*flags*/ None, constraint.getLocator()); case ConstraintKind::DefaultClosureType: return simplifyDefaultClosureTypeConstraint(constraint.getFirstType(), constraint.getSecondType(), constraint.getTypeVariables(), - TMF_GenerateConstraints, + /*flags*/ None, constraint.getLocator()); case ConstraintKind::FunctionInput: @@ -10700,7 +10699,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { return simplifyFunctionComponentConstraint(constraint.getKind(), constraint.getFirstType(), constraint.getSecondType(), - TMF_GenerateConstraints, + /*flags*/ None, constraint.getLocator()); case ConstraintKind::Disjunction: @@ -10712,8 +10711,7 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { return simplifyOneWayConstraint(constraint.getKind(), constraint.getFirstType(), constraint.getSecondType(), - TMF_GenerateConstraints, - constraint.getLocator()); + /*flags*/ None, constraint.getLocator()); } llvm_unreachable("Unhandled ConstraintKind in switch."); From 6480bfaad2c0e7f2e3b813f9bc2c4de987401c40 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Wed, 12 Aug 2020 11:34:18 -0700 Subject: [PATCH 006/120] [gardening] Move FuzzyStringMatcher to libIDE --- .../swift/IDE}/FuzzyStringMatcher.h | 16 +++++++++------- lib/IDE/CMakeLists.txt | 1 + .../Support => lib/IDE}/FuzzyStringMatcher.cpp | 7 ++++--- tools/SourceKit/lib/Support/CMakeLists.txt | 1 - .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 2 +- .../SourceKit/Support/FuzzyStringMatcherTest.cpp | 4 ++-- 6 files changed, 17 insertions(+), 14 deletions(-) rename {tools/SourceKit/include/SourceKit/Support => include/swift/IDE}/FuzzyStringMatcher.h (83%) rename {tools/SourceKit/lib/Support => lib/IDE}/FuzzyStringMatcher.cpp (99%) diff --git a/tools/SourceKit/include/SourceKit/Support/FuzzyStringMatcher.h b/include/swift/IDE/FuzzyStringMatcher.h similarity index 83% rename from tools/SourceKit/include/SourceKit/Support/FuzzyStringMatcher.h rename to include/swift/IDE/FuzzyStringMatcher.h index cb53e962de0d7..af80647e37c3e 100644 --- a/tools/SourceKit/include/SourceKit/Support/FuzzyStringMatcher.h +++ b/include/swift/IDE/FuzzyStringMatcher.h @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,14 +10,15 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_SOURCEKIT_LIB_SUPPORT_FUZZYSTRINGMATCHER_H -#define LLVM_SOURCEKIT_LIB_SUPPORT_FUZZYSTRINGMATCHER_H +#ifndef SWIFT_IDE_FUZZYSTRINGMATCHER_H +#define SWIFT_IDE_FUZZYSTRINGMATCHER_H -#include "SourceKit/Core/LLVM.h" +#include "swift/Basic/LLVM.h" #include "llvm/ADT/BitVector.h" #include -namespace SourceKit { +namespace swift { +namespace ide { /// FuzzyStringMatcher compares candidate strings against a pattern /// string using a fuzzy matching algorithm and provides a numerical @@ -49,6 +50,7 @@ class FuzzyStringMatcher { double scoreCandidate(StringRef candidate) const; }; -} // end namespace SourceKit +} // namespace ide +} // namespace swift -#endif // LLVM_SOURCEKIT_LIB_SUPPORT_FUZZYSTRINGMATCHER_H +#endif // SWIFT_IDE_FUZZYSTRINGMATCHER_H diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 7348c057ef63a..e7529a25a89d5 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -7,6 +7,7 @@ add_swift_host_library(swiftIDE STATIC ConformingMethodList.cpp ExprContextAnalysis.cpp Formatting.cpp + FuzzyStringMatcher.cpp Refactoring.cpp ModuleInterfacePrinting.cpp REPLCodeCompletion.cpp diff --git a/tools/SourceKit/lib/Support/FuzzyStringMatcher.cpp b/lib/IDE/FuzzyStringMatcher.cpp similarity index 99% rename from tools/SourceKit/lib/Support/FuzzyStringMatcher.cpp rename to lib/IDE/FuzzyStringMatcher.cpp index 463d541d0d850..d016464691f19 100644 --- a/tools/SourceKit/lib/Support/FuzzyStringMatcher.cpp +++ b/lib/IDE/FuzzyStringMatcher.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -10,12 +10,13 @@ // //===----------------------------------------------------------------------===// -#include "SourceKit/Support/FuzzyStringMatcher.h" +#include "swift/IDE/FuzzyStringMatcher.h" #include "clang/Basic/CharInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" -using namespace SourceKit; +using namespace swift; +using namespace swift::ide; using clang::toUppercase; using clang::toLowercase; using clang::isUppercase; diff --git a/tools/SourceKit/lib/Support/CMakeLists.txt b/tools/SourceKit/lib/Support/CMakeLists.txt index 901720a521069..3b18cc18e98c9 100644 --- a/tools/SourceKit/lib/Support/CMakeLists.txt +++ b/tools/SourceKit/lib/Support/CMakeLists.txt @@ -1,6 +1,5 @@ add_sourcekit_library(SourceKitSupport Concurrency-libdispatch.cpp - FuzzyStringMatcher.cpp Logging.cpp ImmutableTextBuffer.cpp ThreadSafeRefCntPtr.cpp diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 8ac901da4de3c..a7864e91cc65a 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -11,10 +11,10 @@ //===----------------------------------------------------------------------===// #include "CodeCompletionOrganizer.h" -#include "SourceKit/Support/FuzzyStringMatcher.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Module.h" #include "swift/IDE/CodeCompletionResultPrinter.h" +#include "swift/IDE/FuzzyStringMatcher.h" #include "swift/Frontend/Frontend.h" #include "swift/Markup/XMLUtils.h" #include "clang/Basic/CharInfo.h" diff --git a/unittests/SourceKit/Support/FuzzyStringMatcherTest.cpp b/unittests/SourceKit/Support/FuzzyStringMatcherTest.cpp index e0e7e83b4220c..e190933fcd988 100644 --- a/unittests/SourceKit/Support/FuzzyStringMatcherTest.cpp +++ b/unittests/SourceKit/Support/FuzzyStringMatcherTest.cpp @@ -10,10 +10,10 @@ // //===----------------------------------------------------------------------===// -#include "SourceKit/Support/FuzzyStringMatcher.h" +#include "swift/IDE/FuzzyStringMatcher.h" #include "gtest/gtest.h" -using FuzzyStringMatcher = SourceKit::FuzzyStringMatcher; +using FuzzyStringMatcher = swift::ide::FuzzyStringMatcher; TEST(FuzzyStringMatcher, BasicMatching) { { From 34de805d7fa0080e5fdad87a5bad06312271c4ad Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 09:52:28 -0700 Subject: [PATCH 007/120] [gardening] Move source text printing alongside other completion methods --- .../swift/IDE/CodeCompletionResultPrinter.h | 3 + lib/IDE/CodeCompletionResultPrinter.cpp | 113 ++++++++++++++++ .../lib/SwiftLang/SwiftCompletion.cpp | 123 +----------------- 3 files changed, 118 insertions(+), 121 deletions(-) diff --git a/include/swift/IDE/CodeCompletionResultPrinter.h b/include/swift/IDE/CodeCompletionResultPrinter.h index 717d6282a4309..05d4cb0f473b7 100644 --- a/include/swift/IDE/CodeCompletionResultPrinter.h +++ b/include/swift/IDE/CodeCompletionResultPrinter.h @@ -34,6 +34,9 @@ void printCodeCompletionResultTypeName( void printCodeCompletionResultTypeNameAnnotated( const CodeCompletionResult &Result, llvm::raw_ostream &OS); +void printCodeCompletionResultSourceText( + const CodeCompletionResult &Result, llvm::raw_ostream &OS); + } // namespace ide } // namespace swift diff --git a/lib/IDE/CodeCompletionResultPrinter.cpp b/lib/IDE/CodeCompletionResultPrinter.cpp index e5c9986ab1fde..7da3932d33191 100644 --- a/lib/IDE/CodeCompletionResultPrinter.cpp +++ b/lib/IDE/CodeCompletionResultPrinter.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/Basic/LLVM.h" +#include "swift/AST/ASTPrinter.h" #include "swift/IDE/CodeCompletionResultPrinter.h" #include "swift/IDE/CodeCompletion.h" #include "swift/Markup/XMLUtils.h" @@ -255,3 +256,115 @@ void swift::ide::printCodeCompletionResultTypeNameAnnotated(const CodeCompletion AnnotatingResultPrinter printer(OS); printer.printTypeName(Result); } + +/// Provide the text for the call parameter, including constructing a typed +/// editor placeholder for it. +static void constructTextForCallParam( + ArrayRef ParamGroup, raw_ostream &OS) { + assert(ParamGroup.front().is(ChunkKind::CallParameterBegin)); + + for (; !ParamGroup.empty(); ParamGroup = ParamGroup.slice(1)) { + auto &C = ParamGroup.front(); + if (C.isAnnotation()) + continue; + if (C.is(ChunkKind::CallParameterInternalName) || + C.is(ChunkKind::CallParameterType) || + C.is(ChunkKind::CallParameterTypeBegin) || + C.is(ChunkKind::CallParameterClosureExpr)) { + break; + } + if (!C.hasText()) + continue; + OS << C.getText(); + } + + SmallString<32> DisplayString; + SmallString<32> TypeString; + SmallString<32> ExpansionTypeString; + + for (auto i = ParamGroup.begin(), e = ParamGroup.end(); i != e; ++i) { + auto &C = *i; + if (C.is(ChunkKind::CallParameterTypeBegin)) { + assert(TypeString.empty()); + auto nestingLevel = C.getNestingLevel(); + ++i; + for (; i != e; ++i) { + if (i->endsPreviousNestedGroup(nestingLevel)) + break; + if (!i->isAnnotation() && i->hasText()) { + TypeString += i->getText(); + DisplayString += i->getText(); + } + } + --i; + continue; + } + if (C.is(ChunkKind::CallParameterClosureType)) { + assert(ExpansionTypeString.empty()); + ExpansionTypeString = C.getText(); + continue; + } + if (C.is(ChunkKind::CallParameterType)) { + assert(TypeString.empty()); + TypeString = C.getText(); + } + if (C.is(ChunkKind::CallParameterClosureExpr)) { + // We have a closure expression, so provide it directly instead of in + // a placeholder. + OS << "{"; + if (!C.getText().empty()) + OS << " " << C.getText(); + OS << "\n" << getCodePlaceholder() << "\n}"; + return; + } + if (C.isAnnotation() || !C.hasText()) + continue; + DisplayString += C.getText(); + } + + StringRef Display = DisplayString.str(); + StringRef Type = TypeString.str(); + StringRef ExpansionType = ExpansionTypeString.str(); + if (ExpansionType.empty()) + ExpansionType = Type; + + OS << "<#T##" << Display; + if (Display == Type && Display == ExpansionType) { + // Short version, display and type are the same. + } else { + OS << "##" << Type; + if (ExpansionType != Type) + OS << "##" << ExpansionType; + } + OS << "#>"; +} + +void swift::ide::printCodeCompletionResultSourceText(const CodeCompletionResult &Result, llvm::raw_ostream &OS) { + auto Chunks = Result.getCompletionString()->getChunks(); + for (size_t i = 0; i < Chunks.size(); ++i) { + auto &C = Chunks[i]; + if (C.is(ChunkKind::BraceStmtWithCursor)) { + OS << " {\n" << getCodePlaceholder() << "\n}"; + continue; + } + if (C.is(ChunkKind::CallParameterBegin)) { + size_t Start = i++; + for (; i < Chunks.size(); ++i) { + if (Chunks[i].endsPreviousNestedGroup(C.getNestingLevel())) + break; + } + constructTextForCallParam(Chunks.slice(Start, i-Start), OS); + --i; + continue; + } + if (C.is(ChunkKind::TypeAnnotationBegin)) { + // Skip type annotation structure. + auto level = C.getNestingLevel(); + do { ++i; } while (i != Chunks.size() && !Chunks[i].endsPreviousNestedGroup(level)); + --i; + } + if (!C.isAnnotation() && C.hasText()) { + OS << C.getText(); + } + } +} diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 0870461fede85..c0ab78cb37825 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -75,9 +75,6 @@ struct SwiftToSourceKitCompletionAdapter { bool legacyLiteralToKeyword, bool annotatedDescription); - static void getResultSourceText(const CodeCompletionString *CCStr, - raw_ostream &OS); - static void getResultAssociatedUSRs(ArrayRef AssocUSRs, raw_ostream &OS); }; @@ -466,7 +463,7 @@ bool SwiftToSourceKitCompletionAdapter::handleResult( unsigned TextBegin = SS.size(); { llvm::raw_svector_ostream ccOS(SS); - getResultSourceText(Result->getCompletionString(), ccOS); + ide::printCodeCompletionResultSourceText(*Result, ccOS); } unsigned TextEnd = SS.size(); @@ -603,121 +600,6 @@ getCodeCompletionKeywordKindForUID(UIdent uid) { return CodeCompletionKeywordKind::None; } -using ChunkKind = CodeCompletionString::Chunk::ChunkKind; - -/// Provide the text for the call parameter, including constructing a typed -/// editor placeholder for it. -static void constructTextForCallParam( - ArrayRef ParamGroup, raw_ostream &OS) { - assert(ParamGroup.front().is(ChunkKind::CallParameterBegin)); - - for (; !ParamGroup.empty(); ParamGroup = ParamGroup.slice(1)) { - auto &C = ParamGroup.front(); - if (C.isAnnotation()) - continue; - if (C.is(ChunkKind::CallParameterInternalName) || - C.is(ChunkKind::CallParameterType) || - C.is(ChunkKind::CallParameterTypeBegin) || - C.is(ChunkKind::CallParameterClosureExpr)) { - break; - } - if (!C.hasText()) - continue; - OS << C.getText(); - } - - SmallString<32> DisplayString; - SmallString<32> TypeString; - SmallString<32> ExpansionTypeString; - - for (auto i = ParamGroup.begin(), e = ParamGroup.end(); i != e; ++i) { - auto &C = *i; - if (C.is(ChunkKind::CallParameterTypeBegin)) { - assert(TypeString.empty()); - auto nestingLevel = C.getNestingLevel(); - ++i; - for (; i != e; ++i) { - if (i->endsPreviousNestedGroup(nestingLevel)) - break; - if (!i->isAnnotation() && i->hasText()) { - TypeString += i->getText(); - DisplayString += i->getText(); - } - } - --i; - continue; - } - if (C.is(ChunkKind::CallParameterClosureType)) { - assert(ExpansionTypeString.empty()); - ExpansionTypeString = C.getText(); - continue; - } - if (C.is(ChunkKind::CallParameterType)) { - assert(TypeString.empty()); - TypeString = C.getText(); - } - if (C.is(ChunkKind::CallParameterClosureExpr)) { - // We have a closure expression, so provide it directly instead of in - // a placeholder. - OS << "{"; - if (!C.getText().empty()) - OS << " " << C.getText(); - OS << "\n" << getCodePlaceholder() << "\n}"; - return; - } - if (C.isAnnotation() || !C.hasText()) - continue; - DisplayString += C.getText(); - } - - StringRef Display = DisplayString.str(); - StringRef Type = TypeString.str(); - StringRef ExpansionType = ExpansionTypeString.str(); - if (ExpansionType.empty()) - ExpansionType = Type; - - OS << "<#T##" << Display; - if (Display == Type && Display == ExpansionType) { - // Short version, display and type are the same. - } else { - OS << "##" << Type; - if (ExpansionType != Type) - OS << "##" << ExpansionType; - } - OS << "#>"; -} - -void SwiftToSourceKitCompletionAdapter::getResultSourceText( - const CodeCompletionString *CCStr, raw_ostream &OS) { - auto Chunks = CCStr->getChunks(); - for (size_t i = 0; i < Chunks.size(); ++i) { - auto &C = Chunks[i]; - if (C.is(ChunkKind::BraceStmtWithCursor)) { - OS << " {\n" << getCodePlaceholder() << "\n}"; - continue; - } - if (C.is(ChunkKind::CallParameterBegin)) { - size_t Start = i++; - for (; i < Chunks.size(); ++i) { - if (Chunks[i].endsPreviousNestedGroup(C.getNestingLevel())) - break; - } - constructTextForCallParam(Chunks.slice(Start, i-Start), OS); - --i; - continue; - } - if (C.is(ChunkKind::TypeAnnotationBegin)) { - // Skip type annotation structure. - auto level = C.getNestingLevel(); - do { ++i; } while (i != Chunks.size() && !Chunks[i].endsPreviousNestedGroup(level)); - --i; - } - if (!C.isAnnotation() && C.hasText()) { - OS << C.getText(); - } - } -} - void SwiftToSourceKitCompletionAdapter::getResultAssociatedUSRs( ArrayRef AssocUSRs, raw_ostream &OS) { bool First = true; @@ -1140,8 +1022,7 @@ static void transformAndForwardResults( std::string str = inputBuf->getBuffer().slice(0, offset).str(); { llvm::raw_string_ostream OSS(str); - SwiftToSourceKitCompletionAdapter::getResultSourceText( - exactMatch->getCompletionString(), OSS); + ide::printCodeCompletionResultSourceText(*exactMatch, OSS); } auto buffer = From cf87ad805f3b56ccd7c13154ca1840675e4c4c89 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 10:34:14 -0700 Subject: [PATCH 008/120] [gardening] Move filter name printing alongside other completion methods --- .../swift/IDE/CodeCompletionResultPrinter.h | 3 + lib/IDE/CodeCompletionResultPrinter.cpp | 64 +++++++++++++++++ .../SourceKit/lib/SwiftLang/CodeCompletion.h | 6 -- .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 70 +------------------ .../lib/SwiftLang/SwiftCompletion.cpp | 6 +- 5 files changed, 71 insertions(+), 78 deletions(-) diff --git a/include/swift/IDE/CodeCompletionResultPrinter.h b/include/swift/IDE/CodeCompletionResultPrinter.h index 05d4cb0f473b7..86e1233246dc9 100644 --- a/include/swift/IDE/CodeCompletionResultPrinter.h +++ b/include/swift/IDE/CodeCompletionResultPrinter.h @@ -37,6 +37,9 @@ void printCodeCompletionResultTypeNameAnnotated( void printCodeCompletionResultSourceText( const CodeCompletionResult &Result, llvm::raw_ostream &OS); +void printCodeCompletionResultFilterName( + const CodeCompletionResult &Result, llvm::raw_ostream &OS); + } // namespace ide } // namespace swift diff --git a/lib/IDE/CodeCompletionResultPrinter.cpp b/lib/IDE/CodeCompletionResultPrinter.cpp index 7da3932d33191..1e04e6a599702 100644 --- a/lib/IDE/CodeCompletionResultPrinter.cpp +++ b/lib/IDE/CodeCompletionResultPrinter.cpp @@ -368,3 +368,67 @@ void swift::ide::printCodeCompletionResultSourceText(const CodeCompletionResult } } } + +void swift::ide::printCodeCompletionResultFilterName(const CodeCompletionResult &Result, llvm::raw_ostream &OS) { + auto str = Result.getCompletionString(); + // FIXME: we need a more uniform way to handle operator completions. + if (str->getChunks().size() == 1 && str->getChunks()[0].is(ChunkKind::Dot)) { + OS << "."; + return; + } else if (str->getChunks().size() == 2 && + str->getChunks()[0].is(ChunkKind::QuestionMark) && + str->getChunks()[1].is(ChunkKind::Dot)) { + OS << "?."; + return; + } + + auto FirstTextChunk = str->getFirstTextChunkIndex(); + if (FirstTextChunk.hasValue()) { + auto chunks = str->getChunks().slice(*FirstTextChunk); + for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i) { + auto &C = *i; + + if (C.is(ChunkKind::BraceStmtWithCursor)) + break; // Don't include brace-stmt in filter name. + + if (C.is(ChunkKind::Equal)) { + OS << C.getText(); + break; + } + + bool shouldPrint = !C.isAnnotation(); + switch (C.getKind()) { + case ChunkKind::TypeAnnotation: + case ChunkKind::CallParameterInternalName: + case ChunkKind::CallParameterClosureType: + case ChunkKind::CallParameterClosureExpr: + case ChunkKind::CallParameterType: + case ChunkKind::DeclAttrParamColon: + case ChunkKind::Comma: + case ChunkKind::Whitespace: + case ChunkKind::Ellipsis: + case ChunkKind::Ampersand: + case ChunkKind::OptionalMethodCallTail: + continue; + case ChunkKind::CallParameterTypeBegin: + case ChunkKind::TypeAnnotationBegin: { + // Skip call parameter type or type annotation structure. + auto nestingLevel = C.getNestingLevel(); + do { ++i; } while (i != e && !i->endsPreviousNestedGroup(nestingLevel)); + --i; + continue; + } + case ChunkKind::CallParameterColon: + // Since we don't add the type, also don't add the space after ':'. + if (shouldPrint) + OS << ":"; + continue; + default: + break; + } + + if (C.hasText() && shouldPrint) + OS << C.getText(); + } + } +} diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletion.h b/tools/SourceKit/lib/SwiftLang/CodeCompletion.h index 266282ab3f15b..f8098fbaf8390 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletion.h +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletion.h @@ -132,12 +132,6 @@ class CompletionBuilder { Optional moduleImportDepth; PopularityFactor popularityFactor; -public: - static void getFilterName(CodeCompletionString *str, raw_ostream &OS); - static void getDescription(SwiftResult *result, raw_ostream &OS, - bool leadingPunctuation, - bool annotatedDecription = false); - public: CompletionBuilder(CompletionSink &sink, SwiftResult &base); diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index a7864e91cc65a..c24e17a0e1afd 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -1209,72 +1209,6 @@ bool LimitedResultView::walk(CodeCompletionView::Walker &walker) const { // CompletionBuilder //===----------------------------------------------------------------------===// -void CompletionBuilder::getFilterName(CodeCompletionString *str, - raw_ostream &OS) { - using ChunkKind = CodeCompletionString::Chunk::ChunkKind; - - // FIXME: we need a more uniform way to handle operator completions. - if (str->getChunks().size() == 1 && str->getChunks()[0].is(ChunkKind::Dot)) { - OS << "."; - return; - } else if (str->getChunks().size() == 2 && - str->getChunks()[0].is(ChunkKind::QuestionMark) && - str->getChunks()[1].is(ChunkKind::Dot)) { - OS << "?."; - return; - } - - auto FirstTextChunk = str->getFirstTextChunkIndex(); - if (FirstTextChunk.hasValue()) { - auto chunks = str->getChunks().slice(*FirstTextChunk); - for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i) { - auto &C = *i; - - if (C.is(ChunkKind::BraceStmtWithCursor)) - break; // Don't include brace-stmt in filter name. - - if (C.is(ChunkKind::Equal)) { - OS << C.getText(); - break; - } - - bool shouldPrint = !C.isAnnotation(); - switch (C.getKind()) { - case ChunkKind::TypeAnnotation: - case ChunkKind::CallParameterInternalName: - case ChunkKind::CallParameterClosureType: - case ChunkKind::CallParameterClosureExpr: - case ChunkKind::CallParameterType: - case ChunkKind::DeclAttrParamColon: - case ChunkKind::Comma: - case ChunkKind::Whitespace: - case ChunkKind::Ellipsis: - case ChunkKind::Ampersand: - case ChunkKind::OptionalMethodCallTail: - continue; - case ChunkKind::CallParameterTypeBegin: - case ChunkKind::TypeAnnotationBegin: { - // Skip call parameter type or type annotation structure. - auto nestingLevel = C.getNestingLevel(); - do { ++i; } while (i != e && !i->endsPreviousNestedGroup(nestingLevel)); - --i; - continue; - } - case ChunkKind::CallParameterColon: - // Since we don't add the type, also don't add the space after ':'. - if (shouldPrint) - OS << ":"; - continue; - default: - break; - } - - if (C.hasText() && shouldPrint) - OS << C.getText(); - } - } -} - CompletionBuilder::CompletionBuilder(CompletionSink &sink, SwiftResult &base) : sink(sink), current(base) { typeRelation = current.getExpectedTypeRelation(); @@ -1286,7 +1220,7 @@ CompletionBuilder::CompletionBuilder(CompletionSink &sink, SwiftResult &base) // strings for our inner "." result. if (current.getCompletionString()->getFirstTextChunkIndex().hasValue()) { llvm::raw_svector_ostream OSS(originalName); - getFilterName(current.getCompletionString(), OSS); + ide::printCodeCompletionResultFilterName(current, OSS); } } @@ -1336,7 +1270,7 @@ Completion *CompletionBuilder::finish() { } llvm::raw_svector_ostream OSS(nameStorage); - getFilterName(base.getCompletionString(), OSS); + ide::printCodeCompletionResultFilterName(base, OSS); name = OSS.str(); } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index c0ab78cb37825..06d3a912d4b51 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -49,8 +49,7 @@ struct SwiftToSourceKitCompletionAdapter { llvm::SmallString<64> name; { llvm::raw_svector_ostream OSS(name); - CodeCompletion::CompletionBuilder::getFilterName( - result->getCompletionString(), OSS); + ide::printCodeCompletionResultFilterName(*result, OSS); } llvm::SmallString<64> description; @@ -887,8 +886,7 @@ filterInnerResults(ArrayRef results, bool includeInner, llvm::SmallString<64> filterName; { llvm::raw_svector_ostream OSS(filterName); - CodeCompletion::CompletionBuilder::getFilterName( - result->getCompletionString(), OSS); + ide::printCodeCompletionResultFilterName(*result, OSS); } llvm::SmallString<64> description; { From af78895c453398575151ace94ee5732451060b2f Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 10:20:18 -0700 Subject: [PATCH 009/120] [gardening] Sink compiler invocation code into libIDE --- include/swift/IDE/Utils.h | 8 + lib/IDE/Utils.cpp | 185 ++++++++++++++++++ .../lib/SwiftLang/SwiftASTManager.cpp | 166 +--------------- 3 files changed, 194 insertions(+), 165 deletions(-) diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index f847b188f1b42..8eaf34a69b5e7 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -81,6 +81,14 @@ SourceCompleteResult isSourceInputComplete(std::unique_ptr MemBuf, SourceFileKind SFKind); SourceCompleteResult isSourceInputComplete(StringRef Text, SourceFileKind SFKind); +bool initCompilerInvocation( + CompilerInvocation &Invocation, ArrayRef OrigArgs, + DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile, + llvm::IntrusiveRefCntPtr FileSystem, + const std::string &runtimeResourcePath, + const std::string &diagnosticDocumentationPath, + bool shouldOptimizeForIDE, time_t sessionTimestamp, std::string &Error); + bool initInvocationByClangArguments(ArrayRef ArgList, CompilerInvocation &Invok, std::string &Error); diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 616a0e6e0678f..2133ac9ecf41f 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -14,6 +14,7 @@ #include "swift/Basic/Edit.h" #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/Driver/FrontendUtil.h" #include "swift/Frontend/Frontend.h" #include "swift/Parse/Parser.h" #include "swift/Subsystems.h" @@ -226,6 +227,190 @@ static std::string adjustClangTriple(StringRef TripleStr) { return Result; } +static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( + FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile, + llvm::IntrusiveRefCntPtr FileSystem, + std::string &Error) { + assert(FileSystem); + + llvm::SmallString<128> PrimaryFile; + if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile)) + PrimaryFile = UnresolvedPrimaryFile; + + unsigned primaryCount = 0; + // FIXME: The frontend should be dealing with symlinks, maybe similar to + // clang's FileManager ? + FrontendInputsAndOutputs replacementInputsAndOutputs; + for (const InputFile &input : inputsAndOutputs.getAllInputs()) { + llvm::SmallString<128> newFilename; + if (auto err = FileSystem->getRealPath(input.file(), newFilename)) + newFilename = input.file(); + llvm::sys::path::native(newFilename); + bool newIsPrimary = input.isPrimary() || + (!PrimaryFile.empty() && PrimaryFile == newFilename); + if (newIsPrimary) { + ++primaryCount; + } + assert(primaryCount < 2 && "cannot handle multiple primaries"); + replacementInputsAndOutputs.addInput( + InputFile(newFilename.str(), newIsPrimary, input.buffer())); + } + + if (PrimaryFile.empty() || primaryCount == 1) { + return replacementInputsAndOutputs; + } + + llvm::SmallString<64> Err; + llvm::raw_svector_ostream OS(Err); + OS << "'" << PrimaryFile << "' is not part of the input files"; + Error = std::string(OS.str()); + return replacementInputsAndOutputs; +} + +static void disableExpensiveSILOptions(SILOptions &Opts) { + // Disable the sanitizers. + Opts.Sanitizers = {}; + + // Disable PGO and code coverage. + Opts.GenerateProfile = false; + Opts.EmitProfileCoverageMapping = false; + Opts.UseProfile = ""; +} + +namespace { +class StreamDiagConsumer : public DiagnosticConsumer { + llvm::raw_ostream &OS; + +public: + StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {} + + void handleDiagnostic(SourceManager &SM, + const DiagnosticInfo &Info) override { + // FIXME: Print location info if available. + switch (Info.Kind) { + case DiagnosticKind::Error: + OS << "error: "; + break; + case DiagnosticKind::Warning: + OS << "warning: "; + break; + case DiagnosticKind::Note: + OS << "note: "; + break; + case DiagnosticKind::Remark: + OS << "remark: "; + break; + } + DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, + Info.FormatArgs); + } +}; +} // end anonymous namespace + +bool ide::initCompilerInvocation( + CompilerInvocation &Invocation, ArrayRef OrigArgs, + DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile, + llvm::IntrusiveRefCntPtr FileSystem, + const std::string &runtimeResourcePath, + const std::string &diagnosticDocumentationPath, + bool shouldOptimizeForIDE, time_t sessionTimestamp, std::string &Error) { + SmallVector Args; + // Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at + // the top to allow overriding them with the passed in arguments. + Args.push_back("-resource-dir"); + Args.push_back(runtimeResourcePath.c_str()); + Args.push_back("-Xfrontend"); + Args.push_back("-diagnostic-documentation-path"); + Args.push_back("-Xfrontend"); + Args.push_back(diagnosticDocumentationPath.c_str()); + Args.append(OrigArgs.begin(), OrigArgs.end()); + + SmallString<32> ErrStr; + llvm::raw_svector_ostream ErrOS(ErrStr); + StreamDiagConsumer DiagConsumer(ErrOS); + Diags.addConsumer(DiagConsumer); + + bool HadError = driver::getSingleFrontendInvocationFromDriverArguments( + Args, Diags, [&](ArrayRef FrontendArgs) { + return Invocation.parseArgs(FrontendArgs, Diags); + }, /*ForceNoOutputs=*/true); + + // Remove the StreamDiagConsumer as it's no longer needed. + Diags.removeConsumer(DiagConsumer); + + if (HadError) { + Error = std::string(ErrOS.str()); + return true; + } + + Invocation.getFrontendOptions().InputsAndOutputs = + resolveSymbolicLinksInInputs( + Invocation.getFrontendOptions().InputsAndOutputs, + UnresolvedPrimaryFile, FileSystem, Error); + if (!Error.empty()) + return true; + + ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions(); + ImporterOpts.DetailedPreprocessingRecord = true; + + assert(!Invocation.getModuleName().empty()); + + auto &LangOpts = Invocation.getLangOptions(); + LangOpts.AttachCommentsToDecls = true; + LangOpts.DiagnosticsEditorMode = true; + LangOpts.CollectParsedToken = true; + if (LangOpts.PlaygroundTransform) { + // The playground instrumenter changes the AST in ways that disrupt the + // SourceKit functionality. Since we don't need the instrumenter, and all we + // actually need is the playground semantics visible to the user, like + // silencing the "expression resolves to an unused l-value" error, disable it. + LangOpts.PlaygroundTransform = false; + } + + // Disable the index-store functionality for the sourcekitd requests. + auto &FrontendOpts = Invocation.getFrontendOptions(); + FrontendOpts.IndexStorePath.clear(); + ImporterOpts.IndexStorePath.clear(); + + // Force the action type to be -typecheck. This affects importing the + // SwiftONoneSupport module. + FrontendOpts.RequestedAction = FrontendOptions::ActionType::Typecheck; + + // We don't care about LLVMArgs + FrontendOpts.LLVMArgs.clear(); + + // SwiftSourceInfo files provide source location information for decls coming + // from loaded modules. For most IDE use cases it either has an undesirable + // impact on performance with no benefit (code completion), results in stale + // locations being used instead of more up-to-date indexer locations (cursor + // info), or has no observable effect (diagnostics, which are filtered to just + // those with a location in the primary file, and everything else). + if (shouldOptimizeForIDE) + FrontendOpts.IgnoreSwiftSourceInfo = true; + + // To save the time for module validation, consider the lifetime of ASTManager + // as a single build session. + // NOTE: Do this only if '-disable-modules-validate-system-headers' is *not* + // explicitly enabled. + auto &SearchPathOpts = Invocation.getSearchPathOptions(); + if (!SearchPathOpts.DisableModulesValidateSystemDependencies) { + // NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may + // cause unnecessary validations if they happens within one second + // from the SourceKit startup. + ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" + + std::to_string(sessionTimestamp - 1)); + ImporterOpts.ExtraArgs.push_back( + "-fmodules-validate-once-per-build-session"); + + SearchPathOpts.DisableModulesValidateSystemDependencies = true; + } + + // Disable expensive SIL options to reduce time spent in SILGen. + disableExpensiveSILOptions(Invocation.getSILOptions()); + + return false; +} + bool ide::initInvocationByClangArguments(ArrayRef ArgList, CompilerInvocation &Invok, std::string &Error) { diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 7b30a003845b7..9bc69165c0183 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -40,36 +40,6 @@ using namespace SourceKit; using namespace swift; using namespace swift::sys; -namespace { -class StreamDiagConsumer : public DiagnosticConsumer { - llvm::raw_ostream &OS; - -public: - StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {} - - void handleDiagnostic(SourceManager &SM, - const DiagnosticInfo &Info) override { - // FIXME: Print location info if available. - switch (Info.Kind) { - case DiagnosticKind::Error: - OS << "error: "; - break; - case DiagnosticKind::Warning: - OS << "warning: "; - break; - case DiagnosticKind::Note: - OS << "note: "; - break; - case DiagnosticKind::Remark: - OS << "remark: "; - break; - } - DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, - Info.FormatArgs); - } -}; -} // end anonymous namespace - void SwiftASTConsumer::failed(StringRef Error) { } //===----------------------------------------------------------------------===// @@ -433,46 +403,6 @@ convertFileContentsToInputs(const SmallVectorImpl &contents) { return inputsAndOutputs; } -static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( - FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile, - llvm::IntrusiveRefCntPtr FileSystem, - std::string &Error) { - assert(FileSystem); - - llvm::SmallString<128> PrimaryFile; - if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile)) - PrimaryFile = UnresolvedPrimaryFile; - - unsigned primaryCount = 0; - // FIXME: The frontend should be dealing with symlinks, maybe similar to - // clang's FileManager ? - FrontendInputsAndOutputs replacementInputsAndOutputs; - for (const InputFile &input : inputsAndOutputs.getAllInputs()) { - llvm::SmallString<128> newFilename; - if (auto err = FileSystem->getRealPath(input.file(), newFilename)) - newFilename = input.file(); - llvm::sys::path::native(newFilename); - bool newIsPrimary = input.isPrimary() || - (!PrimaryFile.empty() && PrimaryFile == newFilename); - if (newIsPrimary) { - ++primaryCount; - } - assert(primaryCount < 2 && "cannot handle multiple primaries"); - replacementInputsAndOutputs.addInput( - InputFile(newFilename.str(), newIsPrimary, input.buffer())); - } - - if (PrimaryFile.empty() || primaryCount == 1) { - return replacementInputsAndOutputs; - } - - llvm::SmallString<64> Err; - llvm::raw_svector_ostream OS(Err); - OS << "'" << PrimaryFile << "' is not part of the input files"; - Error = std::string(OS.str()); - return replacementInputsAndOutputs; -} - bool SwiftASTManager::initCompilerInvocation( CompilerInvocation &Invocation, ArrayRef OrigArgs, DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile, @@ -488,101 +418,7 @@ bool SwiftASTManager::initCompilerInvocation( DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - SmallVector Args; - // Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at - // the top to allow overriding them with the passed in arguments. - Args.push_back("-resource-dir"); - Args.push_back(Impl.RuntimeResourcePath.c_str()); - Args.push_back("-Xfrontend"); - Args.push_back("-diagnostic-documentation-path"); - Args.push_back("-Xfrontend"); - Args.push_back(Impl.DiagnosticDocumentationPath.c_str()); - Args.append(OrigArgs.begin(), OrigArgs.end()); - - SmallString<32> ErrStr; - llvm::raw_svector_ostream ErrOS(ErrStr); - StreamDiagConsumer DiagConsumer(ErrOS); - Diags.addConsumer(DiagConsumer); - - bool HadError = driver::getSingleFrontendInvocationFromDriverArguments( - Args, Diags, [&](ArrayRef FrontendArgs) { - return Invocation.parseArgs(FrontendArgs, Diags); - }, /*ForceNoOutputs=*/true); - - // Remove the StreamDiagConsumer as it's no longer needed. - Diags.removeConsumer(DiagConsumer); - - if (HadError) { - Error = std::string(ErrOS.str()); - return true; - } - - Invocation.getFrontendOptions().InputsAndOutputs = - resolveSymbolicLinksInInputs( - Invocation.getFrontendOptions().InputsAndOutputs, - UnresolvedPrimaryFile, FileSystem, Error); - if (!Error.empty()) - return true; - - ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions(); - ImporterOpts.DetailedPreprocessingRecord = true; - - assert(!Invocation.getModuleName().empty()); - - auto &LangOpts = Invocation.getLangOptions(); - LangOpts.AttachCommentsToDecls = true; - LangOpts.DiagnosticsEditorMode = true; - LangOpts.CollectParsedToken = true; - if (LangOpts.PlaygroundTransform) { - // The playground instrumenter changes the AST in ways that disrupt the - // SourceKit functionality. Since we don't need the instrumenter, and all we - // actually need is the playground semantics visible to the user, like - // silencing the "expression resolves to an unused l-value" error, disable it. - LangOpts.PlaygroundTransform = false; - } - - // Disable the index-store functionality for the sourcekitd requests. - auto &FrontendOpts = Invocation.getFrontendOptions(); - FrontendOpts.IndexStorePath.clear(); - ImporterOpts.IndexStorePath.clear(); - - // Force the action type to be -typecheck. This affects importing the - // SwiftONoneSupport module. - FrontendOpts.RequestedAction = FrontendOptions::ActionType::Typecheck; - - // We don't care about LLVMArgs - FrontendOpts.LLVMArgs.clear(); - - // SwiftSourceInfo files provide source location information for decls coming - // from loaded modules. For most IDE use cases it either has an undesirable - // impact on performance with no benefit (code completion), results in stale - // locations being used instead of more up-to-date indexer locations (cursor - // info), or has no observable effect (diagnostics, which are filtered to just - // those with a location in the primary file, and everything else). - if (Impl.Config->shouldOptimizeForIDE()) - FrontendOpts.IgnoreSwiftSourceInfo = true; - - // To save the time for module validation, consider the lifetime of ASTManager - // as a single build session. - // NOTE: Do this only if '-disable-modules-validate-system-headers' is *not* - // explicitly enabled. - auto &SearchPathOpts = Invocation.getSearchPathOptions(); - if (!SearchPathOpts.DisableModulesValidateSystemDependencies) { - // NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may - // cause unnecessary validations if they happens within one second - // from the SourceKit startup. - ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" + - std::to_string(Impl.SessionTimestamp - 1)); - ImporterOpts.ExtraArgs.push_back( - "-fmodules-validate-once-per-build-session"); - - SearchPathOpts.DisableModulesValidateSystemDependencies = true; - } - - // Disable expensive SIL options to reduce time spent in SILGen. - disableExpensiveSILOptions(Invocation.getSILOptions()); - - return false; + return ide::initCompilerInvocation(Invocation, OrigArgs, Diags, UnresolvedPrimaryFile, FileSystem, Impl.RuntimeResourcePath, Impl.DiagnosticDocumentationPath, Impl.Config->shouldOptimizeForIDE(), Impl.SessionTimestamp, Error); } bool SwiftASTManager::initCompilerInvocation(CompilerInvocation &CompInvok, From 0d42d7d915357f73d0c5ff4040d6be3200817a0a Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 10:43:29 -0700 Subject: [PATCH 010/120] [gardening] format changed code --- lib/IDE/CodeCompletionResultPrinter.cpp | 27 ++++++++++++------- .../lib/SwiftLang/SwiftASTManager.cpp | 5 +++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/IDE/CodeCompletionResultPrinter.cpp b/lib/IDE/CodeCompletionResultPrinter.cpp index 1e04e6a599702..7b61aa1835153 100644 --- a/lib/IDE/CodeCompletionResultPrinter.cpp +++ b/lib/IDE/CodeCompletionResultPrinter.cpp @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -#include "swift/Basic/LLVM.h" -#include "swift/AST/ASTPrinter.h" #include "swift/IDE/CodeCompletionResultPrinter.h" +#include "swift/AST/ASTPrinter.h" +#include "swift/Basic/LLVM.h" #include "swift/IDE/CodeCompletion.h" #include "swift/Markup/XMLUtils.h" #include "llvm/Support/raw_ostream.h" @@ -259,8 +259,9 @@ void swift::ide::printCodeCompletionResultTypeNameAnnotated(const CodeCompletion /// Provide the text for the call parameter, including constructing a typed /// editor placeholder for it. -static void constructTextForCallParam( - ArrayRef ParamGroup, raw_ostream &OS) { +static void +constructTextForCallParam(ArrayRef ParamGroup, + raw_ostream &OS) { assert(ParamGroup.front().is(ChunkKind::CallParameterBegin)); for (; !ParamGroup.empty(); ParamGroup = ParamGroup.slice(1)) { @@ -339,7 +340,8 @@ static void constructTextForCallParam( OS << "#>"; } -void swift::ide::printCodeCompletionResultSourceText(const CodeCompletionResult &Result, llvm::raw_ostream &OS) { +void swift::ide::printCodeCompletionResultSourceText( + const CodeCompletionResult &Result, llvm::raw_ostream &OS) { auto Chunks = Result.getCompletionString()->getChunks(); for (size_t i = 0; i < Chunks.size(); ++i) { auto &C = Chunks[i]; @@ -353,14 +355,16 @@ void swift::ide::printCodeCompletionResultSourceText(const CodeCompletionResult if (Chunks[i].endsPreviousNestedGroup(C.getNestingLevel())) break; } - constructTextForCallParam(Chunks.slice(Start, i-Start), OS); + constructTextForCallParam(Chunks.slice(Start, i - Start), OS); --i; continue; } if (C.is(ChunkKind::TypeAnnotationBegin)) { // Skip type annotation structure. auto level = C.getNestingLevel(); - do { ++i; } while (i != Chunks.size() && !Chunks[i].endsPreviousNestedGroup(level)); + do { + ++i; + } while (i != Chunks.size() && !Chunks[i].endsPreviousNestedGroup(level)); --i; } if (!C.isAnnotation() && C.hasText()) { @@ -369,7 +373,8 @@ void swift::ide::printCodeCompletionResultSourceText(const CodeCompletionResult } } -void swift::ide::printCodeCompletionResultFilterName(const CodeCompletionResult &Result, llvm::raw_ostream &OS) { +void swift::ide::printCodeCompletionResultFilterName( + const CodeCompletionResult &Result, llvm::raw_ostream &OS) { auto str = Result.getCompletionString(); // FIXME: we need a more uniform way to handle operator completions. if (str->getChunks().size() == 1 && str->getChunks()[0].is(ChunkKind::Dot)) { @@ -414,7 +419,9 @@ void swift::ide::printCodeCompletionResultFilterName(const CodeCompletionResult case ChunkKind::TypeAnnotationBegin: { // Skip call parameter type or type annotation structure. auto nestingLevel = C.getNestingLevel(); - do { ++i; } while (i != e && !i->endsPreviousNestedGroup(nestingLevel)); + do { + ++i; + } while (i != e && !i->endsPreviousNestedGroup(nestingLevel)); --i; continue; } @@ -430,5 +437,5 @@ void swift::ide::printCodeCompletionResultFilterName(const CodeCompletionResult if (C.hasText() && shouldPrint) OS << C.getText(); } - } + } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 9bc69165c0183..dfc5ea55b7daf 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -418,7 +418,10 @@ bool SwiftASTManager::initCompilerInvocation( DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile, llvm::IntrusiveRefCntPtr FileSystem, std::string &Error) { - return ide::initCompilerInvocation(Invocation, OrigArgs, Diags, UnresolvedPrimaryFile, FileSystem, Impl.RuntimeResourcePath, Impl.DiagnosticDocumentationPath, Impl.Config->shouldOptimizeForIDE(), Impl.SessionTimestamp, Error); + return ide::initCompilerInvocation( + Invocation, OrigArgs, Diags, UnresolvedPrimaryFile, FileSystem, + Impl.RuntimeResourcePath, Impl.DiagnosticDocumentationPath, + Impl.Config->shouldOptimizeForIDE(), Impl.SessionTimestamp, Error); } bool SwiftASTManager::initCompilerInvocation(CompilerInvocation &CompInvok, From 81786104f356b523fb5582c5725041f8fc7e4ce2 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 11:16:53 -0700 Subject: [PATCH 011/120] Move adjustClangTriple above its only caller --- lib/IDE/Utils.cpp | 76 +++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 2133ac9ecf41f..5d0dda4b8e555 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -189,44 +189,6 @@ ide::isSourceInputComplete(StringRef Text,SourceFileKind SFKind) { SFKind); } -// Adjust the cc1 triple string we got from clang, to make sure it will be -// accepted when it goes through the swift clang importer. -static std::string adjustClangTriple(StringRef TripleStr) { - std::string Result; - llvm::raw_string_ostream OS(Result); - - llvm::Triple Triple(TripleStr); - switch (Triple.getSubArch()) { - case llvm::Triple::SubArchType::ARMSubArch_v7: - OS << "armv7"; break; - case llvm::Triple::SubArchType::ARMSubArch_v7s: - OS << "armv7s"; break; - case llvm::Triple::SubArchType::ARMSubArch_v7k: - OS << "armv7k"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6: - OS << "armv6"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6m: - OS << "armv6m"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6k: - OS << "armv6k"; break; - case llvm::Triple::SubArchType::ARMSubArch_v6t2: - OS << "armv6t2"; break; - default: - // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386. - if ((Triple.getOS() == llvm::Triple::MacOSX || - Triple.getOS() == llvm::Triple::Darwin) && Triple.getArch() == llvm::Triple::x86) { - OS << "x86_64"; - } else { - OS << Triple.getArchName(); - } - break; - } - OS << '-' << Triple.getVendorName() << '-' << - Triple.getOSAndEnvironmentName(); - OS.flush(); - return Result; -} - static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile, llvm::IntrusiveRefCntPtr FileSystem, @@ -411,6 +373,44 @@ bool ide::initCompilerInvocation( return false; } +// Adjust the cc1 triple string we got from clang, to make sure it will be +// accepted when it goes through the swift clang importer. +static std::string adjustClangTriple(StringRef TripleStr) { + std::string Result; + llvm::raw_string_ostream OS(Result); + + llvm::Triple Triple(TripleStr); + switch (Triple.getSubArch()) { + case llvm::Triple::SubArchType::ARMSubArch_v7: + OS << "armv7"; break; + case llvm::Triple::SubArchType::ARMSubArch_v7s: + OS << "armv7s"; break; + case llvm::Triple::SubArchType::ARMSubArch_v7k: + OS << "armv7k"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6: + OS << "armv6"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6m: + OS << "armv6m"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6k: + OS << "armv6k"; break; + case llvm::Triple::SubArchType::ARMSubArch_v6t2: + OS << "armv6t2"; break; + default: + // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386. + if ((Triple.getOS() == llvm::Triple::MacOSX || + Triple.getOS() == llvm::Triple::Darwin) && Triple.getArch() == llvm::Triple::x86) { + OS << "x86_64"; + } else { + OS << Triple.getArchName(); + } + break; + } + OS << '-' << Triple.getVendorName() << '-' << + Triple.getOSAndEnvironmentName(); + OS.flush(); + return Result; +} + bool ide::initInvocationByClangArguments(ArrayRef ArgList, CompilerInvocation &Invok, std::string &Error) { From 696c124af7b1031dfcadeef89797e0f8bad95dd6 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 11:45:11 -0700 Subject: [PATCH 012/120] Fix missing dependency --- lib/IDE/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index e7529a25a89d5..d028e874d39b7 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -22,6 +22,7 @@ add_swift_host_library(swiftIDE STATIC target_link_libraries(swiftIDE PRIVATE swiftAST swiftClangImporter + swiftDriver swiftFrontend swiftIndex swiftParse From 6e6de518eb46ef208dfa5deae54891583f3f9951 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Tue, 25 Aug 2020 20:02:52 +0100 Subject: [PATCH 013/120] [Sema] Improve diagnostics for use of self access kind modifier on accessors inside classes (#33602) --- include/swift/AST/DiagnosticsSema.def | 5 +++-- lib/Sema/TypeCheckAttr.cpp | 9 ++++++--- localization/diagnostics/en.yaml | 2 +- test/Sema/immutability.swift | 17 +++++++++++++---- test/decl/ext/extensions.swift | 12 ++++++------ 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8424bd9810a8f..beaf7b6ea3dd5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1294,8 +1294,9 @@ ERROR(nominal_type_not_attribute,none, ERROR(mutating_invalid_global_scope,none, "%0 is only valid on methods", (SelfAccessKind)) -ERROR(mutating_invalid_classes,none, "%0 isn't valid on methods in " - "classes or class-bound protocols", (SelfAccessKind)) +ERROR(mutating_invalid_classes,none, "%0 is not valid on %1s in " + "%select{classes|class-bound protocols}2", + (SelfAccessKind, DescriptiveDeclKind, bool)) ERROR(functions_mutating_and_not,none, "method must not be declared both %0 and %1", diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 4d15582ff9f91..94478abf4f50f 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -317,14 +317,17 @@ void AttributeChecker::visitMutationAttr(DeclAttribute *attr) { llvm_unreachable("unhandled attribute kind"); } + auto DC = FD->getDeclContext(); // mutation attributes may only appear in type context. - if (auto contextTy = FD->getDeclContext()->getDeclaredInterfaceType()) { + if (auto contextTy = DC->getDeclaredInterfaceType()) { // 'mutating' and 'nonmutating' are not valid on types // with reference semantics. if (contextTy->hasReferenceSemantics()) { - if (attrModifier != SelfAccessKind::Consuming) + if (attrModifier != SelfAccessKind::Consuming) { diagnoseAndRemoveAttr(attr, diag::mutating_invalid_classes, - attrModifier); + attrModifier, FD->getDescriptiveKind(), + DC->getSelfProtocolDecl() != nullptr); + } } } else { diagnoseAndRemoveAttr(attr, diag::mutating_invalid_global_scope, diff --git a/localization/diagnostics/en.yaml b/localization/diagnostics/en.yaml index d469f67329008..b346b96b115cc 100644 --- a/localization/diagnostics/en.yaml +++ b/localization/diagnostics/en.yaml @@ -3350,7 +3350,7 @@ msg: "%0 is only valid on methods" - id: mutating_invalid_classes - msg: "%0 isn't valid on methods in classes or class-bound protocols" + msg: "%0 is not valid on %1s in %select{classes|class-bound protocols}2" - id: functions_mutating_and_not msg: "method must not be declared both %0 and %1" diff --git a/test/Sema/immutability.swift b/test/Sema/immutability.swift index beae8a87fa53e..d43c5c299b806 100644 --- a/test/Sema/immutability.swift +++ b/test/Sema/immutability.swift @@ -61,13 +61,13 @@ class FooClass { mutating init(a : Bool) {} // expected-error {{'mutating' may only be used on 'func' declarations}} {{3-12=}} - mutating // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} {{3-12=}} + mutating // expected-error {{'mutating' is not valid on instance methods in classes}} {{3-12=}} func baz() {} - nonmutating // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}} {{3-15=}} + nonmutating // expected-error {{'nonmutating' is not valid on instance methods in classes}} {{3-15=}} func bay() {} - mutating nonmutating // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}} + mutating nonmutating // expected-error {{'mutating' is not valid on instance methods in classes}} expected-error {{'nonmutating' is not valid on instance methods in classes}} func bax() {} var x : Int { @@ -86,6 +86,15 @@ class FooClass { } } + var computed: Int { + mutating get { 0 } // expected-error {{'mutating' is not valid on getters in classes}} {{5-14=}} + nonmutating set {} // expected-error {{'nonmutating' is not valid on setters in classes}} {{5-17=}} + } + + var storedWithObservers: Int = 0 { + mutating willSet {} // expected-error {{'mutating' is not valid on willSet observers in classes}} {{5-14=}} + nonmutating didSet {} // expected-error {{'nonmutating' is not valid on didSet observers in classes}} {{5-17=}} + } } @@ -241,7 +250,7 @@ func test_arguments(_ a : Int, protocol ClassBoundProtocolMutating : class { - mutating // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} {{3-12=}} + mutating // expected-error {{'mutating' is not valid on instance methods in class-bound protocols}} {{3-12=}} func f() } diff --git a/test/decl/ext/extensions.swift b/test/decl/ext/extensions.swift index 6729bba8d1e07..b6f5133acfd8d 100644 --- a/test/decl/ext/extensions.swift +++ b/test/decl/ext/extensions.swift @@ -267,14 +267,14 @@ extension ImposeClassReq1 where Self: AnyObject { var wrappingProperty2: Int { get { return someProperty } - mutating set { someProperty = newValue } // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} + mutating set { someProperty = newValue } // expected-error {{'mutating' is not valid on setters in class-bound protocols}} } - mutating func foo() { // expected-error {{mutating' isn't valid on methods in classes or class-bound protocols}} + mutating func foo() { // expected-error {{mutating' is not valid on instance methods in class-bound protocols}} someProperty = 1 } - nonmutating func bar() { // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}} + nonmutating func bar() { // expected-error {{'nonmutating' is not valid on instance methods in class-bound protocols}} someProperty = 2 } @@ -309,14 +309,14 @@ extension ImposeClassReq2 { var wrappingProperty2: Int { get { return someProperty } - mutating set { someProperty = newValue } // expected-error {{'mutating' isn't valid on methods in classes or class-bound protocols}} + mutating set { someProperty = newValue } // expected-error {{'mutating' is not valid on setters in class-bound protocols}} } - mutating func foo() { // expected-error {{mutating' isn't valid on methods in classes or class-bound protocols}} + mutating func foo() { // expected-error {{mutating' is not valid on instance methods in class-bound protocols}} someProperty = 1 } - nonmutating func bar() { // expected-error {{'nonmutating' isn't valid on methods in classes or class-bound protocols}} + nonmutating func bar() { // expected-error {{'nonmutating' is not valid on instance methods in class-bound protocols}} someProperty = 2 } From ee8ad7bb8b2855122004947ca423bb517d952c31 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 25 Aug 2020 12:54:55 -0700 Subject: [PATCH 014/120] [AutoDiff] NFC: silence no-asserts unused variable warning. (#33629) --- lib/SILOptimizer/Differentiation/PullbackCloner.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index 9de979b76d0f8..7b0c3c62fda76 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -2035,12 +2035,10 @@ void PullbackCloner::Implementation::accumulateAdjointForOptional( SILBasicBlock *bb, SILValue optionalValue, SILValue wrappedAdjoint) { auto pbLoc = getPullback().getLocation(); // Handle `switch_enum` on `Optional`. - auto *optionalEnumDecl = getASTContext().getOptionalDecl(); - auto optionalTy = optionalValue->getType(); - assert(optionalTy.getASTType().getEnumOrBoundGenericEnum() == - optionalEnumDecl); // `Optional` - optionalTy = remapType(optionalTy); + auto optionalTy = remapType(optionalValue->getType()); + assert(optionalTy.getASTType().getEnumOrBoundGenericEnum() == + getASTContext().getOptionalDecl()); // `T` auto wrappedType = optionalTy.getOptionalObjectType(); // `T.TangentVector` From c5b42aca1133eefa909bef1bce25ba7834f66bff Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 25 Aug 2020 13:00:35 -0700 Subject: [PATCH 015/120] Move FuzzyStringMatcherTest to IDE unittests --- unittests/IDE/CMakeLists.txt | 1 + unittests/{SourceKit/Support => IDE}/FuzzyStringMatcherTest.cpp | 0 unittests/SourceKit/Support/CMakeLists.txt | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename unittests/{SourceKit/Support => IDE}/FuzzyStringMatcherTest.cpp (100%) diff --git a/unittests/IDE/CMakeLists.txt b/unittests/IDE/CMakeLists.txt index f55846dd2c6ad..607a574214a71 100644 --- a/unittests/IDE/CMakeLists.txt +++ b/unittests/IDE/CMakeLists.txt @@ -1,5 +1,6 @@ add_swift_unittest(SwiftIDETests CodeCompletionToken.cpp + FuzzyStringMatcherTest.cpp Placeholders.cpp ) target_link_libraries(SwiftIDETests diff --git a/unittests/SourceKit/Support/FuzzyStringMatcherTest.cpp b/unittests/IDE/FuzzyStringMatcherTest.cpp similarity index 100% rename from unittests/SourceKit/Support/FuzzyStringMatcherTest.cpp rename to unittests/IDE/FuzzyStringMatcherTest.cpp diff --git a/unittests/SourceKit/Support/CMakeLists.txt b/unittests/SourceKit/Support/CMakeLists.txt index da17a059b87b3..54cbdce081c87 100644 --- a/unittests/SourceKit/Support/CMakeLists.txt +++ b/unittests/SourceKit/Support/CMakeLists.txt @@ -1,5 +1,4 @@ add_swift_unittest(SourceKitSupportTests - FuzzyStringMatcherTest.cpp ImmutableTextBufferTest.cpp ) From 94b5f7665478c09d34af663c611b4662ee478af4 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 25 Aug 2020 12:54:45 -0700 Subject: [PATCH 016/120] Revert "[SIL] Add SILFunctionType flag for async." This reverts commit 9b8828848d3ed5a5ff90808146d008c94e3ac899. --- include/swift/AST/Types.h | 8 ++- lib/AST/ASTContext.cpp | 6 +-- lib/AST/ASTDemangler.cpp | 3 +- lib/AST/ASTPrinter.cpp | 1 - lib/AST/Type.cpp | 7 ++- lib/IRGen/GenProto.cpp | 2 +- lib/IRGen/LoadableByAddress.cpp | 2 - lib/SIL/IR/SILBuilder.cpp | 1 - lib/SIL/IR/SILFunctionType.cpp | 52 +++++++++---------- lib/SIL/IR/SILType.cpp | 5 +- lib/SIL/Verifier/SILVerifier.cpp | 1 - lib/SILGen/SILGen.cpp | 4 +- lib/SILGen/SILGenBridging.cpp | 2 +- lib/SILGen/SILGenExpr.cpp | 6 +-- lib/SILGen/SILGenFunction.cpp | 4 +- lib/SILGen/SILGenPoly.cpp | 6 +-- .../Differentiation/JVPCloner.cpp | 8 +-- lib/SILOptimizer/Differentiation/Thunk.cpp | 6 +-- .../Differentiation/VJPCloner.cpp | 8 +-- .../ExistentialTransform.cpp | 2 +- .../FunctionSignatureOpts.cpp | 9 ++-- lib/SILOptimizer/IPO/CapturePromotion.cpp | 5 +- lib/SILOptimizer/IPO/ClosureSpecializer.cpp | 2 +- .../Mandatory/Differentiation.cpp | 11 ++-- .../Transforms/AllocBoxToStack.cpp | 5 +- lib/SILOptimizer/Transforms/Outliner.cpp | 4 +- .../UtilityPasses/BugReducerTester.cpp | 3 +- lib/SILOptimizer/Utils/Generics.cpp | 11 ++-- lib/Sema/TypeCheckType.cpp | 7 +-- lib/Serialization/Deserialization.cpp | 3 +- 30 files changed, 81 insertions(+), 113 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index e55c8bf7be3d5..9e6401b31aa95 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -362,12 +362,11 @@ class alignas(1 << TypeAlignInBits) TypeBase { ID : 32 ); - SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+1+3+1+1+2+1+1, + SWIFT_INLINE_BITFIELD(SILFunctionType, TypeBase, NumSILExtInfoBits+1+3+1+2+1+1, ExtInfoBits : NumSILExtInfoBits, HasClangTypeInfo : 1, CalleeConvention : 3, HasErrorResult : 1, - IsAsync : 1, CoroutineKind : 2, HasInvocationSubs : 1, HasPatternSubs : 1 @@ -3980,7 +3979,7 @@ class SILFunctionType final + 1); } - SILFunctionType(GenericSignature genericSig, ExtInfo ext, bool isAsync, + SILFunctionType(GenericSignature genericSig, ExtInfo ext, SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, ArrayRef params, @@ -3994,8 +3993,7 @@ class SILFunctionType final public: static CanSILFunctionType - get(GenericSignature genericSig, ExtInfo ext, bool isAsync, - SILCoroutineKind coroutineKind, + get(GenericSignature genericSig, ExtInfo ext, SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, ArrayRef interfaceParams, ArrayRef interfaceYields, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index f3ee9197874a9..6aa37233e8809 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3300,7 +3300,6 @@ void SILFunctionType::Profile( SILFunctionType::SILFunctionType( GenericSignature genericSig, ExtInfo ext, - bool isAsync, SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, ArrayRef params, @@ -3326,7 +3325,6 @@ SILFunctionType::SILFunctionType( "Bits were dropped!"); static_assert(SILExtInfoBuilder::NumMaskBits == NumSILExtInfoBits, "ExtInfo and SILFunctionTypeBitfields must agree on bit size"); - Bits.SILFunctionType.IsAsync = isAsync; Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind); NumParameters = params.size(); if (coroutineKind == SILCoroutineKind::None) { @@ -3470,7 +3468,7 @@ CanSILBlockStorageType SILBlockStorageType::get(CanType captureType) { CanSILFunctionType SILFunctionType::get( GenericSignature genericSig, - ExtInfo ext, bool isAsync, SILCoroutineKind coroutineKind, + ExtInfo ext, SILCoroutineKind coroutineKind, ParameterConvention callee, ArrayRef params, ArrayRef yields, @@ -3537,7 +3535,7 @@ CanSILFunctionType SILFunctionType::get( } auto fnType = - new (mem) SILFunctionType(genericSig, ext, isAsync, coroutineKind, callee, + new (mem) SILFunctionType(genericSig, ext, coroutineKind, callee, params, yields, normalResults, errorResult, patternSubs, invocationSubs, ctx, properties, witnessMethodConformance); diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 6e44fd70da0ef..44d32c84a7a7d 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -558,8 +558,7 @@ Type ASTBuilder::createImplFunctionType( auto conv = getResultConvention(errorResult->getConvention()); funcErrorResult.emplace(type, conv); } - return SILFunctionType::get(genericSig, einfo, - /*isAsync*/ false, funcCoroutineKind, + return SILFunctionType::get(genericSig, einfo, funcCoroutineKind, funcCalleeConvention, funcParams, funcYields, funcResults, funcErrorResult, SubstitutionMap(), SubstitutionMap(), Ctx); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index ed03d9a2c35c0..aff1100a37bf9 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4277,7 +4277,6 @@ class TypePrinter : public TypeVisitor { void visitSILFunctionType(SILFunctionType *T) { printSILCoroutineKind(T->getCoroutineKind()); - printSILAsyncAttr(T->isAsync()); printFunctionExtInfo(T->getASTContext(), T->getExtInfo(), T->getWitnessMethodConformanceOrInvalid()); printCalleeConvention(T->getCalleeConvention()); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index bc7d9b780b659..f26c9e39182af 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -4378,7 +4378,6 @@ case TypeKind::Id: return SILFunctionType::get( fnTy->getInvocationGenericSignature(), fnTy->getExtInfo(), - fnTy->isAsync(), fnTy->getCoroutineKind(), fnTy->getCalleeConvention(), transInterfaceParams, @@ -5372,7 +5371,7 @@ SILFunctionType::withInvocationSubstitutions(SubstitutionMap subs) const { assert(!subs || CanGenericSignature(subs.getGenericSignature()) == getInvocationGenericSignature()); return SILFunctionType::get(getInvocationGenericSignature(), - getExtInfo(), isAsync(), getCoroutineKind(), + getExtInfo(), getCoroutineKind(), getCalleeConvention(), getParameters(), getYields(), getResults(), getOptionalErrorResult(), @@ -5390,7 +5389,7 @@ SILFunctionType::withPatternSubstitutions(SubstitutionMap subs) const { assert(!subs || CanGenericSignature(subs.getGenericSignature()) == getPatternGenericSignature()); return SILFunctionType::get(getInvocationGenericSignature(), - getExtInfo(), isAsync(), getCoroutineKind(), + getExtInfo(), getCoroutineKind(), getCalleeConvention(), getParameters(), getYields(), getResults(), getOptionalErrorResult(), @@ -5409,7 +5408,7 @@ SILFunctionType::withPatternSpecialization(CanGenericSignature sig, assert(!subs || CanGenericSignature(subs.getGenericSignature()) == getSubstGenericSignature()); return SILFunctionType::get(sig, - getExtInfo(), isAsync(), getCoroutineKind(), + getExtInfo(), getCoroutineKind(), getCalleeConvention(), getParameters(), getYields(), getResults(), getOptionalErrorResult(), diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 6282e402545c8..2795553485a5a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -3056,7 +3056,7 @@ GenericTypeRequirements::GenericTypeRequirements(IRGenModule &IGM, // Construct a representative function type. auto generics = ncGenerics.getCanonicalSignature(); auto fnType = SILFunctionType::get(generics, SILFunctionType::ExtInfo(), - /*isAsync*/ false, SILCoroutineKind::None, + SILCoroutineKind::None, /*callee*/ ParameterConvention::Direct_Unowned, /*params*/ {}, /*yields*/ {}, /*results*/ {}, /*error*/ None, diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 6d798d7ea8131..2a509cbfeeb89 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -297,7 +297,6 @@ LargeSILTypeMapper::getNewSILFunctionType(GenericEnvironment *env, auto newFnType = SILFunctionType::get( fnType->getInvocationGenericSignature(), fnType->getExtInfo(), - fnType->isAsync(), fnType->getCoroutineKind(), fnType->getCalleeConvention(), newParams, @@ -2362,7 +2361,6 @@ static bool rewriteFunctionReturn(StructLoweringState &pass) { auto NewTy = SILFunctionType::get( loweredTy->getSubstGenericSignature(), loweredTy->getExtInfo(), - loweredTy->isAsync(), loweredTy->getCoroutineKind(), loweredTy->getCalleeConvention(), loweredTy->getParameters(), diff --git a/lib/SIL/IR/SILBuilder.cpp b/lib/SIL/IR/SILBuilder.cpp index d6a876037c0b9..c925cc83bb737 100644 --- a/lib/SIL/IR/SILBuilder.cpp +++ b/lib/SIL/IR/SILBuilder.cpp @@ -107,7 +107,6 @@ SILType SILBuilder::getPartialApplyResultType( auto appliedFnType = SILFunctionType::get(nullptr, extInfo, - FTI->isAsync(), FTI->getCoroutineKind(), calleeConvention, newParams, diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 0e50bdd34f8e4..2d3e10054313d 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -94,7 +94,6 @@ CanSILFunctionType SILFunctionType::getUnsubstitutedType(SILModule &M) const { : CanGenericSignature(); return SILFunctionType::get(signature, getExtInfo(), - isAsync(), getCoroutineKind(), getCalleeConvention(), params, yields, results, errorResult, @@ -288,11 +287,11 @@ SILFunctionType::getWithDifferentiability(DifferentiabilityKind kind, } auto newExtInfo = getExtInfo().intoBuilder().withDifferentiabilityKind(kind).build(); - return get(getInvocationGenericSignature(), newExtInfo, isAsync(), - getCoroutineKind(), getCalleeConvention(), newParameters, - getYields(), newResults, getOptionalErrorResult(), - getPatternSubstitutions(), getInvocationSubstitutions(), - getASTContext(), getWitnessMethodConformanceOrInvalid()); + return get(getInvocationGenericSignature(), newExtInfo, getCoroutineKind(), + getCalleeConvention(), newParameters, getYields(), newResults, + getOptionalErrorResult(), getPatternSubstitutions(), + getInvocationSubstitutions(), getASTContext(), + getWitnessMethodConformanceOrInvalid()); } CanSILFunctionType SILFunctionType::getWithoutDifferentiability() { @@ -312,9 +311,9 @@ CanSILFunctionType SILFunctionType::getWithoutDifferentiability() { newResults.push_back(result.getWithDifferentiability( SILResultDifferentiability::DifferentiableOrNotApplicable)); return SILFunctionType::get( - getInvocationGenericSignature(), nondiffExtInfo, isAsync(), - getCoroutineKind(), getCalleeConvention(), newParams, getYields(), - newResults, getOptionalErrorResult(), getPatternSubstitutions(), + getInvocationGenericSignature(), nondiffExtInfo, getCoroutineKind(), + getCalleeConvention(), newParams, getYields(), newResults, + getOptionalErrorResult(), getPatternSubstitutions(), getInvocationSubstitutions(), getASTContext()); } @@ -503,9 +502,9 @@ static CanSILFunctionType getAutoDiffDifferentialType( llvm::makeArrayRef(substConformances)); } return SILFunctionType::get( - GenericSignature(), SILFunctionType::ExtInfo(), /*isAsync*/ false, - SILCoroutineKind::None, ParameterConvention::Direct_Guaranteed, - differentialParams, {}, differentialResults, None, substitutions, + GenericSignature(), SILFunctionType::ExtInfo(), SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, differentialParams, {}, + differentialResults, None, substitutions, /*invocationSubstitutions*/ SubstitutionMap(), ctx); } @@ -682,9 +681,9 @@ static CanSILFunctionType getAutoDiffPullbackType( llvm::makeArrayRef(substConformances)); } return SILFunctionType::get( - GenericSignature(), SILFunctionType::ExtInfo(), /*isAsync*/ false, - SILCoroutineKind::None, ParameterConvention::Direct_Guaranteed, - pullbackParams, {}, pullbackResults, None, substitutions, + GenericSignature(), SILFunctionType::ExtInfo(), SILCoroutineKind::None, + ParameterConvention::Direct_Guaranteed, pullbackParams, {}, + pullbackResults, None, substitutions, /*invocationSubstitutions*/ SubstitutionMap(), ctx); } @@ -738,7 +737,7 @@ static SILFunctionType *getConstrainedAutoDiffOriginalFunctionType( constrainedInvocationGenSig->areAllParamsConcrete() ? GenericSignature() : constrainedInvocationGenSig, - original->getExtInfo(), original->isAsync(), original->getCoroutineKind(), + original->getExtInfo(), original->getCoroutineKind(), original->getCalleeConvention(), newParameters, original->getYields(), newResults, original->getOptionalErrorResult(), /*patternSubstitutions*/ SubstitutionMap(), @@ -829,7 +828,6 @@ CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( // cache and return. cachedResult = SILFunctionType::get( constrainedOriginalFnTy->getSubstGenericSignature(), extInfo, - constrainedOriginalFnTy->isAsync(), constrainedOriginalFnTy->getCoroutineKind(), constrainedOriginalFnTy->getCalleeConvention(), newParameters, constrainedOriginalFnTy->getYields(), newResults, @@ -915,9 +913,9 @@ CanSILFunctionType SILFunctionType::getAutoDiffTransposeFunctionType( for (auto &res : getResults()) newParameters.push_back(getParameterInfoForOriginalResult(res)); return SILFunctionType::get( - getInvocationGenericSignature(), getExtInfo(), isAsync(), - getCoroutineKind(), getCalleeConvention(), newParameters, getYields(), - newResults, getOptionalErrorResult(), getPatternSubstitutions(), + getInvocationGenericSignature(), getExtInfo(), getCoroutineKind(), + getCalleeConvention(), newParameters, getYields(), newResults, + getOptionalErrorResult(), getPatternSubstitutions(), /*invocationSubstitutions*/ {}, getASTContext()); } @@ -991,8 +989,7 @@ Lowering::adjustFunctionType(CanSILFunctionType type, return type; return SILFunctionType::get(type->getInvocationGenericSignature(), - extInfo, type->isAsync(), - type->getCoroutineKind(), callee, + extInfo, type->getCoroutineKind(), callee, type->getParameters(), type->getYields(), type->getResults(), type->getOptionalErrorResult(), @@ -1019,9 +1016,9 @@ CanSILFunctionType SILFunctionType::getWithExtInfo(ExtInfo newExt) { : Lowering::DefaultThickCalleeConvention) : ParameterConvention::Direct_Unowned); - return get(getInvocationGenericSignature(), newExt, isAsync(), - getCoroutineKind(), calleeConvention, getParameters(), getYields(), - getResults(), getOptionalErrorResult(), getPatternSubstitutions(), + return get(getInvocationGenericSignature(), newExt, getCoroutineKind(), + calleeConvention, getParameters(), getYields(), getResults(), + getOptionalErrorResult(), getPatternSubstitutions(), getInvocationSubstitutions(), getASTContext(), getWitnessMethodConformanceOrInvalid()); } @@ -2169,8 +2166,7 @@ static CanSILFunctionType getSILFunctionType( } } - return SILFunctionType::get(genericSig, silExtInfo, - substFnInterfaceType->isAsync(), coroutineKind, + return SILFunctionType::get(genericSig, silExtInfo, coroutineKind, calleeConvention, inputs, yields, results, errorResult, substitutions, SubstitutionMap(), @@ -3760,7 +3756,7 @@ class SILTypeSubstituter : ? origType->getInvocationGenericSignature() : nullptr; - return SILFunctionType::get(genericSig, extInfo, origType->isAsync(), + return SILFunctionType::get(genericSig, extInfo, origType->getCoroutineKind(), origType->getCalleeConvention(), substParams, substYields, substResults, substErrorResult, diff --git a/lib/SIL/IR/SILType.cpp b/lib/SIL/IR/SILType.cpp index 28f323ccf8769..48ca1e5cbc47d 100644 --- a/lib/SIL/IR/SILType.cpp +++ b/lib/SIL/IR/SILType.cpp @@ -671,10 +671,9 @@ TypeBase::replaceSubstitutedSILFunctionTypesWithUnsubstituted(SILModule &M) cons if (!didChange) return sft; - + return SILFunctionType::get(sft->getInvocationGenericSignature(), - sft->getExtInfo(), sft->isAsync(), - sft->getCoroutineKind(), + sft->getExtInfo(), sft->getCoroutineKind(), sft->getCalleeConvention(), newParams, newYields, newResults, newErrorResult, diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 3f27e20fe801d..c2f3de4038027 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -3003,7 +3003,6 @@ class SILVerifier : public SILVerifierBase { auto fnTy = SILFunctionType::get(nullptr, methodTy->getExtInfo(), - methodTy->isAsync(), methodTy->getCoroutineKind(), methodTy->getCalleeConvention(), dynParams, diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index dffd4c3b8dfdf..cd36b343f0e58 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -419,7 +419,7 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, /*clangFunctionType*/ nullptr) .build(); - auto functionTy = SILFunctionType::get(sig, extInfo, /*isAsync*/ false, + auto functionTy = SILFunctionType::get(sig, extInfo, SILCoroutineKind::YieldOnce, ParameterConvention::Direct_Unowned, params, @@ -482,7 +482,7 @@ SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) { }; CanSILFunctionType topLevelType = SILFunctionType::get(nullptr, extInfo, - /*isAsync*/ false, SILCoroutineKind::None, + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, SILResultInfo(Int32Ty, diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index a013d5b188baa..7eedc3b1975fa 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -578,7 +578,7 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, } auto invokeTy = SILFunctionType::get( - genericSig, extInfo, /*isAsync*/ false, SILCoroutineKind::None, + genericSig, extInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, blockInterfaceTy->getResults(), blockInterfaceTy->getOptionalErrorResult(), diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 8ef8f36c118ee..cb8273d129e1e 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2750,7 +2750,6 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, return SILFunctionType::get(genericSig, SILFunctionType::ExtInfo::getThin(), - /*isAsync*/ false, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, result, None, @@ -2897,10 +2896,9 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, params.push_back({C.getUnsafeRawPointerDecl()->getDeclaredInterfaceType() ->getCanonicalType(), ParameterConvention::Direct_Unowned}); - + return SILFunctionType::get(genericSig, SILFunctionType::ExtInfo::getThin(), - /*isAsync*/ false, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, {}, {}, None, @@ -3083,7 +3081,6 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo::getThin(), - /*isAsync*/ false, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, results, None, @@ -3258,7 +3255,6 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto signature = SILFunctionType::get(genericSig, SILFunctionType::ExtInfo::getThin(), - /*isAsync*/ false, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, params, /*yields*/ {}, results, None, diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index 7f6cf2516db66..d9acdc516f6ac 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -602,7 +602,6 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { SILFunctionType::ExtInfo() .withRepresentation(SILFunctionType::Representation:: CFunctionPointer), - /*isAsync*/ false, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, SILParameterInfo(anyObjectMetaTy, @@ -698,8 +697,7 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { // has an overlay to fix the type of argv. .withRepresentation(SILFunctionType::Representation::Thin) .build(), - /*isAsync*/ false, SILCoroutineKind::None, - ParameterConvention::Direct_Unowned, argTypes, + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, argTypes, /*yields*/ {}, SILResultInfo(argc->getType().getASTType(), ResultConvention::Unowned), /*error result*/ None, SubstitutionMap(), SubstitutionMap(), diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 110edd25ba171..0ee11d74f6439 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3245,9 +3245,9 @@ CanSILFunctionType SILGenFunction::buildThunkType( // The type of the thunk function. return SILFunctionType::get( - genericSig, extInfoBuilder.build(), expectedType->isAsync(), - expectedType->getCoroutineKind(), ParameterConvention::Direct_Unowned, - interfaceParams, interfaceYields, interfaceResults, interfaceErrorResult, + genericSig, extInfoBuilder.build(), expectedType->getCoroutineKind(), + ParameterConvention::Direct_Unowned, interfaceParams, interfaceYields, + interfaceResults, interfaceErrorResult, expectedType->getPatternSubstitutions(), SubstitutionMap(), getASTContext()); } diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index f2e1d04aea635..d7b8caa102e53 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -1587,10 +1587,10 @@ void JVPCloner::Implementation::prepareForDifferentialGeneration() { auto *diffGenericEnv = diffGenericSig ? diffGenericSig->getGenericEnvironment() : nullptr; auto diffType = SILFunctionType::get( - diffGenericSig, origTy->getExtInfo(), origTy->isAsync(), - origTy->getCoroutineKind(), origTy->getCalleeConvention(), dfParams, {}, - dfResults, None, origTy->getPatternSubstitutions(), - origTy->getInvocationSubstitutions(), original->getASTContext()); + diffGenericSig, origTy->getExtInfo(), origTy->getCoroutineKind(), + origTy->getCalleeConvention(), dfParams, {}, dfResults, None, + origTy->getPatternSubstitutions(), origTy->getInvocationSubstitutions(), + original->getASTContext()); SILOptFunctionBuilder fb(context.getTransform()); auto linkage = jvp->isSerialized() ? SILLinkage::Public : SILLinkage::Hidden; diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index bd1817c6f3994..820805fff06d9 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -242,9 +242,9 @@ CanSILFunctionType buildThunkType(SILFunction *fn, // The type of the thunk function. return SILFunctionType::get( - genericSig, extInfoBuilder.build(), expectedType->isAsync(), - expectedType->getCoroutineKind(), ParameterConvention::Direct_Unowned, - interfaceParams, interfaceYields, interfaceResults, interfaceErrorResult, + genericSig, extInfoBuilder.build(), expectedType->getCoroutineKind(), + ParameterConvention::Direct_Unowned, interfaceParams, interfaceYields, + interfaceResults, interfaceErrorResult, expectedType->getPatternSubstitutions(), SubstitutionMap(), fn->getASTContext()); } diff --git a/lib/SILOptimizer/Differentiation/VJPCloner.cpp b/lib/SILOptimizer/Differentiation/VJPCloner.cpp index dc0d915148e20..7279ce914d83c 100644 --- a/lib/SILOptimizer/Differentiation/VJPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/VJPCloner.cpp @@ -892,10 +892,10 @@ SILFunction *VJPCloner::Implementation::createEmptyPullback() { auto *pbGenericEnv = pbGenericSig ? pbGenericSig->getGenericEnvironment() : nullptr; auto pbType = SILFunctionType::get( - pbGenericSig, origTy->getExtInfo(), origTy->isAsync(), - origTy->getCoroutineKind(), origTy->getCalleeConvention(), pbParams, {}, - adjResults, None, origTy->getPatternSubstitutions(), - origTy->getInvocationSubstitutions(), original->getASTContext()); + pbGenericSig, origTy->getExtInfo(), origTy->getCoroutineKind(), + origTy->getCalleeConvention(), pbParams, {}, adjResults, None, + origTy->getPatternSubstitutions(), origTy->getInvocationSubstitutions(), + original->getASTContext()); SILOptFunctionBuilder fb(context.getTransform()); auto linkage = vjp->isSerialized() ? SILLinkage::Public : SILLinkage::Private; diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 839810a36223d..66617c63b0c24 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -403,7 +403,7 @@ ExistentialTransform::createExistentialSpecializedFunctionType() { /// Return the new signature. return SILFunctionType::get( - NewGenericSig, ExtInfo, FTy->isAsync(), FTy->getCoroutineKind(), + NewGenericSig, ExtInfo, FTy->getCoroutineKind(), FTy->getCalleeConvention(), InterfaceParams, FTy->getYields(), FTy->getResults(), InterfaceErrorResult, SubstitutionMap(), SubstitutionMap(), diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 328f55ab17ee9..7d1f34ea07cc4 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -408,11 +408,10 @@ FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { UsesGenerics ? FTy->getInvocationGenericSignature() : nullptr; return SILFunctionType::get( - GenericSig, ExtInfo, FTy->isAsync(), FTy->getCoroutineKind(), - FTy->getCalleeConvention(), InterfaceParams, InterfaceYields, - InterfaceResults, InterfaceErrorResult, FTy->getPatternSubstitutions(), - SubstitutionMap(), F->getModule().getASTContext(), - witnessMethodConformance); + GenericSig, ExtInfo, FTy->getCoroutineKind(), FTy->getCalleeConvention(), + InterfaceParams, InterfaceYields, InterfaceResults, InterfaceErrorResult, + FTy->getPatternSubstitutions(), SubstitutionMap(), + F->getModule().getASTContext(), witnessMethodConformance); } /// Compute what the function interface will look like based on the diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index ee4f6badc8505..37533fef306fc 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -428,9 +428,8 @@ ClosureCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, // Create the thin function type for the cloned closure. auto ClonedTy = SILFunctionType::get( OrigFTI->getInvocationGenericSignature(), OrigFTI->getExtInfo(), - OrigFTI->isAsync(), OrigFTI->getCoroutineKind(), - OrigFTI->getCalleeConvention(), ClonedInterfaceArgTys, - OrigFTI->getYields(), OrigFTI->getResults(), + OrigFTI->getCoroutineKind(), OrigFTI->getCalleeConvention(), + ClonedInterfaceArgTys, OrigFTI->getYields(), OrigFTI->getResults(), OrigFTI->getOptionalErrorResult(), SubstitutionMap(), SubstitutionMap(), M.getASTContext(), OrigFTI->getWitnessMethodConformanceOrInvalid()); diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index 137054e99ec18..05ab7bcaf6f10 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -670,7 +670,7 @@ ClosureSpecCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, auto ClonedTy = SILFunctionType::get( ClosureUserFunTy->getInvocationGenericSignature(), ExtInfo, - ClosureUserFunTy->isAsync(), ClosureUserFunTy->getCoroutineKind(), + ClosureUserFunTy->getCoroutineKind(), ClosureUserFunTy->getCalleeConvention(), NewParameterInfoList, ClosureUserFunTy->getYields(), ClosureUserFunTy->getResults(), ClosureUserFunTy->getOptionalErrorResult(), diff --git a/lib/SILOptimizer/Mandatory/Differentiation.cpp b/lib/SILOptimizer/Mandatory/Differentiation.cpp index a2e60703933bf..083f840e892e7 100644 --- a/lib/SILOptimizer/Mandatory/Differentiation.cpp +++ b/lib/SILOptimizer/Mandatory/Differentiation.cpp @@ -872,8 +872,7 @@ static void emitFatalError(ADContext &context, SILFunction *f, // Fatal error function must have type `@convention(thin) () -> Never`. auto fatalErrorFnType = SILFunctionType::get( /*genericSig*/ nullptr, SILFunctionType::ExtInfo::getThin(), - /*isAsync*/ false, SILCoroutineKind::None, - ParameterConvention::Direct_Unowned, {}, + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, {}, /*interfaceYields*/ {}, neverResultInfo, /*interfaceErrorResults*/ None, {}, {}, context.getASTContext()); auto fnBuilder = SILOptFunctionBuilder(context.getTransform()); @@ -1025,10 +1024,10 @@ static SILValue promoteCurryThunkApplicationToDifferentiableFunction( auto newThunkResult = thunkResult.getWithInterfaceType(diffResultFnTy); auto thunkType = SILFunctionType::get( thunkTy->getSubstGenericSignature(), thunkTy->getExtInfo(), - thunkTy->isAsync(), thunkTy->getCoroutineKind(), - thunkTy->getCalleeConvention(), thunkTy->getParameters(), {}, - {newThunkResult}, {}, thunkTy->getPatternSubstitutions(), - thunkTy->getInvocationSubstitutions(), thunkTy->getASTContext()); + thunkTy->getCoroutineKind(), thunkTy->getCalleeConvention(), + thunkTy->getParameters(), {}, {newThunkResult}, {}, + thunkTy->getPatternSubstitutions(), thunkTy->getInvocationSubstitutions(), + thunkTy->getASTContext()); // Construct new curry thunk, returning a `@differentiable` function. SILOptFunctionBuilder fb(dt.getTransform()); diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 93571944f8312..6483b47a12a6f 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -699,9 +699,8 @@ SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, // the parameters promoted. auto ClonedTy = SILFunctionType::get( OrigFTI->getInvocationGenericSignature(), OrigFTI->getExtInfo(), - OrigFTI->isAsync(), OrigFTI->getCoroutineKind(), - OrigFTI->getCalleeConvention(), ClonedInterfaceArgTys, - OrigFTI->getYields(), OrigFTI->getResults(), + OrigFTI->getCoroutineKind(), OrigFTI->getCalleeConvention(), + ClonedInterfaceArgTys, OrigFTI->getYields(), OrigFTI->getResults(), OrigFTI->getOptionalErrorResult(), OrigFTI->getPatternSubstitutions(), OrigFTI->getInvocationSubstitutions(), M.getASTContext(), OrigFTI->getWitnessMethodConformanceOrInvalid()); diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 4426205c94ff0..e052a6b000f3c 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -302,7 +302,7 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { /*clangFunctionType*/ nullptr) .build(); auto FunctionType = SILFunctionType::get( - nullptr, ExtInfo, /*isAsync*/ false, SILCoroutineKind::None, + nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, /*yields*/ {}, Results, None, SubstitutionMap(), SubstitutionMap(), @@ -1203,7 +1203,7 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { SILResultInfo(BridgedReturn.getReturnType(), ResultConvention::Owned)); } auto FunctionType = SILFunctionType::get( - nullptr, ExtInfo, /*isAsync*/ false, SILCoroutineKind::None, + nullptr, ExtInfo, SILCoroutineKind::None, ParameterConvention::Direct_Unowned, Parameters, {}, Results, None, SubstitutionMap(), SubstitutionMap(), diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index 3ec8f130c3496..a8f823e584d5a 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -89,8 +89,7 @@ class BugReducerTester : public SILFunctionTransform { false /*noescape*/, DifferentiabilityKind::NonDifferentiable, nullptr /*clangFunctionType*/) .build(), - /*isAsync*/ false, SILCoroutineKind::None, - ParameterConvention::Direct_Unowned, + SILCoroutineKind::None, ParameterConvention::Direct_Unowned, ArrayRef(), ArrayRef(), ResultInfoArray, None, SubstitutionMap(), SubstitutionMap(), getFunction()->getModule().getASTContext()); diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 39dfd0c59c28f..2bde614efd66c 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -785,11 +785,10 @@ ReabstractionInfo::createSubstitutedType(SILFunction *OrigF, // Use the new specialized generic signature. auto NewFnTy = SILFunctionType::get( - CanSpecializedGenericSig, FnTy->getExtInfo(), FnTy->isAsync(), - FnTy->getCoroutineKind(), FnTy->getCalleeConvention(), - FnTy->getParameters(), FnTy->getYields(), FnTy->getResults(), - FnTy->getOptionalErrorResult(), FnTy->getPatternSubstitutions(), - SubstitutionMap(), M.getASTContext(), + CanSpecializedGenericSig, FnTy->getExtInfo(), FnTy->getCoroutineKind(), + FnTy->getCalleeConvention(), FnTy->getParameters(), FnTy->getYields(), + FnTy->getResults(), FnTy->getOptionalErrorResult(), + FnTy->getPatternSubstitutions(), SubstitutionMap(), M.getASTContext(), FnTy->getWitnessMethodConformanceOrInvalid()); // This is an interface type. It should not have any archetypes. @@ -857,7 +856,7 @@ createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const { ? SubstFTy->getInvocationGenericSignature() : CanGenericSignature(); return SILFunctionType::get( - Signature, SubstFTy->getExtInfo(), SubstFTy->isAsync(), + Signature, SubstFTy->getExtInfo(), SubstFTy->getCoroutineKind(), SubstFTy->getCalleeConvention(), SpecializedParams, SpecializedYields, SpecializedResults, SubstFTy->getOptionalErrorResult(), SubstitutionMap(), SubstitutionMap(), diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 864d2b926adb9..8ec5b7a9f942f 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1749,7 +1749,6 @@ namespace { Type resolveSILFunctionType(FunctionTypeRepr *repr, TypeResolutionOptions options, - bool isAsync = false, SILCoroutineKind coroutineKind = SILCoroutineKind::None, SILFunctionType::ExtInfo extInfo @@ -2230,7 +2229,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, attrs.has(TAK_noescape), diffKind, nullptr) .build(); - ty = resolveSILFunctionType(fnRepr, options, isAsync, coroutineKind, extInfo, + ty = resolveSILFunctionType(fnRepr, options, coroutineKind, extInfo, calleeConvention, witnessMethodProtocol); if (!ty || ty->hasError()) return ty; @@ -2845,7 +2844,6 @@ Type TypeResolver::resolveSILBoxType(SILBoxTypeRepr *repr, Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, TypeResolutionOptions options, - bool isAsync, SILCoroutineKind coroutineKind, SILFunctionType::ExtInfo extInfo, ParameterConvention callee, @@ -3040,8 +3038,7 @@ Type TypeResolver::resolveSILFunctionType(FunctionTypeRepr *repr, "found witness_method without matching conformance"); } - return SILFunctionType::get(genericSig, extInfo, isAsync, - coroutineKind, callee, + return SILFunctionType::get(genericSig, extInfo, coroutineKind, callee, interfaceParams, interfaceYields, interfaceResults, interfaceErrorResult, interfacePatternSubs, invocationSubs, diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 0bb3cef17922d..a2b83b1faaf00 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5568,8 +5568,7 @@ class TypeDeserializer { if (!patternSubsOrErr) return patternSubsOrErr.takeError(); - return SILFunctionType::get(invocationSig, extInfo, - async, coroutineKind.getValue(), + return SILFunctionType::get(invocationSig, extInfo, coroutineKind.getValue(), calleeConvention.getValue(), allParams, allYields, allResults, errorResult, From 5d813f38ecc944e360c72ddad293b706532ce404 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 25 Aug 2020 12:58:53 -0700 Subject: [PATCH 017/120] Revert "[SIL] Add flag to SILFunctionType::Profile for async." This reverts commit b8976a3ec5c10d4b50e1a007f743bc48cc34fae1. --- include/swift/AST/Types.h | 4 ++-- lib/AST/ASTContext.cpp | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 9e6401b31aa95..21e9a5ccd37f6 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -4575,14 +4575,14 @@ class SILFunctionType final void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, getInvocationGenericSignature(), - getExtInfo(), isAsync(), getCoroutineKind(), getCalleeConvention(), + getExtInfo(), getCoroutineKind(), getCalleeConvention(), getParameters(), getYields(), getResults(), getOptionalErrorResult(), getWitnessMethodConformanceOrInvalid(), getPatternSubstitutions(), getInvocationSubstitutions()); } static void Profile(llvm::FoldingSetNodeID &ID, GenericSignature genericSig, ExtInfo info, - bool isAsync, SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, + SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, ArrayRef params, ArrayRef yields, ArrayRef results, Optional errorResult, ProtocolConformanceRef conformance, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6aa37233e8809..633527ccf7735 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3260,7 +3260,6 @@ void SILFunctionType::Profile( llvm::FoldingSetNodeID &id, GenericSignature genericParams, ExtInfo info, - bool isAsync, SILCoroutineKind coroutineKind, ParameterConvention calleeConvention, ArrayRef params, @@ -3274,7 +3273,6 @@ void SILFunctionType::Profile( auto infoKey = info.getFuncAttrKey(); id.AddInteger(infoKey.first); id.AddPointer(infoKey.second); - id.AddBoolean(isAsync); id.AddInteger(unsigned(coroutineKind)); id.AddInteger(unsigned(calleeConvention)); id.AddInteger(params.size()); @@ -3486,8 +3484,8 @@ CanSILFunctionType SILFunctionType::get( invocationSubs = invocationSubs.getCanonical(); llvm::FoldingSetNodeID id; - SILFunctionType::Profile(id, genericSig, ext, isAsync, coroutineKind, callee, - params, yields, normalResults, errorResult, + SILFunctionType::Profile(id, genericSig, ext, coroutineKind, callee, params, + yields, normalResults, errorResult, witnessMethodConformance, patternSubs, invocationSubs); From 78159c83f76a7871445d307031865ac33ec7df98 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 25 Aug 2020 09:57:21 -0700 Subject: [PATCH 018/120] [Explicit Module Builds] Handle #canImport in the dependency scanner by adding scanner "loaders" to the ASTContext. --- .../Serialization/ModuleDependencyScanner.h | 110 ++++++++++ lib/Frontend/Frontend.cpp | 30 ++- lib/Serialization/ModuleDependencyScanner.cpp | 198 ++++++------------ .../can_import_placeholder.swift | 55 +++++ 4 files changed, 255 insertions(+), 138 deletions(-) create mode 100644 include/swift/Serialization/ModuleDependencyScanner.h create mode 100644 test/ScanDependencies/can_import_placeholder.swift diff --git a/include/swift/Serialization/ModuleDependencyScanner.h b/include/swift/Serialization/ModuleDependencyScanner.h new file mode 100644 index 0000000000000..07006770d8f49 --- /dev/null +++ b/include/swift/Serialization/ModuleDependencyScanner.h @@ -0,0 +1,110 @@ +//===--- ModuleDependencyScanner.h - Import Swift modules --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/ASTContext.h" +#include "swift/Frontend/ModuleInterfaceLoader.h" +#include "swift/Serialization/SerializedModuleLoader.h" + +namespace swift { + /// A module "loader" that looks for .swiftinterface and .swiftmodule files + /// for the purpose of determining dependencies, but does not attempt to + /// load the module files. + class ModuleDependencyScanner : public SerializedModuleLoaderBase { + /// The module we're scanning dependencies of. + Identifier moduleName; + + /// Scan the given interface file to determine dependencies. + llvm::ErrorOr scanInterfaceFile( + Twine moduleInterfacePath, bool isFramework); + + InterfaceSubContextDelegate &astDelegate; + public: + Optional dependencies; + + /// Describes the kind of dependencies this scanner is able to identify + ModuleDependenciesKind dependencyKind; + + ModuleDependencyScanner( + ASTContext &ctx, ModuleLoadingMode LoadMode, Identifier moduleName, + InterfaceSubContextDelegate &astDelegate, + ModuleDependenciesKind dependencyKind = ModuleDependenciesKind::Swift) + : SerializedModuleLoaderBase(ctx, nullptr, LoadMode, + /*IgnoreSwiftSourceInfoFile=*/true), + moduleName(moduleName), astDelegate(astDelegate), + dependencyKind(dependencyKind) {} + + std::error_code findModuleFilesInDirectory( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, + std::unique_ptr *ModuleBuffer, + std::unique_ptr *ModuleDocBuffer, + std::unique_ptr *ModuleSourceInfoBuffer, + bool IsFramework) override; + + virtual void collectVisibleTopLevelModuleNames( + SmallVectorImpl &names) const override { + llvm_unreachable("Not used"); + } + }; + + /// A ModuleLoader that loads placeholder dependency module stubs specified in + /// -placeholder-dependency-module-map-file + /// This loader is used only in dependency scanning to inform the scanner that a + /// set of modules constitute placeholder dependencies that are not visible to the + /// scanner but will nevertheless be provided by the scanner's clients. + /// This "loader" will not attempt to load any module files. + class PlaceholderSwiftModuleScanner : public ModuleDependencyScanner { + /// Scan the given placeholder module map + void parsePlaceholderModuleMap(StringRef fileName) { + ExplicitModuleMapParser parser(Allocator); + auto result = + parser.parseSwiftExplicitModuleMap(fileName, PlaceholderDependencyModuleMap); + if (result == std::errc::invalid_argument) { + Ctx.Diags.diagnose(SourceLoc(), + diag::placeholder_dependency_module_map_corrupted, + fileName); + } + else if (result == std::errc::no_such_file_or_directory) { + Ctx.Diags.diagnose(SourceLoc(), + diag::placeholder_dependency_module_map_missing, + fileName); + } + } + + llvm::StringMap PlaceholderDependencyModuleMap; + llvm::BumpPtrAllocator Allocator; + + public: + PlaceholderSwiftModuleScanner(ASTContext &ctx, ModuleLoadingMode LoadMode, + Identifier moduleName, + StringRef PlaceholderDependencyModuleMap, + InterfaceSubContextDelegate &astDelegate) + : ModuleDependencyScanner(ctx, LoadMode, moduleName, astDelegate, + ModuleDependenciesKind::SwiftPlaceholder) { + + // FIXME: Find a better place for this map to live, to avoid + // doing the parsing on every module. + if (!PlaceholderDependencyModuleMap.empty()) { + parsePlaceholderModuleMap(PlaceholderDependencyModuleMap); + } + } + + std::error_code findModuleFilesInDirectory( + AccessPathElem ModuleID, const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, + std::unique_ptr *ModuleBuffer, + std::unique_ptr *ModuleDocBuffer, + std::unique_ptr *ModuleSourceInfoBuffer, + bool IsFramework) override; + }; +} diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 7e000a0b6a563..96f73546529c6 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -33,6 +33,7 @@ #include "swift/SILOptimizer/Utils/Generics.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/Serialization/SerializedModuleLoader.h" +#include "swift/Serialization/ModuleDependencyScanner.h" #include "swift/Strings.h" #include "swift/Subsystems.h" #include "clang/AST/ASTContext.h" @@ -520,9 +521,36 @@ bool CompilerInstance::setUpModuleLoaders() { this->DefaultSerializedLoader = ISML.get(); Context->addModuleLoader(std::move(ISML)); } - + Context->addModuleLoader(std::move(clangImporter), /*isClang*/ true); + // When scanning for dependencies, we must add the scanner loaders in order to handle + // ASTContext operations such as canImportModule + if (Invocation.getFrontendOptions().RequestedAction == + FrontendOptions::ActionType::ScanDependencies) { + auto ModuleCachePath = getModuleCachePathFromClang(Context + ->getClangModuleLoader()->getClangInstance()); + auto &FEOpts = Invocation.getFrontendOptions(); + ModuleInterfaceLoaderOptions LoaderOpts(FEOpts); + InterfaceSubContextDelegateImpl ASTDelegate(Context->SourceMgr, Context->Diags, + Context->SearchPathOpts, Context->LangOpts, + LoaderOpts, + Context->getClangModuleLoader(), + /*buildModuleCacheDirIfAbsent*/false, + ModuleCachePath, + FEOpts.PrebuiltModuleCachePath, + FEOpts.SerializeModuleInterfaceDependencyHashes, + FEOpts.shouldTrackSystemDependencies()); + auto mainModuleName = Context->getIdentifier(FEOpts.ModuleName); + std::unique_ptr PSMS = + std::make_unique(*Context, + MLM, + mainModuleName, + Context->SearchPathOpts.PlaceholderDependencyModuleMap, + ASTDelegate); + Context->addModuleLoader(std::move(PSMS)); + } + return false; } diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index ab06b6189060f..fbe77f062b9b6 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -19,157 +19,81 @@ #include "swift/Basic/FileTypes.h" #include "swift/Frontend/ModuleInterfaceLoader.h" #include "swift/Serialization/SerializedModuleLoader.h" +#include "swift/Serialization/ModuleDependencyScanner.h" #include "swift/Subsystems.h" using namespace swift; using llvm::ErrorOr; -namespace { -/// A module "loader" that looks for .swiftinterface and .swiftmodule files -/// for the purpose of determining dependencies, but does not attempt to -/// load the module files. -class ModuleDependencyScanner : public SerializedModuleLoaderBase { - /// The module we're scanning dependencies of. - Identifier moduleName; - - /// Scan the given interface file to determine dependencies. - ErrorOr scanInterfaceFile( - Twine moduleInterfacePath, bool isFramework); - - InterfaceSubContextDelegate &astDelegate; -public: - Optional dependencies; - - /// Describes the kind of dependencies this scanner is able to identify - ModuleDependenciesKind dependencyKind; - - ModuleDependencyScanner( - ASTContext &ctx, ModuleLoadingMode LoadMode, Identifier moduleName, - InterfaceSubContextDelegate &astDelegate, - ModuleDependenciesKind dependencyKind = ModuleDependenciesKind::Swift) - : SerializedModuleLoaderBase(ctx, nullptr, LoadMode, - /*IgnoreSwiftSourceInfoFile=*/true), - moduleName(moduleName), astDelegate(astDelegate), - dependencyKind(dependencyKind) {} - - virtual std::error_code findModuleFilesInDirectory( - AccessPathElem ModuleID, - const SerializedModuleBaseName &BaseName, - SmallVectorImpl *ModuleInterfacePath, - std::unique_ptr *ModuleBuffer, - std::unique_ptr *ModuleDocBuffer, - std::unique_ptr *ModuleSourceInfoBuffer, - bool IsFramework) override { - using namespace llvm::sys; - - auto &fs = *Ctx.SourceMgr.getFileSystem(); - - auto ModPath = BaseName.getName(file_types::TY_SwiftModuleFile); - auto InPath = BaseName.getName(file_types::TY_SwiftModuleInterfaceFile); - - if (LoadMode == ModuleLoadingMode::OnlySerialized || !fs.exists(InPath)) { - if (fs.exists(ModPath)) { - // The module file will be loaded directly. - auto dependencies = scanModuleFile(ModPath); - if (dependencies) { - this->dependencies = std::move(dependencies.get()); - return std::error_code(); - } - return dependencies.getError(); - } else { - return std::make_error_code(std::errc::no_such_file_or_directory); +std::error_code ModuleDependencyScanner::findModuleFilesInDirectory( + AccessPathElem ModuleID, + const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, + std::unique_ptr *ModuleBuffer, + std::unique_ptr *ModuleDocBuffer, + std::unique_ptr *ModuleSourceInfoBuffer, + bool IsFramework) { + using namespace llvm::sys; + + auto &fs = *Ctx.SourceMgr.getFileSystem(); + + auto ModPath = BaseName.getName(file_types::TY_SwiftModuleFile); + auto InPath = BaseName.getName(file_types::TY_SwiftModuleInterfaceFile); + + if (LoadMode == ModuleLoadingMode::OnlySerialized || !fs.exists(InPath)) { + if (fs.exists(ModPath)) { + // The module file will be loaded directly. + auto dependencies = scanModuleFile(ModPath); + if (dependencies) { + this->dependencies = std::move(dependencies.get()); + return std::error_code(); } + return dependencies.getError(); + } else { + return std::make_error_code(std::errc::no_such_file_or_directory); } - assert(fs.exists(InPath)); - // Use the private interface file if exits. - auto PrivateInPath = - BaseName.getName(file_types::TY_PrivateSwiftModuleInterfaceFile); - if (fs.exists(PrivateInPath)) { - InPath = PrivateInPath; - } - auto dependencies = scanInterfaceFile(InPath, IsFramework); - if (dependencies) { - this->dependencies = std::move(dependencies.get()); - return std::error_code(); - } - - return dependencies.getError(); } - - virtual void collectVisibleTopLevelModuleNames( - SmallVectorImpl &names) const override { - llvm_unreachable("Not used"); + assert(fs.exists(InPath)); + // Use the private interface file if exits. + auto PrivateInPath = + BaseName.getName(file_types::TY_PrivateSwiftModuleInterfaceFile); + if (fs.exists(PrivateInPath)) { + InPath = PrivateInPath; } -}; - -/// A ModuleLoader that loads placeholder dependency module stubs specified in -/// -placeholder-dependency-module-map-file -/// This loader is used only in dependency scanning to inform the scanner that a -/// set of modules constitute placeholder dependencies that are not visible to the -/// scanner but will nevertheless be provided by the scanner's clients. -/// This "loader" will not attempt to load any module files. -class PlaceholderSwiftModuleScanner : public ModuleDependencyScanner { - /// Scan the given placeholder module map - void parsePlaceholderModuleMap(StringRef fileName) { - ExplicitModuleMapParser parser(Allocator); - auto result = - parser.parseSwiftExplicitModuleMap(fileName, PlaceholderDependencyModuleMap); - if (result == std::errc::invalid_argument) { - Ctx.Diags.diagnose(SourceLoc(), - diag::placeholder_dependency_module_map_corrupted, - fileName); - } - else if (result == std::errc::no_such_file_or_directory) { - Ctx.Diags.diagnose(SourceLoc(), - diag::placeholder_dependency_module_map_missing, - fileName); - } + auto dependencies = scanInterfaceFile(InPath, IsFramework); + if (dependencies) { + this->dependencies = std::move(dependencies.get()); + return std::error_code(); } - llvm::StringMap PlaceholderDependencyModuleMap; - llvm::BumpPtrAllocator Allocator; - -public: - PlaceholderSwiftModuleScanner(ASTContext &ctx, ModuleLoadingMode LoadMode, - Identifier moduleName, - StringRef PlaceholderDependencyModuleMap, - InterfaceSubContextDelegate &astDelegate) - : ModuleDependencyScanner(ctx, LoadMode, moduleName, astDelegate, - ModuleDependenciesKind::SwiftPlaceholder) { - - // FIXME: Find a better place for this map to live, to avoid - // doing the parsing on every module. - if (!PlaceholderDependencyModuleMap.empty()) { - parsePlaceholderModuleMap(PlaceholderDependencyModuleMap); - } - } + return dependencies.getError(); +} - std::error_code findModuleFilesInDirectory( - AccessPathElem ModuleID, const SerializedModuleBaseName &BaseName, - SmallVectorImpl *ModuleInterfacePath, - std::unique_ptr *ModuleBuffer, - std::unique_ptr *ModuleDocBuffer, - std::unique_ptr *ModuleSourceInfoBuffer, - bool IsFramework) override { - StringRef moduleName = ModuleID.Item.str(); - auto it = PlaceholderDependencyModuleMap.find(moduleName); - // If no placeholder module stub path is given matches the name, return with an - // error code. - if (it == PlaceholderDependencyModuleMap.end()) { - return std::make_error_code(std::errc::not_supported); - } - auto &moduleInfo = it->getValue(); - assert(!moduleInfo.moduleBuffer && - "Placeholder dependency module stubs cannot have an associated buffer"); - auto dependencies = ModuleDependencies::forPlaceholderSwiftModuleStub( - moduleInfo.modulePath, moduleInfo.moduleDocPath, - moduleInfo.moduleSourceInfoPath); - this->dependencies = std::move(dependencies); - return std::error_code{}; +std::error_code PlaceholderSwiftModuleScanner::findModuleFilesInDirectory( + AccessPathElem ModuleID, const SerializedModuleBaseName &BaseName, + SmallVectorImpl *ModuleInterfacePath, + std::unique_ptr *ModuleBuffer, + std::unique_ptr *ModuleDocBuffer, + std::unique_ptr *ModuleSourceInfoBuffer, + bool IsFramework) { + StringRef moduleName = ModuleID.Item.str(); + auto it = PlaceholderDependencyModuleMap.find(moduleName); + // If no placeholder module stub path is given matches the name, return with an + // error code. + if (it == PlaceholderDependencyModuleMap.end()) { + return std::make_error_code(std::errc::not_supported); } -}; -} // namespace + auto &moduleInfo = it->getValue(); + assert(!moduleInfo.moduleBuffer && + "Placeholder dependency module stubs cannot have an associated buffer"); + + auto dependencies = ModuleDependencies::forPlaceholderSwiftModuleStub( + moduleInfo.modulePath, moduleInfo.moduleDocPath, + moduleInfo.moduleSourceInfoPath); + this->dependencies = std::move(dependencies); + return std::error_code{}; +} static std::vector getCompiledCandidates(ASTContext &ctx, StringRef moduleName, diff --git a/test/ScanDependencies/can_import_placeholder.swift b/test/ScanDependencies/can_import_placeholder.swift new file mode 100644 index 0000000000000..8b64644fc9a9b --- /dev/null +++ b/test/ScanDependencies/can_import_placeholder.swift @@ -0,0 +1,55 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache +// RUN: mkdir -p %t/inputs + +// RUN: echo "[{" > %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"SomeExternalModule\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/t/inputs/SomeExternalModule.swiftmodule\"," >> %/t/inputs/map.json +// RUN: echo "\"docPath\": \"%/t/inputs/SomeExternalModule.swiftdoc\"," >> %/t/inputs/map.json +// RUN: echo "\"sourceInfoPath\": \"%/t/inputs/SomeExternalModule.swiftsourceinfo\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}]" >> %/t/inputs/map.json + +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules + +// Check the contents of the JSON output +// RUN: %FileCheck %s < %t/deps.json + +// REQUIRES: executable_test +// REQUIRES: objc_interop +#if canImport(SomeExternalModule) +import SomeExternalModule +#endif + +// CHECK: "mainModuleName": "deps" + +/// --------Main module +// CHECK-LABEL: "modulePath": "deps.swiftmodule", +// CHECK-NEXT: sourceFiles +// CHECK-NEXT: can_import_placeholder.swift + +// CHECK: directDependencies +// CHECK-NEXT: { +// CHECK-NEXT: "swiftPlaceholder": "SomeExternalModule" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "SwiftOnoneSupport" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "F" +// CHECK-NEXT: } +// CHECK-NEXT: ], + From 7815ecf2a0137e82292b195d4dc92db346afc9d8 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Mon, 24 Aug 2020 23:44:11 -0300 Subject: [PATCH 019/120] [CSApply] Handle OptionalForce component when is initial component on key path expr --- lib/Sema/CSApply.cpp | 16 ++++++++++++++-- test/Constraints/keypath.swift | 8 ++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 276974ed4ab42..0add614158d91 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -4732,9 +4732,21 @@ namespace { KeyPathExpr::Component::forOptionalChain(objectTy, loc)); break; } - case KeyPathExpr::Component::Kind::OptionalForce: - buildKeyPathOptionalForceComponent(resolvedComponents); + case KeyPathExpr::Component::Kind::OptionalForce: { + // Handle force optional when it is the first component e.g. + // \String?.!.count + if (resolvedComponents.empty()) { + auto loc = origComponent.getLoc(); + auto objectTy = componentTy->getOptionalObjectType(); + assert(objectTy); + + resolvedComponents.push_back( + KeyPathExpr::Component::forOptionalForce(objectTy, loc)); + } else { + buildKeyPathOptionalForceComponent(resolvedComponents); + } break; + } case KeyPathExpr::Component::Kind::Invalid: { auto component = origComponent; component.setComponentType(leafTy); diff --git a/test/Constraints/keypath.swift b/test/Constraints/keypath.swift index 107739ee0b9a8..2512e9d9f372b 100644 --- a/test/Constraints/keypath.swift +++ b/test/Constraints/keypath.swift @@ -179,3 +179,11 @@ func key_path_root_mismatch(_ base: KeyPathBase?, subBase: KeyPathBaseSubtype let _ : T = subBase[keyPath: kpa] // expected-error {{key path with root type 'AnotherBase' cannot be applied to a base of type 'KeyPathBaseSubtype?'}} } + +// SR-13442 +func SR13442(_ x: KeyPath) -> T { "1"[keyPath: x] } + +func testSR13442() { + _ = SR13442(\.!.count) // OK + _ = SR13442(\String?.!.count) // OK +} From 8f72547e2fee46358a232981f62bceecd2d38504 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Tue, 25 Aug 2020 16:37:48 -0700 Subject: [PATCH 020/120] [test] ABIChecker: add a test for -use-interface-for-module flag --- test/api-digester/use-interface-for.swift | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/api-digester/use-interface-for.swift diff --git a/test/api-digester/use-interface-for.swift b/test/api-digester/use-interface-for.swift new file mode 100644 index 0000000000000..6399084893092 --- /dev/null +++ b/test/api-digester/use-interface-for.swift @@ -0,0 +1,20 @@ +// REQUIRES: VENDOR=apple + +// RUN: %empty-directory(%t.mod) +// RUN: %empty-directory(%t.sdk) +// RUN: %empty-directory(%t.module-cache) + +// RUN: echo "public func foo() {}" > %t.swift + +// RUN: %target-swift-frontend -emit-module -emit-module-interface-path %t.mod/cake.swiftinterface %t.swift %clang-importer-sdk-nosource -parse-as-library -enable-library-evolution -disable-objc-attr-requires-foundation-module -module-cache-path %t.module-cache -emit-module-path %t.mod/cake.swiftmodule -module-name cake -swift-version 5 + +// Step 1: we should be able to load if we prefer cake.swiftinterface +// RUN: %api-digester -dump-sdk -print-module -module cake -I %t.mod -sdk %clang-importer-sdk-path -module-cache-path %t.module-cache -o %t.json -abi -abort-on-module-fail -use-interface-for-module cake + +// RUN: echo "Swift Syntax Error" >> %t.mod/cake.swiftinterface + +// Step 2: we shouldn't be able to load if we prefer cake.swiftinterface and cake.swiftinterface is broken +// RUN: not %api-digester -dump-sdk -print-module -module cake -I %t.mod -sdk %clang-importer-sdk-path -module-cache-path %t.module-cache -o %t.json -abi -abort-on-module-fail -use-interface-for-module cake + +// Step 3: we should be able to load if we don't prefer cake.swiftinterface +// RUN: %api-digester -dump-sdk -print-module -module cake -I %t.mod -sdk %clang-importer-sdk-path -module-cache-path %t.module-cache -o %t.json -abi -abort-on-module-fail From d82a767e1396e103c0186564f1e7105a9e5a59ba Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 25 Aug 2020 16:02:21 -0700 Subject: [PATCH 021/120] SIL: Add a SILFunction::Purpose for global init once functions --- include/swift/SIL/SILFunction.h | 5 +++++ lib/SIL/IR/SILPrinter.cpp | 3 +++ lib/SIL/Parser/ParseSIL.cpp | 2 ++ lib/SILGen/SILGen.cpp | 1 + lib/Serialization/ModuleFormat.h | 2 +- test/SILGen/default_arguments.swift | 2 +- test/SILGen/global_resilience.swift | 4 ++-- test/SILGen/lazy_globals.swift | 8 ++++---- test/SILGen/lazy_globals_multiple_vars.swift | 6 +++--- test/SILGen/observers.swift | 2 +- test/SILOptimizer/access_marker_verify.swift | 6 +++--- 11 files changed, 26 insertions(+), 15 deletions(-) diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 5297539e43494..ec81558ab3179 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -121,6 +121,7 @@ class SILFunction enum class Purpose : uint8_t { None, GlobalInit, + GlobalInitOnceFunction, LazyPropertyGetter }; @@ -832,6 +833,10 @@ class SILFunction /// function itself does not need this attribute. It is private and only /// called within the addressor. bool isGlobalInit() const { return specialPurpose == Purpose::GlobalInit; } + + bool isGlobalInitOnceFunction() const { + return specialPurpose == Purpose::GlobalInitOnceFunction; + } bool isLazyPropertyGetter() const { return specialPurpose == Purpose::LazyPropertyGetter; diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 52d038ec240a9..17c3810556b77 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2606,6 +2606,9 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { case SILFunction::Purpose::GlobalInit: OS << "[global_init] "; break; + case SILFunction::Purpose::GlobalInitOnceFunction: + OS << "[global_init_once_fn] "; + break; case SILFunction::Purpose::LazyPropertyGetter: OS << "[lazy_getter] "; break; diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index f70112cbcdaad..f12b216fe0802 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -961,6 +961,8 @@ static bool parseDeclSILOptional(bool *isTransparent, *specialPurpose = SILFunction::Purpose::GlobalInit; else if (specialPurpose && SP.P.Tok.getText() == "lazy_getter") *specialPurpose = SILFunction::Purpose::LazyPropertyGetter; + else if (specialPurpose && SP.P.Tok.getText() == "global_init_once_fn") + *specialPurpose = SILFunction::Purpose::GlobalInitOnceFunction; else if (isWeakImported && SP.P.Tok.getText() == "weak_imported") { if (M.getASTContext().LangOpts.Target.isOSBinFormatCOFF()) SP.P.diagnose(SP.P.Tok, diag::attr_unsupported_on_target, diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index dffd4c3b8dfdf..883a43f0f2e6e 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1366,6 +1366,7 @@ SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, auto *f = builder.createFunction( SILLinkage::Private, funcName, initSILType, nullptr, SILLocation(binding), IsNotBare, IsNotTransparent, IsNotSerialized, IsNotDynamic); + f->setSpecialPurpose(SILFunction::Purpose::GlobalInitOnceFunction); f->setDebugScope(new (M) SILDebugScope(RegularLocation(binding), f)); auto dc = binding->getDeclContext(); SILGenFunction(*this, *f, dc).emitLazyGlobalInitializer(binding, pbdEntry); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 50a000ccb62ae..2b95150e58ff2 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 574; // reapply isUserAccessible +const uint16_t SWIFTMODULE_VERSION_MINOR = 575; // GlobalInitOnceFunction SILFunction purpose /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/test/SILGen/default_arguments.swift b/test/SILGen/default_arguments.swift index 8fc06fb8ed67a..94450cf10b7a9 100644 --- a/test/SILGen/default_arguments.swift +++ b/test/SILGen/default_arguments.swift @@ -156,7 +156,7 @@ class Foo { return x } - // CHECK-LABEL: sil private [ossa] @globalinit_33_E52D764B1F2009F2390B2B8DF62DAEB8_func0 + // CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_33_E52D764B1F2009F2390B2B8DF62DAEB8_func0 // CHECK: string_literal utf8 "Foo" static let x = Foo(int:0) diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index 78b425a9ed684..ee01a547e3b4e 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -43,7 +43,7 @@ public var myEmptyGlobal = MyEmptyStruct() // Mutable addressor for fixed-layout global -// CHECK-LABEL: sil private [ossa] @globalinit_{{.*}}_func1 +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_{{.*}}_func1 // CHECK: alloc_global @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVv // CHECK: return @@ -52,7 +52,7 @@ public var myEmptyGlobal = MyEmptyStruct() // CHECK: global_addr @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVv // CHECK: return -// CHECK-OPT-LABEL: sil private @globalinit_{{.*}}_func1 +// CHECK-OPT-LABEL: sil private [global_init_once_fn] @globalinit_{{.*}}_func1 // CHECK-OPT: alloc_global @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVv // CHECK-OPT: return diff --git a/test/SILGen/lazy_globals.swift b/test/SILGen/lazy_globals.swift index 4e1cc299c3468..c772aa7a2bd27 100644 --- a/test/SILGen/lazy_globals.swift +++ b/test/SILGen/lazy_globals.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s -// CHECK: sil private [ossa] @globalinit_[[T:.*]]_func0 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T:.*]]_func0 : $@convention(c) () -> () { // CHECK: alloc_global @$s12lazy_globals1xSiv // CHECK: [[XADDR:%.*]] = global_addr @$s12lazy_globals1xSivp : $*Int // CHECK: store {{%.*}} to [trivial] [[XADDR]] : $*Int @@ -16,7 +16,7 @@ // CHECK: } var x: Int = 0 -// CHECK: sil private [ossa] @globalinit_[[T:.*]]_func1 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T:.*]]_func1 : $@convention(c) () -> () { // CHECK: alloc_global @$s12lazy_globals3FooV3fooSivpZ // CHECK: [[XADDR:%.*]] = global_addr @$s12lazy_globals3FooV3fooSivpZ : $*Int // CHECK: store {{.*}} to [trivial] [[XADDR]] : $*Int @@ -40,7 +40,7 @@ struct Foo { static var initialized: Int = 57 } -// CHECK: sil private [ossa] @globalinit_[[T:.*]]_func3 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T:.*]]_func3 : $@convention(c) () -> () { // CHECK: alloc_global @$s12lazy_globals3BarO3barSivpZ // CHECK: [[XADDR:%.*]] = global_addr @$s12lazy_globals3BarO3barSivpZ : $*Int // CHECK: store {{.*}} to [trivial] [[XADDR]] : $*Int @@ -63,7 +63,7 @@ enum Bar { func f() -> (Int, Int) { return (1, 2) } -// CHECK: sil private [ossa] @globalinit_[[T]]_func4 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T]]_func4 : $@convention(c) () -> () { // CHECK: function_ref @$s12lazy_globals1fSi_SityF : $@convention(thin) () -> (Int, Int) // CHECK: sil hidden [global_init] [ossa] @$s12lazy_globals2a1Sivau : $@convention(thin) () -> Builtin.RawPointer // CHECK: function_ref @globalinit_[[T]]_func4 : $@convention(c) () -> () diff --git a/test/SILGen/lazy_globals_multiple_vars.swift b/test/SILGen/lazy_globals_multiple_vars.swift index 0c415d5c20231..bad5e0b41e1cc 100644 --- a/test/SILGen/lazy_globals_multiple_vars.swift +++ b/test/SILGen/lazy_globals_multiple_vars.swift @@ -1,6 +1,6 @@ // RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s -// CHECK: sil private [ossa] [[INIT_A_B:@globalinit_.*]] : +// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_A_B:@globalinit_.*]] : // CHECK: alloc_global @$s26lazy_globals_multiple_vars1aSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1aSiv // CHECK: alloc_global @$s26lazy_globals_multiple_vars1bSiv @@ -13,7 +13,7 @@ // CHECK: function_ref [[INIT_A_B]] var (a, b) = (1, 2) -// CHECK: sil private [ossa] [[INIT_C:@globalinit_.*]] : +// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_C:@globalinit_.*]] : // CHECK-NOT: global_addr @$s26lazy_globals_multiple_vars1dSiv // CHECK: alloc_global @$s26lazy_globals_multiple_vars1cSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1cSiv @@ -21,7 +21,7 @@ var (a, b) = (1, 2) // CHECK: sil hidden [global_init] [ossa] @$s26lazy_globals_multiple_vars1cSivau // CHECK: global_addr [[TOKEN_C:@globalinit_.*]] : // CHECK: function_ref [[INIT_C]] -// CHECK: sil private [ossa] [[INIT_D:@globalinit_.*]] : +// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_D:@globalinit_.*]] : // CHECK-NOT: global_addr @$s26lazy_globals_multiple_vars1cSiv // CHECK: alloc_global @$s26lazy_globals_multiple_vars1dSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1dSiv diff --git a/test/SILGen/observers.swift b/test/SILGen/observers.swift index 3051c0a1126c7..22ac63b2a603e 100644 --- a/test/SILGen/observers.swift +++ b/test/SILGen/observers.swift @@ -171,7 +171,7 @@ public struct DidSetWillSetTests { var global_observing_property : Int = zero { // The variable is initialized with "zero". - // CHECK-LABEL: sil private [ossa] @globalinit_{{.*}}_func1 : $@convention(c) () -> () { + // CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_{{.*}}_func1 : $@convention(c) () -> () { // CHECK: bb0: // CHECK-NEXT: alloc_global @$s9observers25global_observing_propertySiv // CHECK-NEXT: %1 = global_addr @$s9observers25global_observing_propertySivp : $*Int diff --git a/test/SILOptimizer/access_marker_verify.swift b/test/SILOptimizer/access_marker_verify.swift index 1ed6f62f84fdd..af14c54be2fcf 100644 --- a/test/SILOptimizer/access_marker_verify.swift +++ b/test/SILOptimizer/access_marker_verify.swift @@ -627,7 +627,7 @@ func testShims() -> UInt32 { // --- global variable initialization. var globalString1 = "⓪" // start non-empty -// CHECK-LABEL: sil private [ossa] @globalinit_33_{{.*}}_func0 : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_33_{{.*}}_func0 : $@convention(c) () -> () { // CHECK: alloc_global @$s20access_marker_verify13globalString1SSvp // CHECK: [[GA:%.*]] = global_addr @$s20access_marker_verify13globalString1SSvp : $*String // CHECK: apply @@ -637,7 +637,7 @@ var globalString1 = "⓪" // start non-empty // CHECK-LABEL: } // end sil function 'globalinit_33_180BF7B9126DB0C8C6C26F15ACD01908_func0' var globalString2 = globalString1 -// CHECK-LABEL: sil private [ossa] @globalinit_33_180BF7B9126DB0C8C6C26F15ACD01908_func1 : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_33_180BF7B9126DB0C8C6C26F15ACD01908_func1 : $@convention(c) () -> () { // CHECK: alloc_global @$s20access_marker_verify13globalString2SSvp // CHECK: [[GA:%.*]] = global_addr @$s20access_marker_verify13globalString2SSvp : $*String // CHECK: apply @@ -1037,7 +1037,7 @@ func testPointerInit(x: Int, y: UnsafeMutablePointer) { class testInitExistentialGlobal { static var testProperty: P = StructP() } -// CHECK-LABEL: sil private [ossa] @globalinit{{.*}} : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit{{.*}} : $@convention(c) () -> () { // CHECK: alloc_global @$s20access_marker_verify25testInitExistentialGlobalC0D8PropertyAA1P_pvpZ // CHECK: [[GADR:%.*]] = global_addr @$s20access_marker_verify25testInitExistentialGlobalC0D8PropertyAA1P_pvpZ : $*P // CHECK: %{{.*}} = apply %{{.*}}({{.*}}) : $@convention(method) (@thin StructP.Type) -> StructP From f74a3b47fca9efa3c0d0005c63f5ae97afe918b9 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 25 Aug 2020 13:29:18 -0700 Subject: [PATCH 022/120] [SIL] Added async flag to SILExtInfo. --- include/swift/AST/ExtInfo.h | 26 +++++++++++++------ include/swift/AST/Types.h | 4 +-- include/swift/Demangling/TypeDecoder.h | 25 +++++++++++++----- lib/AST/ASTDemangler.cpp | 2 +- lib/AST/ASTPrinter.cpp | 3 +++ lib/SILGen/SILGen.cpp | 1 + lib/SILOptimizer/Transforms/Outliner.cpp | 3 ++- .../UtilityPasses/BugReducerTester.cpp | 3 ++- lib/Sema/TypeCheckType.cpp | 5 ++-- lib/Serialization/Deserialization.cpp | 3 ++- test/SIL/Parser/async.sil | 2 +- test/SIL/Serialization/basic.sil | 2 +- 12 files changed, 53 insertions(+), 26 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index a19023f621042..51592181aabf2 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -495,16 +495,17 @@ class SILExtInfoBuilder { // If bits are added or removed, then TypeBase::SILFunctionTypeBits // and NumMaskBits must be updated, and they must match. - // |representation|pseudogeneric| noescape |differentiability| - // | 0 .. 3 | 4 | 5 | 6 .. 7 | + // |representation|pseudogeneric| noescape | async | differentiability| + // | 0 .. 3 | 4 | 5 | 6 | 7 .. 8 | // enum : unsigned { RepresentationMask = 0xF << 0, PseudogenericMask = 1 << 4, NoEscapeMask = 1 << 5, - DifferentiabilityMaskOffset = 6, + AsyncMask = 1 << 6, + DifferentiabilityMaskOffset = 7, DifferentiabilityMask = 0x3 << DifferentiabilityMaskOffset, - NumMaskBits = 8 + NumMaskBits = 9 }; unsigned bits; // Naturally sized for speed. @@ -523,10 +524,11 @@ class SILExtInfoBuilder { // Constructor for polymorphic type. SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, - DifferentiabilityKind diffKind, const clang::Type *type) + bool isAsync, DifferentiabilityKind diffKind, + const clang::Type *type) : SILExtInfoBuilder( ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | - (isNoEscape ? NoEscapeMask : 0) | + (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | (((unsigned)diffKind << DifferentiabilityMaskOffset) & DifferentiabilityMask), ClangTypeInfo(type)) {} @@ -552,6 +554,8 @@ class SILExtInfoBuilder { // Is this function guaranteed to be no-escape by the type system? constexpr bool isNoEscape() const { return bits & NoEscapeMask; } + constexpr bool isAsync() const { return bits & AsyncMask; } + constexpr DifferentiabilityKind getDifferentiabilityKind() const { return DifferentiabilityKind((bits & DifferentiabilityMask) >> DifferentiabilityMaskOffset); @@ -616,6 +620,10 @@ class SILExtInfoBuilder { : (bits & ~NoEscapeMask), clangTypeInfo); } + SILExtInfoBuilder withAsync(bool isAsync = true) const { + return SILExtInfoBuilder(isAsync ? (bits | AsyncMask) : (bits & ~AsyncMask), + clangTypeInfo); + } SILExtInfoBuilder withDifferentiabilityKind(DifferentiabilityKind differentiability) const { return SILExtInfoBuilder( @@ -657,8 +665,8 @@ class SILExtInfo { static SILExtInfo getThin() { return SILExtInfoBuilder(SILExtInfoBuilder::Representation::Thin, false, - false, DifferentiabilityKind::NonDifferentiable, - nullptr) + false, false, + DifferentiabilityKind::NonDifferentiable, nullptr) .build(); } @@ -681,6 +689,8 @@ class SILExtInfo { constexpr bool isNoEscape() const { return builder.isNoEscape(); } + constexpr bool isAsync() const { return builder.isAsync(); } + constexpr DifferentiabilityKind getDifferentiabilityKind() const { return builder.getDifferentiabilityKind(); } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 21e9a5ccd37f6..1dd1aed9bd772 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -308,7 +308,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { protected: enum { NumAFTExtInfoBits = 9 }; - enum { NumSILExtInfoBits = 8 }; + enum { NumSILExtInfoBits = 9 }; union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) + @@ -4046,7 +4046,7 @@ class SILFunctionType final return SILCoroutineKind(Bits.SILFunctionType.CoroutineKind); } - bool isAsync() const { return Bits.SILFunctionType.IsAsync; } + bool isAsync() const { return getExtInfo().isAsync(); } /// Return the array of all the yields. ArrayRef getYields() const { diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index e591e978dccd1..1726133027988 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -238,49 +238,60 @@ class ImplFunctionTypeFlags { unsigned Rep : 3; unsigned Pseudogeneric : 1; unsigned Escaping : 1; + unsigned Async : 1; unsigned DifferentiabilityKind : 2; public: ImplFunctionTypeFlags() - : Rep(0), Pseudogeneric(0), Escaping(0), DifferentiabilityKind(0) {} + : Rep(0), Pseudogeneric(0), Escaping(0), Async(0), + DifferentiabilityKind(0) {} ImplFunctionTypeFlags(ImplFunctionRepresentation rep, bool pseudogeneric, - bool noescape, + bool noescape, bool async, ImplFunctionDifferentiabilityKind diffKind) : Rep(unsigned(rep)), Pseudogeneric(pseudogeneric), Escaping(noescape), - DifferentiabilityKind(unsigned(diffKind)) {} + Async(async), DifferentiabilityKind(unsigned(diffKind)) {} ImplFunctionTypeFlags withRepresentation(ImplFunctionRepresentation rep) const { return ImplFunctionTypeFlags( - rep, Pseudogeneric, Escaping, + rep, Pseudogeneric, Escaping, Async, + ImplFunctionDifferentiabilityKind(DifferentiabilityKind)); + } + + ImplFunctionTypeFlags + withAsync() const { + return ImplFunctionTypeFlags( + ImplFunctionRepresentation(Rep), Pseudogeneric, Escaping, true, ImplFunctionDifferentiabilityKind(DifferentiabilityKind)); } ImplFunctionTypeFlags withEscaping() const { return ImplFunctionTypeFlags( - ImplFunctionRepresentation(Rep), Pseudogeneric, true, + ImplFunctionRepresentation(Rep), Pseudogeneric, true, Async, ImplFunctionDifferentiabilityKind(DifferentiabilityKind)); } ImplFunctionTypeFlags withPseudogeneric() const { return ImplFunctionTypeFlags( - ImplFunctionRepresentation(Rep), true, Escaping, + ImplFunctionRepresentation(Rep), true, Escaping, Async, ImplFunctionDifferentiabilityKind(DifferentiabilityKind)); } ImplFunctionTypeFlags withDifferentiabilityKind(ImplFunctionDifferentiabilityKind diffKind) const { return ImplFunctionTypeFlags(ImplFunctionRepresentation(Rep), Pseudogeneric, - Escaping, diffKind); + Escaping, Async, diffKind); } ImplFunctionRepresentation getRepresentation() const { return ImplFunctionRepresentation(Rep); } + bool isAsync() const { return Async; } + bool isEscaping() const { return Escaping; } bool isPseudogeneric() const { return Pseudogeneric; } diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 44d32c84a7a7d..327b401cd5df8 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -530,7 +530,7 @@ Type ASTBuilder::createImplFunctionType( // [TODO: Store-SIL-Clang-type] auto einfo = SILExtInfoBuilder(representation, flags.isPseudogeneric(), - !flags.isEscaping(), diffKind, + !flags.isEscaping(), flags.isAsync(), diffKind, /*clangFunctionType*/ nullptr) .build(); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index aff1100a37bf9..5b4d41700d3e0 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -4133,6 +4133,9 @@ class TypePrinter : public TypeVisitor { if (info.isNoEscape()) { Printer.printSimpleAttr("@noescape") << " "; } + if (info.isAsync()) { + Printer.printSimpleAttr("@async") << " "; + } } void visitAnyFunctionTypeParams(ArrayRef Params, diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index cd36b343f0e58..4154b9e9c4ca8 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -415,6 +415,7 @@ SILGenModule::getKeyPathProjectionCoroutine(bool isReadAccess, SILFunctionType::ExtInfoBuilder(SILFunctionTypeRepresentation::Thin, /*pseudogeneric*/ false, /*non-escaping*/ false, + /*async*/ false, DifferentiabilityKind::NonDifferentiable, /*clangFunctionType*/ nullptr) .build(); diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index e052a6b000f3c..b5a89537fbe0e 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -298,7 +298,7 @@ CanSILFunctionType BridgedProperty::getOutlinedFunctionType(SILModule &M) { auto ExtInfo = SILFunctionType::ExtInfoBuilder( SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, /*noescape*/ false, - DifferentiabilityKind::NonDifferentiable, + /*async*/ false, DifferentiabilityKind::NonDifferentiable, /*clangFunctionType*/ nullptr) .build(); auto FunctionType = SILFunctionType::get( @@ -1181,6 +1181,7 @@ CanSILFunctionType ObjCMethodCall::getOutlinedFunctionType(SILModule &M) { SILFunctionType::ExtInfoBuilder(SILFunctionType::Representation::Thin, /*pseudogeneric*/ false, /*noescape*/ false, + /*async*/ false, DifferentiabilityKind::NonDifferentiable, /*clangFunctionType*/ nullptr) .build(); diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index a8f823e584d5a..9ef7e2d70befd 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -86,7 +86,8 @@ class BugReducerTester : public SILFunctionTransform { nullptr, SILFunctionType::ExtInfoBuilder( SILFunctionType::Representation::Thin, false /*isPseudoGeneric*/, - false /*noescape*/, DifferentiabilityKind::NonDifferentiable, + false /*noescape*/, false /*async*/, + DifferentiabilityKind::NonDifferentiable, nullptr /*clangFunctionType*/) .build(), SILCoroutineKind::None, ParameterConvention::Direct_Unowned, diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 8ec5b7a9f942f..a3845b07b62ae 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2146,8 +2146,6 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, SILFunctionType::Representation rep; TypeRepr *witnessMethodProtocol = nullptr; - auto isAsync = attrs.has(TAK_async); - auto coroutineKind = SILCoroutineKind::None; if (attrs.has(TAK_yield_once)) { coroutineKind = SILCoroutineKind::YieldOnce; @@ -2226,7 +2224,8 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // [TODO: Store-SIL-Clang-type] auto extInfo = SILFunctionType::ExtInfoBuilder( rep, attrs.has(TAK_pseudogeneric), - attrs.has(TAK_noescape), diffKind, nullptr) + attrs.has(TAK_noescape), attrs.has(TAK_async), + diffKind, nullptr) .build(); ty = resolveSILFunctionType(fnRepr, options, coroutineKind, extInfo, diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index a2b83b1faaf00..75b27d0883ec2 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5419,7 +5419,8 @@ class TypeDeserializer { auto extInfo = SILFunctionType::ExtInfoBuilder(*representation, pseudogeneric, - noescape, *diffKind, clangFunctionType) + noescape, async, *diffKind, + clangFunctionType) .build(); // Process the coroutine kind. diff --git a/test/SIL/Parser/async.sil b/test/SIL/Parser/async.sil index 32cfdebb32c0f..f65cee1ca6e16 100644 --- a/test/SIL/Parser/async.sil +++ b/test/SIL/Parser/async.sil @@ -16,7 +16,7 @@ bb0(%int : $Builtin.Int32): return %0 : $() } -// CHECK: sil @async_test : $@async +// CHECK: sil @async_test : $@convention(thin) @async sil @async_test : $@async () -> () { bb0: %0 = tuple () diff --git a/test/SIL/Serialization/basic.sil b/test/SIL/Serialization/basic.sil index 1d9f1de1a810e..f6950e30ab2a5 100644 --- a/test/SIL/Serialization/basic.sil +++ b/test/SIL/Serialization/basic.sil @@ -15,7 +15,7 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): return undef : $() } -// CHECK-LABEL: sil @async_test : $@async @convention(thin) +// CHECK-LABEL: sil @async_test : $@convention(thin) @async sil @async_test : $@async () -> () { bb0: %0 = tuple () From 6d6165a9db02dc98c037947bdb91071bf5104dcf Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 25 Aug 2020 17:43:27 -0700 Subject: [PATCH 023/120] SILOptimizer: Recognize globalinit once functions using an attribute instead of symbol name matching --- lib/SIL/IR/SILGlobalVariable.cpp | 2 +- lib/SILOptimizer/ARC/ARCLoopOpts.cpp | 2 +- lib/SILOptimizer/ARC/ARCSequenceOpts.cpp | 4 ++-- lib/SILOptimizer/IPO/GlobalOpt.cpp | 2 +- test/SILOptimizer/globalopt_trivial_nontrivial.sil | 6 +++--- test/SILOptimizer/globalopt_trivial_nontrivial_ossa.sil | 6 +++--- test/SILOptimizer/static_initializer.sil | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/SIL/IR/SILGlobalVariable.cpp b/lib/SIL/IR/SILGlobalVariable.cpp index 609a8318b153d..85407811bae76 100644 --- a/lib/SIL/IR/SILGlobalVariable.cpp +++ b/lib/SIL/IR/SILGlobalVariable.cpp @@ -272,7 +272,7 @@ SILFunction *swift::findInitializer(SILFunction *AddrF, if (!CallToOnce) return nullptr; SILFunction *callee = getCalleeOfOnceCall(CallToOnce); - if (!callee->getName().startswith("globalinit_")) + if (!callee->isGlobalInitOnceFunction()) return nullptr; return callee; } diff --git a/lib/SILOptimizer/ARC/ARCLoopOpts.cpp b/lib/SILOptimizer/ARC/ARCLoopOpts.cpp index 2cadf6426c013..aa54592497e51 100644 --- a/lib/SILOptimizer/ARC/ARCLoopOpts.cpp +++ b/lib/SILOptimizer/ARC/ARCLoopOpts.cpp @@ -47,7 +47,7 @@ class ARCLoopOpts : public SILFunctionTransform { return; // Skip global init functions. - if (F->getName().startswith("globalinit_")) + if (F->isGlobalInitOnceFunction()) return; auto *LA = getAnalysis(); diff --git a/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp b/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp index fa0602b04bd4f..f628be8b5b29c 100644 --- a/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp +++ b/lib/SILOptimizer/ARC/ARCSequenceOpts.cpp @@ -183,7 +183,7 @@ processFunctionWithoutLoopSupport(SILFunction &F, bool FreezePostDomReleases, // globalinit_func. Since that is not *that* interesting from an ARC // perspective (i.e. no ref count operations in a loop), disable it on such // functions temporarily in order to unblock others. This should be removed. - if (F.getName().startswith("globalinit_")) + if (F.isGlobalInitOnceFunction()) return false; LLVM_DEBUG(llvm::dbgs() << "***** Processing " << F.getName() << " *****\n"); @@ -231,7 +231,7 @@ static bool processFunctionWithLoopSupport( // globalinit_func. Since that is not *that* interesting from an ARC // perspective (i.e. no ref count operations in a loop), disable it on such // functions temporarily in order to unblock others. This should be removed. - if (F.getName().startswith("globalinit_")) + if (F.isGlobalInitOnceFunction()) return false; LLVM_DEBUG(llvm::dbgs() << "***** Processing " << F.getName() << " *****\n"); diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 113d71ccb32b8..1c34038499262 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -260,7 +260,7 @@ void SILGlobalOpt::collectOnceCall(BuiltinInst *BI) { UnhandledOnceCallee = true; return; } - if (!Callee->getName().startswith("globalinit_")) + if (!Callee->isGlobalInitOnceFunction()) return; // We currently disable optimizing the initializer if a globalinit_func diff --git a/test/SILOptimizer/globalopt_trivial_nontrivial.sil b/test/SILOptimizer/globalopt_trivial_nontrivial.sil index 3625c10946211..8f094f633febb 100644 --- a/test/SILOptimizer/globalopt_trivial_nontrivial.sil +++ b/test/SILOptimizer/globalopt_trivial_nontrivial.sil @@ -38,7 +38,7 @@ sil_global private @globalinit_nontrivialglobal_token : $Builtin.Word sil_global hidden [let] @$nontrivialglobal : $TClass -sil private @globalinit_trivialglobal_func : $@convention(c) () -> () { +sil private [global_init_once_fn] @globalinit_trivialglobal_func : $@convention(c) () -> () { bb0: alloc_global @$trivialglobal %1 = global_addr @$trivialglobal : $*TStruct @@ -83,13 +83,13 @@ bb0: return %4 : $Int32 } -// CHECK-LABEL: sil private @globalinit_nontrivialglobal_func : +// CHECK-LABEL: sil private [global_init_once_fn] @globalinit_nontrivialglobal_func : // CHECK: alloc_global @$nontrivialglobal // CHECK: [[GLOBL_ADDR:%.*]] = global_addr @$nontrivialglobal : $*TClass // CHECK: [[REF:%.*]] = alloc_ref $TClass // CHECK: store [[REF]] to [[GLOBL_ADDR]] : $*TClass // CHECK: } // end sil function 'globalinit_nontrivialglobal_func' -sil private @globalinit_nontrivialglobal_func : $@convention(c) () -> () { +sil private [global_init_once_fn] @globalinit_nontrivialglobal_func : $@convention(c) () -> () { bb0: alloc_global @$nontrivialglobal %1 = global_addr @$nontrivialglobal : $*TClass diff --git a/test/SILOptimizer/globalopt_trivial_nontrivial_ossa.sil b/test/SILOptimizer/globalopt_trivial_nontrivial_ossa.sil index 182210cb2671e..433aff21f011f 100644 --- a/test/SILOptimizer/globalopt_trivial_nontrivial_ossa.sil +++ b/test/SILOptimizer/globalopt_trivial_nontrivial_ossa.sil @@ -32,7 +32,7 @@ sil_global private @globalinit_nontrivialglobal_token : $Builtin.Word sil_global hidden [let] @$nontrivialglobal : $TClass -sil private [ossa] @globalinit_trivialglobal_func : $@convention(c) () -> () { +sil private [global_init_once_fn] [ossa] @globalinit_trivialglobal_func : $@convention(c) () -> () { bb0: alloc_global @$trivialglobal %1 = global_addr @$trivialglobal : $*TStruct @@ -75,13 +75,13 @@ bb0: return %4 : $Int32 } -// CHECK-LABEL: sil private [ossa] @globalinit_nontrivialglobal_func : +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_nontrivialglobal_func : // CHECK: alloc_global @$nontrivialglobal // CHECK: [[GLOBL_ADDR:%.*]] = global_addr @$nontrivialglobal : $*TClass // CHECK: [[REF:%.*]] = alloc_ref $TClass // CHECK: store [[REF]] to [init] [[GLOBL_ADDR]] : $*TClass // CHECK: } // end sil function 'globalinit_nontrivialglobal_func' -sil private [ossa] @globalinit_nontrivialglobal_func : $@convention(c) () -> () { +sil private [global_init_once_fn] [ossa] @globalinit_nontrivialglobal_func : $@convention(c) () -> () { bb0: alloc_global @$nontrivialglobal %1 = global_addr @$nontrivialglobal : $*TClass diff --git a/test/SILOptimizer/static_initializer.sil b/test/SILOptimizer/static_initializer.sil index bcdb29794c702..dbc5e28526a4f 100644 --- a/test/SILOptimizer/static_initializer.sil +++ b/test/SILOptimizer/static_initializer.sil @@ -25,8 +25,8 @@ sil_global private @globalinit_token0 : $Builtin.Word // CHECK-NEXT: } sil_global @_Tv2ch1xSi : $Outer -// CHECK-LABEL: sil private @globalinit_func0 : $@convention(c) () -> () { -sil private @globalinit_func0 : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] @globalinit_func0 : $@convention(c) () -> () { +sil private [global_init_once_fn] @globalinit_func0 : $@convention(c) () -> () { bb0: %0 = global_addr @_Tv2ch1xSi : $*Outer %1 = integer_literal $Builtin.Int32, 2 From 79b0a7ef75ba74a7ecb802d958c3db1aa0a6fb8d Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Wed, 26 Aug 2020 02:05:29 +0100 Subject: [PATCH 024/120] [CSDiagnostics] Emit fix-its to insert requirement stubs for missing protocols in context (#33628) * [CSDiagnostics] Emit fix-its to insert requirement stubs in editor mode for missing protocols in context * [CSDiagnostics] Only include missing requirements in stub fix-it for missing protocols in context The conforming type may already have declarations that could satisfy a requirement, so we shouldn't include it in the fix-it --- include/swift/AST/ProtocolConformance.h | 17 +++---- lib/Sema/CSDiagnostics.cpp | 35 ++++++++++++++- .../fixits_missing_protocols_in_context.swift | 45 +++++++++++++++++++ 3 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 test/decl/protocol/fixits_missing_protocols_in_context.swift diff --git a/include/swift/AST/ProtocolConformance.h b/include/swift/AST/ProtocolConformance.h index 1136fb5be73cd..1f61cdf438175 100644 --- a/include/swift/AST/ProtocolConformance.h +++ b/include/swift/AST/ProtocolConformance.h @@ -465,21 +465,22 @@ class NormalProtocolConformance : public RootProtocolConformance, uint64_t LoaderContextData; friend class ASTContext; + void resolveLazyInfo() const; + + void differenceAndStoreConditionalRequirements() const; + +public: NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol, SourceLoc loc, DeclContext *dc, ProtocolConformanceState state) - : RootProtocolConformance(ProtocolConformanceKind::Normal, conformingType), - ProtocolAndState(protocol, state), Loc(loc), ContextAndInvalid(dc, false) - { + : RootProtocolConformance(ProtocolConformanceKind::Normal, + conformingType), + ProtocolAndState(protocol, state), Loc(loc), + ContextAndInvalid(dc, false) { assert(!conformingType->hasArchetype() && "ProtocolConformances should store interface types"); } - void resolveLazyInfo() const; - - void differenceAndStoreConditionalRequirements() const; - -public: /// Get the protocol being conformed to. ProtocolDecl *getProtocol() const { return ProtocolAndState.getPointer(); } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ec52d0ec612ad..4bef05bd9c4eb 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -17,8 +17,10 @@ #include "CSDiagnostics.h" #include "ConstraintSystem.h" #include "MiscDiagnostics.h" +#include "TypeCheckProtocol.h" #include "TypoCorrection.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Expr.h" @@ -2768,9 +2770,11 @@ bool ContextualFailure::tryProtocolConformanceFixIt( // Let's build a list of protocols that the context does not conform to. SmallVector missingProtoTypeStrings; + SmallVector missingProtocols; for (auto protocol : layout.getProtocols()) { if (!TypeChecker::conformsToProtocol(fromType, protocol->getDecl(), getDC())) { missingProtoTypeStrings.push_back(protocol->getString()); + missingProtocols.push_back(protocol->getDecl()); } } @@ -2792,8 +2796,6 @@ bool ContextualFailure::tryProtocolConformanceFixIt( // Emit a diagnostic to inform the user that they need to conform to the // missing protocols. - // - // TODO: Maybe also insert the requirement stubs? auto conformanceDiag = emitDiagnostic(diag::assign_protocol_conformance_fix_it, unwrappedToType, nominal->getDescriptiveKind(), fromType); @@ -2808,6 +2810,35 @@ bool ContextualFailure::tryProtocolConformanceFixIt( conformanceDiag.fixItInsert(nameEndLoc, ": " + protoString); } + // Emit fix-its to insert requirement stubs if we're in editor mode. + if (!getASTContext().LangOpts.DiagnosticsEditorMode) { + return true; + } + + { + llvm::SmallString<128> Text; + llvm::raw_svector_ostream SS(Text); + llvm::SetVector missingWitnesses; + for (auto protocol : missingProtocols) { + auto conformance = NormalProtocolConformance( + nominal->getDeclaredType(), protocol, SourceLoc(), nominal, + ProtocolConformanceState::Incomplete); + ConformanceChecker checker(getASTContext(), &conformance, + missingWitnesses); + checker.resolveValueWitnesses(); + checker.resolveTypeWitnesses(); + } + + for (auto decl : missingWitnesses) { + swift::printRequirementStub(decl, nominal, nominal->getDeclaredType(), + nominal->getStartLoc(), SS); + } + + if (!Text.empty()) { + conformanceDiag.fixItInsertAfter(nominal->getBraces().Start, Text.str()); + } + } + return true; } diff --git a/test/decl/protocol/fixits_missing_protocols_in_context.swift b/test/decl/protocol/fixits_missing_protocols_in_context.swift new file mode 100644 index 0000000000000..0511266896062 --- /dev/null +++ b/test/decl/protocol/fixits_missing_protocols_in_context.swift @@ -0,0 +1,45 @@ +// RUN: %target-swift-frontend -typecheck -diagnostics-editor-mode -verify %s + +// Test that we emit fix-its to insert requirement stubs for the missing protocol conformance, in addition to adding the conformance. + +protocol P { + func method() + var property: Int { get } +} + +class C { + var p: P? + + func assign() { + p = self + // expected-error@-1 {{cannot assign value of type 'C' to type 'P?'}} + // expected-note@-2 {{add missing conformance to 'P' to class 'C'}} {{8-8=: P}} {{10-10=\n func method() {\n <#code#>\n \}\n\n var property: Int\n}} + } +} + +// Test that we don't emit fix-it to insert a requirement stub if there is already a satisfying witness. + +class C1 { + var p: P? + + func assign() { + p = self + // expected-error@-1 {{cannot assign value of type 'C1' to type 'P?'}} + // expected-note@-2 {{add missing conformance to 'P' to class 'C1'}} {{9-9=: P}} {{11-11=\n var property: Int\n}} + } + + func method() {} +} + +class C2 { + var p: P? + + func assign() { + p = self + // expected-error@-1 {{cannot assign value of type 'C2' to type 'P?'}} + // expected-note@-2 {{add missing conformance to 'P' to class 'C2'}} {{9-9=: P}} + } + + func method() {} + var property: Int = 0 +} From a53f3633c04630f416bdf8e8105d78a3883c83fb Mon Sep 17 00:00:00 2001 From: David Zarzycki Date: Tue, 25 Aug 2020 10:02:01 -0400 Subject: [PATCH 025/120] [IRGen] NFC: Simplify IRGenModule::defineTypeMetadata Also make `IRGenModule::getAddrOfTypeMetadata()` more clear that a GEP is being forced and optional aliasing is involved rather than imply that full metadata always requires a GEP (which it does not). --- lib/IRGen/GenDecl.cpp | 87 ++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 250519e87905f..9ac46be66bbc9 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3943,32 +3943,14 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, return cast(addr); } - /// For concrete metadata, we want to use the initializer on the - /// "full metadata", and define the "direct" address point as an alias. - TypeMetadataAddress addrKind; - unsigned adjustmentIndex; - - auto nominal = concreteType->getAnyNominal(); - - // Native Swift class metadata has a destructor before the address point. - // Foreign class metadata candidates do not, and neither does value type - // metadata. - if (nominal && isa(nominal) && - !requiresForeignTypeMetadata(nominal)) { - addrKind = TypeMetadataAddress::FullMetadata; - adjustmentIndex = MetadataAdjustmentIndex::Class; - } else { - addrKind = TypeMetadataAddress::FullMetadata; - adjustmentIndex = MetadataAdjustmentIndex::ValueType; - } - auto entity = (isPrespecialized && !irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable( *this, concreteType)) ? LinkEntity::forNoncanonicalSpecializedGenericTypeMetadata( concreteType) - : LinkEntity::forTypeMetadata(concreteType, addrKind); + : LinkEntity::forTypeMetadata(concreteType, + TypeMetadataAddress::FullMetadata); auto DbgTy = DebugTypeInfo::getMetadata(MetatypeType::get(concreteType), entity.getDefaultDeclarationType(*this)->getPointerTo(), @@ -3986,30 +3968,35 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType, if (link.isUsed()) addUsedGlobal(var); - // Keep type metadata around for all types. - if (nominal) + /// For concrete metadata, we want to use the initializer on the + /// "full metadata", and define the "direct" address point as an alias. + unsigned adjustmentIndex = MetadataAdjustmentIndex::ValueType; + + if (auto nominal = concreteType->getAnyNominal()) { + // Keep type metadata around for all types. addRuntimeResolvableType(nominal); - // Don't define the alias for foreign type metadata or prespecialized generic - // metadata, since neither is ABI. - if ((nominal && requiresForeignTypeMetadata(nominal)) || isPrespecialized) - return var; + // Don't define the alias for foreign type metadata or prespecialized + // generic metadata, since neither is ABI. + if (requiresForeignTypeMetadata(nominal) || isPrespecialized) + return var; - // For concrete metadata, declare the alias to its address point. - auto directEntity = LinkEntity::forTypeMetadata(concreteType, - TypeMetadataAddress::AddressPoint); + // Native Swift class metadata has a destructor before the address point. + if (isa(nominal)) { + adjustmentIndex = MetadataAdjustmentIndex::Class; + } + } - llvm::Constant *addr = var; - // Do an adjustment if necessary. - if (adjustmentIndex) { - llvm::Constant *indices[] = { + llvm::Constant *indices[] = { llvm::ConstantInt::get(Int32Ty, 0), - llvm::ConstantInt::get(Int32Ty, adjustmentIndex) - }; - addr = llvm::ConstantExpr::getInBoundsGetElementPtr(/*Ty=*/nullptr, - addr, indices); - } + llvm::ConstantInt::get(Int32Ty, adjustmentIndex)}; + auto addr = llvm::ConstantExpr::getInBoundsGetElementPtr(/*Ty=*/nullptr, var, + indices); addr = llvm::ConstantExpr::getBitCast(addr, TypeMetadataPtrTy); + + // For concrete metadata, declare the alias to its address point. + auto directEntity = LinkEntity::forTypeMetadata( + concreteType, TypeMetadataAddress::AddressPoint); return defineAlias(directEntity, addr); } @@ -4030,15 +4017,17 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType, auto nominal = concreteType->getAnyNominal(); - llvm::Type *defaultVarTy; - unsigned adjustmentIndex; - bool foreign = nominal && requiresForeignTypeMetadata(nominal); + + // Foreign classes and prespecialized generic types do not use an alias into + // the full metadata and therefore require a GEP. bool fullMetadata = foreign || (concreteType->getAnyGeneric() && concreteType->getAnyGeneric()->isGenericContext()); - // Foreign classes reference the full metadata with a GEP. + llvm::Type *defaultVarTy; + unsigned adjustmentIndex; + if (fullMetadata) { defaultVarTy = FullTypeMetadataStructTy; if (concreteType->getClassOrBoundGenericClass() && !foreign) { @@ -4046,9 +4035,9 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType, } else { adjustmentIndex = MetadataAdjustmentIndex::ValueType; } - // The symbol for other nominal type metadata is generated at the address - // point. } else if (nominal) { + // The symbol for native non-generic nominal type metadata is generated at + // the aliased address point (see defineTypeMetadata() above). assert(!nominal->hasClangNode()); defaultVarTy = TypeMetadataStructTy; @@ -4082,13 +4071,9 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType, switch (canonicality) { case TypeMetadataCanonicality::Canonical: - if (fullMetadata) { - entity = LinkEntity::forTypeMetadata(concreteType, - TypeMetadataAddress::FullMetadata); - } else { - entity = LinkEntity::forTypeMetadata(concreteType, - TypeMetadataAddress::AddressPoint); - } + entity = LinkEntity::forTypeMetadata( + concreteType, fullMetadata ? TypeMetadataAddress::FullMetadata + : TypeMetadataAddress::AddressPoint); break; case TypeMetadataCanonicality::Noncanonical: entity = From 9962f799b7efffa4eaace4af0e8e7fd8f51a0b6b Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 26 Aug 2020 11:46:13 -0400 Subject: [PATCH 026/120] [RemoteMirror] Fix potential uninitialized data access in convertChild. The else branch of the type checking code just does assert(false). When asserts are off, the code will fall through and access the uninitialized FieldInfo variable. Instead, return something sensible for this error case. --- stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 114cf058512d0..92853104c25ea 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -447,6 +447,12 @@ static swift_childinfo_t convertChild(const TypeInfo *TI, unsigned Index) { FieldInfo = &(RecordTI->getFields()[Index]); } else { assert(false && "convertChild(TI): TI must be record or enum typeinfo"); + return { + "unknown TypeInfo kind", + 0, + SWIFT_UNKNOWN, + 0, + }; } return { From bf5fb4d2bb6ca857aa4d2a932413ea77e8face7b Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 26 Aug 2020 09:56:36 -0700 Subject: [PATCH 027/120] [CodeCompletion] Don't update VFS content hash map after each completion This was not needed. The list of depenencies should not be changed during fast-completion sessions. This was also harmful because it calls stat(2) for all the dependency files. rdar://problem/67773257 --- lib/IDE/CompletionInstance.cpp | 2 -- .../CodeComplete/complete_checkdeps_vfs.swift | 29 +++++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 2f8a6cd7040b5..1aeffb3304356 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -485,8 +485,6 @@ bool CompletionInstance::performCachedOperationIfPossible( } CachedReuseCount += 1; - cacheDependencyHashIfNeeded(CI, SM.getCodeCompletionBufferID(), - InMemoryDependencyHash); return true; } diff --git a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift index 388c09e57af33..84753c3a8c3f2 100644 --- a/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift +++ b/test/SourceKit/CodeComplete/complete_checkdeps_vfs.swift @@ -4,8 +4,8 @@ func foo(value: MyStruct) { // REQUIRES: shell -// RUN: DEPCHECK_INTERVAL=1 -// RUN: SLEEP_TIME=2 +// RUN: DEPCHECK_INTERVAL=2 +// RUN: SLEEP_TIME=3 // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/VFS) @@ -23,7 +23,14 @@ func foo(value: MyStruct) { // RUN: -shell -- echo "### Keep" == \ // RUN: -shell -- sleep ${SLEEP_TIME} == \ -// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject_mod/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ + +// RUN: -shell -- echo "### Rollback without sleep" == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift == \ + +// RUN: -shell -- echo "### After sleep" == \ +// RUN: -shell -- sleep ${SLEEP_TIME} == \ +// RUN: -req=complete -pos=2:9 -pass-as-sourcetext -vfs-files=%t/VFS/Main.swift=@%s,%t/VFS/Library.swift=@%S/Inputs/checkdeps/MyProject/Library.swift %t/VFS/Main.swift -- -target %target-triple %t/VFS/Main.swift %t/VFS/LibraryExt.swift %t/VFS/Library.swift \ // RUN: | %FileCheck %s @@ -50,3 +57,19 @@ func foo(value: MyStruct) { // CHECK-DAG: key.description: "self" // CHECK: ] // CHECK: key.reusingastcontext: 1 + +// CHECK-LABEL: ### Rollback without sleep +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod_mod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK: key.reusingastcontext: 1 + +// CHECK-LABEL: ### After sleep +// CHECK: key.results: [ +// CHECK-DAG: key.description: "myStructMethod()" +// CHECK-DAG: key.description: "extensionMethod()" +// CHECK-DAG: key.description: "self" +// CHECK: ] +// CHECK-NOT: key.reusingastcontext: 1 From 58604ff1bae00e9481cbbccc48b37e707c2fc48c Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 26 Aug 2020 13:24:44 -0400 Subject: [PATCH 028/120] [Runtime] Fix memory ordering on a store in ConcurrentReadableArray. --- include/swift/Runtime/Concurrent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index 10269653284df..6ee42cdc29c1d 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -517,7 +517,7 @@ template struct ConcurrentReadableArray { auto *newStorage = Storage::allocate(newCapacity); if (storage) { std::copy(storage->data(), storage->data() + count, newStorage->data()); - newStorage->Count.store(count, std::memory_order_relaxed); + newStorage->Count.store(count, std::memory_order_release); FreeList.push_back(storage); } From 66e2ce691a6b77af17dfe6195b1cd057c4b9ddbd Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 26 Aug 2020 11:38:16 -0700 Subject: [PATCH 029/120] Remove apparently unnecessary filter for CMO serializing references to globalinit once variables --- lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp index ea8429c854876..ce00fdd8b8e43 100644 --- a/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp +++ b/lib/SILOptimizer/IPO/CrossModuleSerializationSetup.cpp @@ -337,9 +337,6 @@ bool CrossModuleSerializationSetup::canSerialize(SILInstruction *inst, }); return canUse; } - if (auto *GAI = dyn_cast(inst)) { - return !GAI->getReferencedGlobal()->getName().startswith("globalinit_"); - } if (auto *MI = dyn_cast(inst)) { return !MI->getMember().isForeign; } From 29c7216c6c0db7fe556a9ce223a2acc447554a8a Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Wed, 26 Aug 2020 13:07:05 -0700 Subject: [PATCH 030/120] DependenciesScanner: include search path options in PCM building commands Unlike Swift modules, building Clang PCMs requires search path options like -F and -I to look for transitive header includes because in module maps, we can only find top-level headers. rdar://67589328 --- lib/ClangImporter/ClangModuleDependencyScanner.cpp | 14 ++++++++++++++ test/ScanDependencies/batch_module_scan.swift | 1 + test/ScanDependencies/module_deps.swift | 2 -- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index ed49b5213ead5..290dd69506ed2 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -254,6 +254,20 @@ void ClangImporter::recordModuleDependencies( swiftArgs.push_back("-module-name"); swiftArgs.push_back(clangModuleDep.ModuleName); + // Pass down search paths to the -emit-module action. + // Unlike building Swift modules, we need to include all search paths to + // the clang invocation to build PCMs because transitive headers can only + // be found via search paths. Passing these headers as explicit inputs can + // be quite challenging. + for (auto &path: Impl.SwiftContext.SearchPathOpts.ImportSearchPaths) { + swiftArgs.push_back("-I"); + swiftArgs.push_back(path); + } + for (auto &path: Impl.SwiftContext.SearchPathOpts.FrameworkSearchPaths) { + swiftArgs.push_back(path.IsSystem ? "-Fsystem": "-F"); + swiftArgs.push_back(path.Path); + } + // Swift frontend option for input file path (Foo.modulemap). swiftArgs.push_back(clangModuleDep.ClangModuleMapFile); // Module-level dependencies. diff --git a/test/ScanDependencies/batch_module_scan.swift b/test/ScanDependencies/batch_module_scan.swift index 08151fc4eb6f6..4f0c173f7a99f 100644 --- a/test/ScanDependencies/batch_module_scan.swift +++ b/test/ScanDependencies/batch_module_scan.swift @@ -28,6 +28,7 @@ // CHECK-PCM-NEXT: }, // CHECK-PCM-NEXT: { // CHECK-PCM-NEXT: "modulePath": "F.pcm", +// CHECK-PCM: "-I" // CHECK-SWIFT: { // CHECK-SWIFT-NEXT: "mainModuleName": "F", diff --git a/test/ScanDependencies/module_deps.swift b/test/ScanDependencies/module_deps.swift index 7e3a62507ed99..05b6f15c322fb 100644 --- a/test/ScanDependencies/module_deps.swift +++ b/test/ScanDependencies/module_deps.swift @@ -195,9 +195,7 @@ import SubE /// --------Clang module SwiftShims // CHECK-LABEL: "modulePath": "SwiftShims.pcm", -// CHECK-NO-SEARCH-PATHS-NOT: "-I" // CHECK-NO-SEARCH-PATHS-NOT: "-sdk" -// CHECK-NO-SEARCH-PATHS-NOT: "-F" // CHECK-NO-SEARCH-PATHS-NOT: "-prebuilt-module-cache-path" // Check make-style dependencies From 82e870b8a1f7af86fedefc969c304a2c945c984c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 26 Aug 2020 13:19:14 -0700 Subject: [PATCH 031/120] build: remove the Python 2 check The Swift build is now fully migrated to Python 3. Although it is backwards/forwards compatible, the build invokes everything with the Python3 interpreter, and the Python 2 interpreter should be unused. Remove the last reference to it. --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 844fcdb251f37..a84adb0ca4636 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -914,7 +914,6 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") endif() endif() -find_package(Python2 COMPONENTS Interpreter REQUIRED) find_package(Python3 COMPONENTS Interpreter REQUIRED) # From 362a25ae84ca4f56b4dfb810b6f9ecc438c0439a Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 15 Jan 2020 16:15:34 -0800 Subject: [PATCH 032/120] [AST] Support SILFunctionType/SILBlockStorageType in ClangTypeConverter. This translation is a pre-requisite to storing Clang types in SILFunctionType. --- lib/AST/ClangTypeConverter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index d4ccea512f069..2a08f78fb282e 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -576,12 +576,19 @@ clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type) { } clang::QualType ClangTypeConverter::visitSILFunctionType(SILFunctionType *type) { - llvm::report_fatal_error("Expected only AST types but found a SIL function."); + // We must've already computed it before if applicable. + return clang::QualType(type->getClangTypeInfo().getType(), 0); } clang::QualType ClangTypeConverter::visitSILBlockStorageType(SILBlockStorageType *type) { - llvm::report_fatal_error("Expected only AST types but found a SIL block."); + // We'll select (void)(^)(). This isn't correct for all blocks, but block + // storage type should only be converted for function signature lowering, + // where the parameter types do not matter. + auto &clangCtx = ClangASTContext; + auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy); + auto blockTy = clangCtx.getBlockPointerType(fnTy); + return clangCtx.getCanonicalType(blockTy); } clang::QualType From 78ea3de8e4e8c21528e7e60913fc466541c740ad Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 25 Aug 2020 23:29:53 -0700 Subject: [PATCH 033/120] [AST] Introduce a new type to represent a type hole in constraint system Instead of using `UnresolvedType` as a placeholder for a type hole, let's switch over to a dedicated "rich" `HoleType` which is capable of storing "originator" type - type variable or dependent member type which couldn't be resolved. This makes it easier for the solver to determine origins of a hole which helps to diagnose certain problems better. It also helps code completion to locate "expected type" of the context even when it couldn't be completely resolved. --- include/swift/AST/TypeNodes.def | 1 + include/swift/AST/Types.h | 38 ++++++++++++++++++++++++++--- lib/AST/ASTContext.cpp | 11 +++++++-- lib/AST/ASTDumper.cpp | 12 +++++++++ lib/AST/ASTMangler.cpp | 1 + lib/AST/ASTPrinter.cpp | 11 +++++++++ lib/AST/Type.cpp | 8 +++--- lib/AST/TypeWalker.cpp | 1 + lib/IRGen/IRGenDebugInfo.cpp | 1 + lib/Sema/CSSimplify.cpp | 2 ++ lib/Serialization/Serialization.cpp | 4 +++ 11 files changed, 81 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/TypeNodes.def b/include/swift/AST/TypeNodes.def index b04f82bb9a8de..f827339dd5064 100644 --- a/include/swift/AST/TypeNodes.def +++ b/include/swift/AST/TypeNodes.def @@ -89,6 +89,7 @@ TYPE(Error, Type) UNCHECKED_TYPE(Unresolved, Type) +UNCHECKED_TYPE(Hole, Type) ABSTRACT_TYPE(Builtin, Type) ABSTRACT_TYPE(AnyBuiltinInteger, BuiltinType) BUILTIN_TYPE(BuiltinInteger, AnyBuiltinIntegerType) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index e55c8bf7be3d5..d5fae0c50be9f 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -148,7 +148,10 @@ class RecursiveTypeProperties { /// This type contains an OpaqueTypeArchetype. HasOpaqueArchetype = 0x400, - Last_Property = HasOpaqueArchetype + /// This type constains a type hole. + HasTypeHole = 0x800, + + Last_Property = HasTypeHole }; enum { BitWidth = countBitsUsed(Property::Last_Property) }; @@ -203,6 +206,10 @@ class RecursiveTypeProperties { /// generic type? bool hasUnboundGeneric() const { return Bits & HasUnboundGeneric; } + /// Does a type with these properties structurally contain a + /// type hole? + bool hasTypeHole() const { return Bits & HasTypeHole; } + /// Returns the set of properties present in either set. friend RecursiveTypeProperties operator|(Property lhs, Property rhs) { return RecursiveTypeProperties(unsigned(lhs) | unsigned(rhs)); @@ -574,9 +581,7 @@ class alignas(1 << TypeAlignInBits) TypeBase { } /// Determine whether this type involves a hole. - bool hasHole() const { - return getRecursiveProperties().hasUnresolvedType(); - } + bool hasHole() const { return getRecursiveProperties().hasTypeHole(); } /// Determine whether the type involves a context-dependent archetype. bool hasArchetype() const { @@ -5728,6 +5733,31 @@ TypeVariableType : public TypeBase { }; DEFINE_EMPTY_CAN_TYPE_WRAPPER(TypeVariableType, Type) +/// HoleType - This represents a placeholder type for a type variable +/// or dependent member type that cannot be resolved to a concrete type +/// because the expression is ambiguous. This type is only used by the +/// constraint solver and transformed into UnresolvedType to be used in AST. +class HoleType : public TypeBase { + using OriginatorType = + llvm::PointerUnion; + + OriginatorType Originator; + + HoleType(ASTContext &C, OriginatorType originator, + RecursiveTypeProperties properties) + : TypeBase(TypeKind::Hole, &C, properties), Originator(originator) {} + +public: + static Type get(ASTContext &ctx, OriginatorType originatorType); + + OriginatorType getOriginatorType() const { return Originator; } + + static bool classof(const TypeBase *T) { + return T->getKind() == TypeKind::Hole; + } +}; +DEFINE_EMPTY_CAN_TYPE_WRAPPER(HoleType, Type) + inline bool TypeBase::isTypeVariableOrMember() { if (is()) return true; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index f3ee9197874a9..6c7e155e8b6d4 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2358,6 +2358,13 @@ Type ErrorType::get(Type originalType) { return entry = new (mem) ErrorType(ctx, originalType, properties); } +Type HoleType::get(ASTContext &ctx, OriginatorType originator) { + assert(originator); + RecursiveTypeProperties properties = RecursiveTypeProperties::HasTypeHole; + auto arena = getArena(properties); + return new (ctx, arena) HoleType(ctx, originator, properties); +} + BuiltinIntegerType *BuiltinIntegerType::get(BuiltinIntegerWidth BitWidth, const ASTContext &C) { assert(!BitWidth.isArbitraryWidth()); @@ -2957,7 +2964,7 @@ isFunctionTypeCanonical(ArrayRef params, static RecursiveTypeProperties getGenericFunctionRecursiveProperties(ArrayRef params, Type result) { - static_assert(RecursiveTypeProperties::BitWidth == 11, + static_assert(RecursiveTypeProperties::BitWidth == 12, "revisit this if you add new recursive type properties"); RecursiveTypeProperties properties; @@ -3513,7 +3520,7 @@ CanSILFunctionType SILFunctionType::get( void *mem = ctx.Allocate(bytes, alignof(SILFunctionType)); RecursiveTypeProperties properties; - static_assert(RecursiveTypeProperties::BitWidth == 11, + static_assert(RecursiveTypeProperties::BitWidth == 12, "revisit this if you add new recursive type properties"); for (auto ¶m : params) properties |= param.getInterfaceType()->getRecursiveProperties(); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index d8352e9c62e2c..95b603206fb35 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3496,6 +3496,18 @@ namespace { TRIVIAL_TYPE_PRINTER(Unresolved, unresolved) + void visitHoleType(HoleType *T, StringRef label) { + printCommon(label, "hole_type"); + auto originatorTy = T->getOriginatorType(); + if (auto *typeVar = originatorTy.dyn_cast()) { + printRec("type_variable", typeVar); + } else { + printRec("dependent_member_type", + originatorTy.get()); + } + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } + void visitBuiltinIntegerType(BuiltinIntegerType *T, StringRef label) { printCommon(label, "builtin_integer_type"); if (T->isFixedWidth()) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index be1c4af4e0ea1..a79b63409090c 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -915,6 +915,7 @@ void ASTMangler::appendType(Type type, const ValueDecl *forDecl) { TypeBase *tybase = type.getPointer(); switch (type->getKind()) { case TypeKind::TypeVariable: + case TypeKind::Hole: llvm_unreachable("mangling type variable"); case TypeKind::Module: diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index de48919eab2b3..d4b1c58b95cbc 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3813,6 +3813,17 @@ class TypePrinter : public TypeVisitor { Printer << "_"; } + void visitHoleType(HoleType *T) { + if (Options.PrintTypesForDebugging) { + Printer << "<getOriginatorType(); + visit(Type(reinterpret_cast(originatorTy.getOpaqueValue()))); + Printer << ">>"; + } else { + Printer << "<>"; + } + } + #ifdef ASTPRINTER_HANDLE_BUILTINTYPE #error "ASTPRINTER_HANDLE_BUILTINTYPE should not be defined?!" #endif diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index bc7d9b780b659..6cc2cc05a01fd 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -153,9 +153,7 @@ bool TypeBase::isAny() { return isEqual(getASTContext().TheAnyType); } -bool TypeBase::isHole() { - return isEqual(getASTContext().TheUnresolvedType); -} +bool TypeBase::isHole() { return getCanonicalType()->is(); } bool TypeBase::isAnyClassReferenceType() { return getCanonicalType().isAnyClassReferenceType(); @@ -221,6 +219,7 @@ bool CanType::isReferenceTypeImpl(CanType type, const GenericSignatureImpl *sig, case TypeKind::LValue: case TypeKind::InOut: case TypeKind::TypeVariable: + case TypeKind::Hole: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: case TypeKind::SILToken: @@ -1149,6 +1148,7 @@ CanType TypeBase::computeCanonicalType() { case TypeKind::Error: case TypeKind::Unresolved: case TypeKind::TypeVariable: + case TypeKind::Hole: llvm_unreachable("these types are always canonical"); #define SUGARED_TYPE(id, parent) \ @@ -4244,6 +4244,7 @@ case TypeKind::Id: case TypeKind::Error: case TypeKind::Unresolved: case TypeKind::TypeVariable: + case TypeKind::Hole: case TypeKind::GenericTypeParam: case TypeKind::SILToken: case TypeKind::Module: @@ -4983,6 +4984,7 @@ ReferenceCounting TypeBase::getReferenceCounting() { case TypeKind::LValue: case TypeKind::InOut: case TypeKind::TypeVariable: + case TypeKind::Hole: case TypeKind::BoundGenericEnum: case TypeKind::BoundGenericStruct: case TypeKind::SILToken: diff --git a/lib/AST/TypeWalker.cpp b/lib/AST/TypeWalker.cpp index f5be4e10b5223..f95bdb01366f9 100644 --- a/lib/AST/TypeWalker.cpp +++ b/lib/AST/TypeWalker.cpp @@ -34,6 +34,7 @@ class Traversal : public TypeVisitor bool visitErrorType(ErrorType *ty) { return false; } bool visitUnresolvedType(UnresolvedType *ty) { return false; } + bool visitHoleType(HoleType *ty) { return false; } bool visitBuiltinType(BuiltinType *ty) { return false; } bool visitTypeAliasType(TypeAliasType *ty) { if (auto parent = ty->getParent()) diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index e1cd39d903dde..65387d02719d7 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -1571,6 +1571,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { case TypeKind::Unresolved: case TypeKind::LValue: case TypeKind::TypeVariable: + case TypeKind::Hole: case TypeKind::Module: case TypeKind::SILBlockStorage: case TypeKind::SILBox: diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 1608f19e49155..31921f7d626ab 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4685,6 +4685,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case TypeKind::Error: case TypeKind::Unresolved: + case TypeKind::Hole: return getTypeMatchFailure(locator); case TypeKind::GenericTypeParam: @@ -5366,6 +5367,7 @@ ConstraintSystem::simplifyConstructionConstraint( case TypeKind::Unresolved: case TypeKind::Error: + case TypeKind::Hole: return SolutionKind::Error; case TypeKind::GenericFunction: diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 20179b2876c19..5be326c2a7e5e 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3925,6 +3925,10 @@ class Serializer::TypeSerializer : public TypeVisitor { llvm_unreachable("should not serialize an invalid type"); } + void visitHoleType(const HoleType *) { + llvm_unreachable("should not serialize an invalid type"); + } + void visitModuleType(const ModuleType *) { llvm_unreachable("modules are currently not first-class values"); } From 48c976768092174f5925a6fa5fa372ab84457f80 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 26 Aug 2020 18:42:05 -0700 Subject: [PATCH 034/120] [CSApply] Make sure that hole type are not propagated to AST --- lib/Sema/CSApply.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 276974ed4ab42..b4ec32888c67c 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7662,6 +7662,8 @@ namespace { // "Mismatched types!"); assert(!exprType->hasTypeVariable() && "Should not write type variable into expression!"); + assert(!exprType->hasHole() && + "Should not write type holes into expression!"); expr->setType(exprType); if (auto kp = dyn_cast(expr)) { From d039107bdba7319f2c72169c35a06dce89020f67 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 26 Aug 2020 18:43:10 -0700 Subject: [PATCH 035/120] [ConstraintSystem] Replace use of `UnresolvedType` with specialized `HoleType` --- lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSSimplify.cpp | 18 +++++++++++++++++- lib/Sema/ConstraintSystem.cpp | 31 ++++++++++++++++++++----------- lib/Sema/ConstraintSystem.h | 5 +++-- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 8919c9e352f4f..807a86b01a85a 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -335,7 +335,7 @@ void ConstraintSystem::PotentialBindings::finalize( if (locator->isLastElement()) PotentiallyIncomplete = true; - addPotentialBinding(PotentialBinding::forHole(cs.getASTContext(), locator)); + addPotentialBinding(PotentialBinding::forHole(TypeVar, locator)); } // Let's always consider `Any` to be a last resort binding because diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 31921f7d626ab..e39a8506d361e 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4434,6 +4434,12 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::FunctionBuilderBodyResult: { + // If result type of the body couldn't be determined + // there is going to be other fix available to diagnose + // the underlying issue. + if (lhs->isHole()) + return true; + conversionsOrFixes.push_back(ContextualMismatch::create( *this, lhs, rhs, getConstraintLocator(locator))); break; @@ -4685,9 +4691,19 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case TypeKind::Error: case TypeKind::Unresolved: - case TypeKind::Hole: return getTypeMatchFailure(locator); + case TypeKind::Hole: { + // If it's allowed to attempt fixes, let's delegate + // decision to `repairFailures`, since depending on + // locator we might either ignore such a mismatch, + // or record a specialized fix. + if (shouldAttemptFixes()) + break; + + return getTypeMatchFailure(locator); + } + case TypeKind::GenericTypeParam: llvm_unreachable("unmapped dependent type in type checker"); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1214740c8d1d5..be0fc64926e92 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -2617,11 +2617,13 @@ Type ConstraintSystem::simplifyTypeImpl(Type type, // there will be a missing conformance fix applied in diagnostic mode, // so the concrete dependent member type is considered a "hole" in // order to continue solving. + auto memberTy = DependentMemberType::get(lookupBaseType, assocType); if (shouldAttemptFixes() && - getPhase() == ConstraintSystemPhase::Solving) - return getASTContext().TheUnresolvedType; + getPhase() == ConstraintSystemPhase::Solving) { + return HoleType::get(getASTContext(), memberTy); + } - return DependentMemberType::get(lookupBaseType, assocType); + return memberTy; } auto subs = SubstitutionMap::getProtocolSubstitutions( @@ -2653,16 +2655,23 @@ Type ConstraintSystem::simplifyType(Type type) const { } Type Solution::simplifyType(Type type) const { - if (!type->hasTypeVariable()) + if (!(type->hasTypeVariable() || type->hasHole())) return type; // Map type variables to fixed types from bindings. - return getConstraintSystem().simplifyTypeImpl(type, - [&](TypeVariableType *tvt) -> Type { - auto known = typeBindings.find(tvt); - assert(known != typeBindings.end()); - return known->second; - }); + auto &cs = getConstraintSystem(); + auto resolvedType = cs.simplifyTypeImpl( + type, [&](TypeVariableType *tvt) -> Type { return getFixedType(tvt); }); + + // Holes shouldn't be reachable through a solution, they are only + // useful to determine what went wrong exactly. + if (resolvedType->hasHole()) { + return resolvedType.transform([&](Type type) { + return type->isHole() ? Type(cs.getASTContext().TheUnresolvedType) : type; + }); + } + + return resolvedType; } size_t Solution::getTotalMemory() const { @@ -4646,7 +4655,7 @@ SolutionApplicationTarget SolutionApplicationTarget::forInitialization( // Determine the contextual type for the initialization. TypeLoc contextualType; if (!(isa(pattern) && !pattern->isImplicit()) && - patternType && !patternType->isHole()) { + patternType && !patternType->is()) { contextualType = TypeLoc::withoutLoc(patternType); // Only provide a TypeLoc if it makes sense to allow diagnostics. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index bc95ed45c0b55..18579bfc857a8 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -4564,9 +4564,10 @@ class ConstraintSystem { return {type, kind, BindingSource}; } - static PotentialBinding forHole(ASTContext &ctx, + static PotentialBinding forHole(TypeVariableType *typeVar, ConstraintLocator *locator) { - return {ctx.TheUnresolvedType, AllowedBindingKind::Exact, + return {HoleType::get(typeVar->getASTContext(), typeVar), + AllowedBindingKind::Exact, /*source=*/locator}; } }; From b4d3237beab389b0686e8727dcdf185fee5636d6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 26 Aug 2020 18:45:17 -0700 Subject: [PATCH 036/120] [CSDiagnostics] Adjust diagnostics to account that type holes in solution are `UnresolvedType` --- lib/Sema/CSDiagnostics.cpp | 4 ++-- lib/Sema/CSDiagnostics.h | 30 +++++++++++++++++------------ test/Constraints/rdar44770297.swift | 6 ++---- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ec52d0ec612ad..25b54e13e7538 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -5057,7 +5057,7 @@ bool CollectionElementContextualFailure::diagnoseAsError() { // holes present in the contextual type. if (FailureDiagnostic::getContextualTypePurpose(getAnchor()) == ContextualTypePurpose::CTP_ForEachStmt && - contextualType->hasHole()) { + contextualType->hasUnresolvedType()) { diagnostic.emplace(emitDiagnostic( (contextualType->is() && !eltType->is()) ? diag::cannot_match_expr_tuple_pattern_with_nontuple_value @@ -6315,7 +6315,7 @@ bool UnableToInferClosureParameterType::diagnoseAsError() { if (parentExpr) { // Missing or invalid member reference in call. if (auto *AE = dyn_cast(parentExpr)) { - if (getType(AE->getFn())->isHole()) + if (getType(AE->getFn())->is()) return false; } diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index e1c925322d495..956c91736dd0e 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -94,20 +94,26 @@ class FailureDiagnostic { /// Resolve type variables present in the raw type, if any. Type resolveType(Type rawType, bool reconstituteSugar = false, bool wantRValue = true) const { - if (!rawType->hasTypeVariable()) { - if (reconstituteSugar) - rawType = rawType->reconstituteSugar(/*recursive*/ true); - return wantRValue ? rawType->getRValueType() : rawType; + auto &cs = getConstraintSystem(); + + if (rawType->hasTypeVariable() || rawType->hasHole()) { + rawType = rawType.transform([&](Type type) { + if (auto *typeVar = type->getAs()) { + auto resolvedType = S.simplifyType(typeVar); + Type GP = typeVar->getImpl().getGenericParameter(); + return resolvedType->is() && GP + ? GP + : resolvedType; + } + + return type->isHole() ? Type(cs.getASTContext().TheUnresolvedType) + : type; + }); } - auto &cs = getConstraintSystem(); - return cs.simplifyTypeImpl(rawType, [&](TypeVariableType *typeVar) -> Type { - auto fixed = S.simplifyType(typeVar); - auto *genericParam = typeVar->getImpl().getGenericParameter(); - if (fixed->isHole() && genericParam) - return genericParam; - return resolveType(fixed, reconstituteSugar, wantRValue); - }); + if (reconstituteSugar) + rawType = rawType->reconstituteSugar(/*recursive*/ true); + return wantRValue ? rawType->getRValueType() : rawType; } template diff --git a/test/Constraints/rdar44770297.swift b/test/Constraints/rdar44770297.swift index 8d0b128b9e136..9853dd1ca2f53 100644 --- a/test/Constraints/rdar44770297.swift +++ b/test/Constraints/rdar44770297.swift @@ -4,10 +4,8 @@ protocol P { associatedtype A } -func foo(_: () throws -> T) -> T.A? { +func foo(_: () throws -> T) -> T.A? { // expected-note {{where 'T' = 'Never'}} fatalError() } -let _ = foo() {fatalError()} & nil // expected-error {{value of optional type 'Never.A?' must be unwrapped to a value of type 'Never.A'}} -// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} -// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} +let _ = foo() {fatalError()} & nil // expected-error {{global function 'foo' requires that 'Never' conform to 'P'}} From d287963c4d175dfdad824d2628df525f87295bea Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 26 Aug 2020 19:27:21 -0700 Subject: [PATCH 037/120] [sil-mode] Change viewcfg integration to specify dot as a renderer. --- utils/sil-mode.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/sil-mode.el b/utils/sil-mode.el index fe29c99d9b58b..59cc44ba0c4c7 100644 --- a/utils/sil-mode.el +++ b/utils/sil-mode.el @@ -259,6 +259,7 @@ ;; *NOTE* viewcfg must be in the $PATH and .dot files should be associated with ;; the graphviz app. (defvar sil-mode-viewcfg-program-name "viewcfg") +(defvar sil-mode-viewcfg-renderer "dot") (defvar sil-mode-viewcfg-buffer-name "*viewcfg*") (defcustom sil-mode-viewcfg-command-default "viewcfg" @@ -281,7 +282,8 @@ (process-connection-type nil)) (let ((p (start-process sil-mode-viewcfg-program-name sil-mode-viewcfg-buffer-name - sil-mode-viewcfg-command))) + sil-mode-viewcfg-command + (concat "--renderer=" sil-mode-viewcfg-renderer)))) (process-send-region p brace-start brace-end) (process-send-eof p))))) From 5b5e30b6d7877c58e260839c0daf878fd1506f54 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:01:59 -0400 Subject: [PATCH 038/120] Implement implicit member chains --- include/swift/AST/DiagnosticsSema.def | 2 + lib/Sema/CSApply.cpp | 23 +- lib/Sema/CSDiagnostics.cpp | 45 ++-- lib/Sema/CSGen.cpp | 115 ++++++--- lib/Sema/CSSimplify.cpp | 84 ++++--- lib/Sema/CSSolver.cpp | 13 ++ lib/Sema/ConstraintSystem.cpp | 1 + lib/Sema/ConstraintSystem.h | 74 ++++++ test/Constraints/construction.swift | 5 +- test/Constraints/diagnostics.swift | 2 +- test/expr/delayed-ident/member_chains.swift | 218 ++++++++++++++++++ test/expr/delayed-ident/nested_type.swift | 19 ++ test/expr/delayed-ident/static_var.swift | 3 +- .../postfix/dot/optional_context_member.swift | 5 +- 14 files changed, 509 insertions(+), 100 deletions(-) create mode 100644 test/expr/delayed-ident/member_chains.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index beaf7b6ea3dd5..5a0a494f535e6 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -112,6 +112,8 @@ ERROR(expected_argument_in_contextual_member,none, "member %0 expects argument of type %1", (DeclName, Type)) ERROR(expected_parens_in_contextual_member,none, "member %0 is a function; did you mean to call it?", (DeclName)) +ERROR(expected_parens_in_contextual_member_type,none, + "member %0 is a function that produces expected type %1; did you mean to call it?", (DeclName, Type)) ERROR(expected_result_in_contextual_member,none, "member %0 in %2 produces result of type %1, but context expects %2", diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 0add614158d91..1e1775e464dd1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2753,17 +2753,16 @@ namespace { } Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { - // Dig out the type of the base, which will be the result type of this - // expression. If constraint solving resolved this to an UnresolvedType, + // If constraint solving resolved the base type to an UnresolvedType, // then we're in an ambiguity tolerant mode used for diagnostic // generation. Just leave this as an unresolved member reference. - Type resultTy = simplifyType(cs.getType(expr)); - if (resultTy->hasUnresolvedType()) { - cs.setType(expr, resultTy); + Type baseTy = simplifyType(solution.getUnresolvedMemberBaseType(expr)); + + if (baseTy->hasUnresolvedType()) { + cs.setType(expr, baseTy); return expr; } - Type baseTy = resultTy->getRValueType(); auto &ctx = cs.getASTContext(); // Find the selected member. @@ -2821,7 +2820,8 @@ namespace { diagnoseAmbiguousNominalMember(baseTy, result); } - return coerceToType(result, resultTy, cs.getConstraintLocator(expr)); + return coerceToType(result, simplifyType(cs.getType(expr)), + cs.getConstraintLocator(expr)); } /// Diagnose if the base type is optional, we're referring to a nominal @@ -8449,6 +8449,15 @@ void Solution::setExprTypes(Expr *expr) const { expr->walk(SET); } +Type Solution::getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) const { + auto result = unresolvedMemberBaseTypes.find(expr); + if (result != unresolvedMemberBaseTypes.end()) + return result->second; + + auto &cs = getConstraintSystem(); + return cs.getUnresolvedMemberBaseType(expr); +} + /// MARK: SolutionResult implementation. SolutionResult SolutionResult::forSolved(Solution &&solution) { diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 4bef05bd9c4eb..5c04d1aefce5a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1200,14 +1200,6 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { auto *anchor = castToExpr(getAnchor()); - // If this is an unresolved member expr e.g. `.foo` its - // base type is going to be the same as result type minus - // r-value adjustment because base could be an l-value type. - // We want to fix both cases by only diagnose one of them, - // otherwise this is just going to result in a duplcate diagnostic. - if (getLocator()->isLastElement()) - return false; - if (auto assignExpr = dyn_cast(anchor)) anchor = assignExpr->getSrc(); @@ -2137,10 +2129,15 @@ bool ContextualFailure::diagnoseAsError() { return false; } - case ConstraintLocator::RValueAdjustment: { + case ConstraintLocator::Member: + case ConstraintLocator::FunctionResult: + case ConstraintLocator::UnresolvedMember: { auto &solution = getSolution(); - auto overload = getOverloadChoiceIfAvailable( - getConstraintLocator(anchor, ConstraintLocator::UnresolvedMember)); + auto locator = getConstraintLocator(anchor, + isExpr(anchor) + ? ConstraintLocator::UnresolvedMember + : ConstraintLocator::Member); + auto overload = getOverloadChoiceIfAvailable(locator); if (!(overload && overload->choice.isDecl())) return false; @@ -2175,13 +2172,22 @@ bool ContextualFailure::diagnoseAsError() { }); if (numMissingArgs == 0 || numMissingArgs > 1) { - auto diagnostic = emitDiagnostic( - diag::expected_parens_in_contextual_member, choice->getName()); - - // If there are no parameters we can suggest a fix-it - // to form an explicit call. - if (numMissingArgs == 0) - diagnostic.fixItInsertAfter(getSourceRange().End, "()"); + auto applyFixIt = [&](InFlightDiagnostic &diagnostic) { + // If there are no parameters we can suggest a fix-it + // to form an explicit call. + if (numMissingArgs == 0) + diagnostic.fixItInsertAfter(getSourceRange().End, "()"); + }; + if (fnType->getResult()->isEqual(toType)) { + auto diag = emitDiagnostic( + diag::expected_parens_in_contextual_member_type, + choice->getName(), fnType->getResult()); + applyFixIt(diag); + } else { + auto diag = emitDiagnostic(diag::expected_parens_in_contextual_member, + choice->getName()); + applyFixIt(diag); + } } else { emitDiagnostic(diag::expected_argument_in_contextual_member, choice->getName(), params.front().getPlainType()); @@ -2400,6 +2406,9 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { if (getLocator()->isLastElement()) return false; + if (getLocator()->isLastElement()) + return false; + auto *srcFT = getFromType()->getAs(); if (!srcFT || !(srcFT->getParams().empty() || diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index d54105b2c721a..91f8ad9bf8b77 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -848,6 +848,35 @@ namespace { return tv; } + Type addUnresolvedMemberChainConstraints(Expr *expr, Type resultTy, + ConstraintLocator *resultLocator, + unsigned additionalOptions = 0) { + // If this is a member chain hanging off of an UnresolvedMemberExpr, + // and we're at the last element of the chain, then the contextual type + // (represented with a new type variable) must equal the base type. + if (CS.isMemberChainTail(expr)) { + auto *chainBaseExpr = CS.getMemberChainBase(expr); + if (auto *UME = dyn_cast(chainBaseExpr)) { + // Create a new type variable representing the result of the chain. + auto chainResultTy = CS.createTypeVariable(resultLocator, + additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape); + auto chainBaseTy = CS.getUnresolvedMemberBaseType(UME); + + // The result of this element of the chain must be convertible to the + // contextual type, and the contextual type must be equal to the base. + CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, + resultLocator); + CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, + CS.getConstraintLocator(UME, ConstraintLocator::MemberRefBase)); + + return chainResultTy; + } + } + + // Otherwise, just use the proposed result type. + return resultTy; + } + /// Add constraints for a subscript operation. Type addSubscriptConstraints( Expr *anchor, Type baseTy, Expr *index, @@ -1431,15 +1460,17 @@ namespace { expr->getArgument() ? FunctionRefKind::DoubleApply : FunctionRefKind::Compound; - auto memberLocator - = CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember); + auto memberLocator = CS.getConstraintLocator(expr, + ConstraintLocator::UnresolvedMember); // Since base type in this case is completely dependent on context it // should be marked as a potential hole. - auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape | - TVO_CanBindToHole); - auto memberTy = CS.createTypeVariable( - memberLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); + auto baseTy = CS.createTypeVariable(baseLocator, + TVO_CanBindToHole | TVO_CanBindToNoEscape); + CS.setUnresolvedMemberBaseType(expr, baseTy); + + auto memberTy = CS.createTypeVariable(memberLocator, + TVO_CanBindToLValue | TVO_CanBindToNoEscape); // An unresolved member expression '.member' is modeled as a value member // constraint @@ -1455,14 +1486,11 @@ namespace { // If there is an argument, apply it. if (auto arg = expr->getArgument()) { - // The result type of the function must be convertible to the base type. - // TODO: we definitely want this to include ImplicitlyUnwrappedOptional; - // does it need to include everything else in the world? - auto outputTy = CS.createTypeVariable( - CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), - TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::Conversion, outputTy, baseTy, - CS.getConstraintLocator(expr, ConstraintLocator::RValueAdjustment)); + // Create a new type variable for the result of the function. + auto outputLocator = CS.getConstraintLocator(expr, + ConstraintLocator::FunctionResult); + auto outputTy = CS.createTypeVariable(outputLocator, + TVO_CanBindToNoEscape); // The function/enum case must be callable with the given argument. @@ -1481,23 +1509,14 @@ namespace { CS.getConstraintLocator(expr), {expr->getArgumentLabels(), expr->getUnlabeledTrailingClosureIndex()}); - return baseTy; + return addUnresolvedMemberChainConstraints(expr, outputTy, + outputLocator); } - - // Otherwise, the member needs to be convertible to the base type. - CS.addConstraint(ConstraintKind::Conversion, memberTy, baseTy, - CS.getConstraintLocator(expr, ConstraintLocator::RValueAdjustment)); - // The member type also needs to be convertible to the context type, which - // preserves lvalue-ness. - auto resultTy = CS.createTypeVariable(memberLocator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::Conversion, memberTy, resultTy, - memberLocator); - CS.addConstraint(ConstraintKind::Equal, resultTy, baseTy, - memberLocator); - return resultTy; + // Otherwise, add the usual constraints for an element of an unresolved + // member chain. + return addUnresolvedMemberChainConstraints(expr, memberTy, memberLocator, + TVO_CanBindToLValue); } Type visitUnresolvedDotExpr(UnresolvedDotExpr *expr) { @@ -1561,9 +1580,14 @@ namespace { return methodTy; } - return addMemberRefConstraints(expr, expr->getBase(), expr->getName(), - expr->getFunctionRefKind(), - expr->getOuterAlternatives()); + auto resultTy = addMemberRefConstraints(expr, expr->getBase(), + expr->getName(), + expr->getFunctionRefKind(), + expr->getOuterAlternatives()); + + return addUnresolvedMemberChainConstraints(expr, resultTy, + CS.getConstraintLocator(expr, ConstraintLocator::Member), + TVO_CanBindToLValue); } Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) { @@ -1722,10 +1746,15 @@ namespace { if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal)) return nullptr; - return addSubscriptConstraints(expr, CS.getType(base), - expr->getIndex(), - decl, expr->getArgumentLabels(), - expr->getUnlabeledTrailingClosureIndex()); + auto resultTy = addSubscriptConstraints(expr, CS.getType(base), + expr->getIndex(), + decl, expr->getArgumentLabels(), + expr->getUnlabeledTrailingClosureIndex()); + auto resultLocator = CS.getConstraintLocator(expr, + ConstraintLocator::FunctionResult); + + return addUnresolvedMemberChainConstraints(expr, resultTy, resultLocator, + TVO_CanBindToLValue); } Type visitArrayExpr(ArrayExpr *expr) { @@ -2908,6 +2937,15 @@ namespace { resultType = fixedType; } + // If the ApplyExpr is a CallExpr, add chain constraints as necessary. + if (isa(expr)) { + auto resultLocator = CS.getConstraintLocator(expr, + ConstraintLocator::FunctionResult); + + return addUnresolvedMemberChainConstraints(expr, resultType, + resultLocator); + } + return resultType; } @@ -3218,7 +3256,8 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return objectTy; + return addUnresolvedMemberChainConstraints(expr, objectTy, locator, + TVO_CanBindToLValue); } Type visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) { @@ -3253,7 +3292,7 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return objectTy; + return addUnresolvedMemberChainConstraints(expr, objectTy, locator); } Type visitOpenExistentialExpr(OpenExistentialExpr *expr) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 5ba9345c7c555..274fc1055697d 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4135,6 +4135,20 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::FunctionResult: { + // FIXME: Is this necessary? + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, + locator)) + break; + + // If this mismatch occurs at the tail of an unresolved member chain, + // there's a contextual mismatch. + if (isUnresolvedMemberChainTail(anchor)) { + auto *fix = IgnoreContextualType::create(*this, lhs, rhs, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + break; + } + auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); // If this is a mismatch between contextual type and (trailing) // closure with explicitly specified result type let's record it @@ -4181,6 +4195,16 @@ bool ConstraintSystem::repairFailures( repairByInsertingExplicitCall(lhs, rhs)) return true; } + + // If this mismatch occurs at the tail of an unresolved member chain, + // there's a contextual mismatch. + if (isUnresolvedMemberChainTail(anchor)) { + auto *fix = IgnoreContextualType::create(*this, lhs, rhs, + getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + break; + } + break; } @@ -4306,12 +4330,7 @@ bool ConstraintSystem::repairFailures( break; } - // Unresolved member type mismatches are handled when - // r-value adjustment constraint fails. - case ConstraintLocator::UnresolvedMember: - return true; - - case ConstraintLocator::RValueAdjustment: { + case ConstraintLocator::UnresolvedMember: { if (!isExpr(anchor)) break; @@ -4319,9 +4338,7 @@ bool ConstraintSystem::repairFailures( locator)) break; - // r-value adjustment is used to connect base type of - // unresolved member to its output type, if there is - // a type mismatch here it's contextual e.g. + // If there is a type mismatch here it's contextual e.g., // `let x: E = .foo(42)`, where `.foo` is a member of `E` // but produces an incorrect type. auto *fix = IgnoreContextualType::create(*this, lhs, rhs, @@ -6779,27 +6796,32 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, } } // Initializer reference which requires contextual base type e.g. - // `.init(...)`. + // `.init(...)`. Could also be a nested type or typealias being constructed + // via implicit member syntax, e.g., `let _: Base = .Nested()` where + // `Base.Nested: Base`. } else if (auto *UME = getAsExpr(anchor)) { - // We need to find type variable which represents contextual base. - auto *baseLocator = cs.getConstraintLocator( - UME, locatorEndsWith(locator, ConstraintLocator::ConstructorMember) - ? ConstraintLocator::UnresolvedMember - : ConstraintLocator::MemberRefBase); - - // FIXME: Type variables responsible for contextual base could be cached - // in the constraint system to speed up lookup. - auto result = llvm::find_if( - cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { - return typeVar->getImpl().getLocator() == baseLocator; - }); + // If we're accessing a nested type to perform the construction implicitly, + // then the type we're constructing may not actually be the base of the + // UnresolvedMemberExpr--instead, it will be the type of the nested type + // member. + if (locatorEndsWith(locator, ConstraintLocator::ConstructorMember)) { + auto *baseLocator = cs.getConstraintLocator(UME, + ConstraintLocator::UnresolvedMember); + auto result = llvm::find_if( + cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { + return typeVar->getImpl().getLocator() == baseLocator; + }); + assert(result != cs.getTypeVariables().end()); + baseType = cs.simplifyType(*result); - assert(result != cs.getTypeVariables().end()); - baseType = cs.simplifyType(*result)->getRValueType(); - // Constraint for member base is formed as '$T.Type[.(anchor)) { // Key path can't refer to initializers e.g. `\Type.init` return AllowInvalidRefInKeyPath::forRef(cs, init, locator); @@ -10061,6 +10083,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( impact = 10; } + if (locator->isLastElement()) { + // If this is a contextual failure for an unresolved member, then increase + // the impact to attempt other fixes first and avoid ambiguity. + impact = 5; + } + if (recordFix(fix, impact)) return SolutionKind::Error; diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 8f0afc7b18b81..ba10f3c128c73 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -176,6 +176,10 @@ Solution ConstraintSystem::finalize() { solution.nodeTypes.insert(nodeType); } + for (auto &baseType : UnresolvedMemberBaseTypes) { + solution.unresolvedMemberBaseTypes.insert(baseType); + } + // Remember contextual types. solution.contextualTypes.assign( contextualTypes.begin(), contextualTypes.end()); @@ -251,6 +255,11 @@ void ConstraintSystem::applySolution(const Solution &solution) { setType(nodeType.first, nodeType.second); } + // Add the unresolved member base types back. + for (auto &baseType : solution.unresolvedMemberBaseTypes) { + setUnresolvedMemberBaseType(baseType.first, baseType.second); + } + // Add the contextual types. for (const auto &contextualType : solution.contextualTypes) { if (!getContextualTypeInfo(contextualType.first)) { @@ -475,6 +484,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numOpenedExistentialTypes = cs.OpenedExistentialTypes.size(); numDefaultedConstraints = cs.DefaultedConstraints.size(); numAddedNodeTypes = cs.addedNodeTypes.size(); + numUnresolvedMemberBaseTypes = cs.UnresolvedMemberBaseTypes.size(); numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); @@ -553,6 +563,9 @@ ConstraintSystem::SolverScope::~SolverScope() { } truncate(cs.addedNodeTypes, numAddedNodeTypes); + // Remove any unresolved member base types. + truncate(cs.UnresolvedMemberBaseTypes, numUnresolvedMemberBaseTypes); + // Remove any conformances checked along the current path. truncate(cs.CheckedConformances, numCheckedConformances); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 1214740c8d1d5..0f518e4695ab8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3679,6 +3679,7 @@ void constraints::simplifyLocator(ASTNode &anchor, } case ConstraintLocator::ApplyFunction: + case ConstraintLocator::FunctionResult: // Extract application function. if (auto applyExpr = getAsExpr(anchor)) { anchor = applyExpr->getFn(); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index bc95ed45c0b55..cb2f16f26696b 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1080,6 +1080,9 @@ class Solution { /// The node -> type mappings introduced by this solution. llvm::DenseMap nodeTypes; + /// The unresolved member base types introduced by this solution. + llvm::MapVector unresolvedMemberBaseTypes; + /// Contextual types introduced by this solution. std::vector> contextualTypes; @@ -1195,6 +1198,9 @@ class Solution { /// "resolved" concrete type. Type getResolvedType(ASTNode node) const; + /// Retrieve the type of a particular \c UnresolvedMemberExpr's base. + Type getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) const; + /// Resolve type variables present in the raw type, using generic parameter /// types where possible. Type resolveInterfaceType(Type type) const; @@ -2050,6 +2056,9 @@ class ConstraintSystem { llvm::DenseMap OpenedParameterTypes; + /// Maps \c UnresolvedMemberExprs to their (implicit) base types. + llvm::MapVector UnresolvedMemberBaseTypes; + /// The set of constraint restrictions used to reach the /// current constraint system. /// @@ -2571,6 +2580,8 @@ class ConstraintSystem { unsigned numAddedNodeTypes; + unsigned numUnresolvedMemberBaseTypes; + unsigned numCheckedConformances; unsigned numDisabledConstraints; @@ -2873,6 +2884,17 @@ class ConstraintSystem { return CTP_Unused; } + void setUnresolvedMemberBaseType(UnresolvedMemberExpr *expr, Type type) { + assert(expr && "Expected non-null expression"); + assert(type && "Expected non-null type"); + UnresolvedMemberBaseTypes[expr] = type; + } + + Type getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) { + assert(expr && "Expected non-null expression"); + return UnresolvedMemberBaseTypes.find(expr)->second; + } + void setSolutionApplicationTarget( SolutionApplicationTargetsKey key, SolutionApplicationTarget target) { assert(solutionApplicationTargets.count(key) == 0 && @@ -3022,6 +3044,58 @@ class ConstraintSystem { bool isDeclUnavailable(const Decl *D, ConstraintLocator *locator = nullptr) const; + /// Find the next element in a chain of members. If this expression is (or + /// could be) the base of such a chain, this will return \c nullptr. + Expr *getMemberChainSubExpr(Expr *expr) { + assert(expr && "isMemberChainSubExpr called with null expr!"); + if (auto *UDE = dyn_cast(expr)) { + return UDE->getBase(); + } else if (auto *CE = dyn_cast(expr)) { + return CE->getFn(); + } else if (auto *BOE = dyn_cast(expr)) { + return BOE->getSubExpr(); + } else if (auto *FVE = dyn_cast(expr)) { + return FVE->getSubExpr(); + } else if (auto *SE = dyn_cast(expr)) { + return SE->getBase(); + } else { + return nullptr; + } + } + + /// Returns the base of a chain of member accesses/method calls. Most + /// expressions do not participate in member chains, and just return \c this. + Expr *getMemberChainBase(Expr *expr) { + if (auto *subExpr = getMemberChainSubExpr(expr)) + return getMemberChainBase(subExpr); + else + return expr; + } + + /// Whether this expression is a member of a member chain. + bool isMemberChainMember(Expr *expr) { + return getMemberChainSubExpr(expr) != nullptr; + } + /// Whether this expression sits at the end of a chain of member accesses. + bool isMemberChainTail(Expr *expr) { + assert(expr && "isMemberChainTail called with null expr!"); + // If this expression's parent is not itself part of a chain (or, this expr + // has no parent expr), this must be the tail of the chain. + Expr *parent = getParentExpr(expr); + + return parent == nullptr || !isMemberChainMember(parent); + } + + /// Whether this node sits at the end of a member chain that hangs off an + /// \c UnresolvedMemberExpr at the base. + bool isUnresolvedMemberChainTail(ASTNode node) { + if (auto *expr = getAsExpr(node)) + return isMemberChainTail(expr) + && isa(getMemberChainBase(expr)); + else + return false; + } + public: /// Whether we should attempt to fix problems. diff --git a/test/Constraints/construction.swift b/test/Constraints/construction.swift index 82f660ed6a7bb..048667c4e129b 100644 --- a/test/Constraints/construction.swift +++ b/test/Constraints/construction.swift @@ -22,7 +22,7 @@ enum Z { init(_ x: Int, _ y: Int) { self = .point(x, y) } } -enum Optional { // expected-note {{'T' declared as parameter to type 'Optional'}} +enum Optional { case none case value(T) @@ -59,8 +59,7 @@ acceptString("\(hello), \(world) #\(i)!") Optional(1) // expected-warning{{unused}} Optional(1) // expected-warning{{unused}} _ = .none as Optional -Optional(.none) // expected-error{{generic parameter 'T' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{9-9=}} -// expected-error@-1 {{cannot infer contextual base in reference to member 'none'}} +Optional(.none) // expected-error {{cannot infer contextual base in reference to member 'none'}} // Interpolation _ = "\(hello), \(world) #\(i)!" diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index bff33fe7caa73..fac6d8b343a10 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -423,7 +423,7 @@ let _ : Color = .rainbow(42) // expected-error {{argument passed to call that t let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of type '(Double, Float)' to specified type '(Int, Float)'}} -let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}} +let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function that produces expected type 'Color'; did you mean to call it?}} {{25-25=()}} let _: Color = .overload(a : 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} let _: Color = .overload(1.0) // expected-error {{no exact matches in call to static method 'overload'}} diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift new file mode 100644 index 0000000000000..c8f468ace2e9f --- /dev/null +++ b/test/expr/delayed-ident/member_chains.swift @@ -0,0 +1,218 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +struct ImplicitMembers: Equatable { + struct Other { + var implicit: ImplicitMembers { ImplicitMembers() } + } + + static var other = Other() + static func createOther() -> Other { + Other() + } + var anotherOther: Other { Other() } + func getAnotherOther() -> Other { + Other() + } + + static var implicit = ImplicitMembers() + static func createImplicit() -> ImplicitMembers { + ImplicitMembers() + } + + static var optional: ImplicitMembers? = ImplicitMembers() + static func createOptional() -> ImplicitMembers? { + ImplicitMembers() + } + static var superOptional: ImplicitMembers??? = ImplicitMembers() + + var another: ImplicitMembers { ImplicitMembers() } + + func getAnother() -> ImplicitMembers { + ImplicitMembers() + } + + func getAnother(arg: Int) -> ImplicitMembers { + ImplicitMembers() + } + + var anotherOptional: ImplicitMembers? { ImplicitMembers() } + + func getAnotherOptional() -> ImplicitMembers? { + ImplicitMembers() + } + + func getAnotherOptional(arg: Int) -> ImplicitMembers? { + ImplicitMembers() + } + + subscript(arg: Void) -> ImplicitMembers { ImplicitMembers() } + subscript(optional arg: Void) -> ImplicitMembers? { ImplicitMembers() } + subscript(func arg: Void) -> (() -> ImplicitMembers) { { ImplicitMembers() } } + subscript(funcOptional arg: Void) -> (() -> ImplicitMembers?) { { ImplicitMembers() } } + subscript(optionalFunc arg: Void) -> (() -> ImplicitMembers)? { { ImplicitMembers() } } +} + +let _: ImplicitMembers = .implicit +let _: ImplicitMembers? = .implicit +let _: ImplicitMembers? = .optional + +let _: ImplicitMembers = .implicit.another.another +let _: ImplicitMembers = .createImplicit().another.another +let _: ImplicitMembers = .init().another.another + +let _: ImplicitMembers = .implicit.getAnother().another +let _: ImplicitMembers = .createImplicit().getAnother().another +let _: ImplicitMembers = .init().getAnother().another + +let _: ImplicitMembers = .implicit.getAnother(arg: 0).another +let _: ImplicitMembers = .createImplicit().getAnother(arg: 0).another +let _: ImplicitMembers = .init().getAnother(arg: 0).another + +let _: ImplicitMembers = .implicit.another.getAnother() +let _: ImplicitMembers = .createImplicit().another.getAnother() +let _: ImplicitMembers = .init().another.getAnother() + +let _: ImplicitMembers = .implicit.another.getAnother(arg: 0) +let _: ImplicitMembers = .createImplicit().another.getAnother(arg: 0) +let _: ImplicitMembers = .init().another.getAnother(arg: 0) + +let _: ImplicitMembers = .implicit.getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .createImplicit().getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .init().getAnother().getAnother(arg: 0) + +let _: ImplicitMembers = .implicit.getAnother().getAnother(arg: 0).another +let _: ImplicitMembers = .createImplicit().getAnother().getAnother(arg: 0).another +let _: ImplicitMembers = .init().getAnother().getAnother(arg: 0).another + +let _: ImplicitMembers = .implicit.another.getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .createImplicit().another.getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .init().another.getAnother().getAnother(arg: 0) + +let _: ImplicitMembers = .implicit.another.another.another.another.another +let _: ImplicitMembers = .implicit.getAnother().getAnother().getAnother().getAnother().getAnother() +let _: ImplicitMembers = .implicit.getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0) + +let _: ImplicitMembers = .optional! +let _: ImplicitMembers = .optional!.another +let _: ImplicitMembers = .createOptional()!.another +let _: ImplicitMembers = .optional!.anotherOptional! +let _: ImplicitMembers = .createOptional()!.anotherOptional! +let _: ImplicitMembers = .optional!.getAnotherOptional()! +let _: ImplicitMembers = .createOptional()!.getAnotherOptional()! + +let _: ImplicitMembers = .other.implicit +let _: ImplicitMembers = .implicit.anotherOther.implicit +let _: ImplicitMembers = .createOther().implicit +let _: ImplicitMembers = .implicit.getAnotherOther().implicit + +let _: ImplicitMembers = .other // expected-error {{member 'other' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit.anotherOther // expected-error {{member 'implicit' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMember // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMember'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMethod() // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMethod'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMember.another // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMember'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMethod().another // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMethod'}} +let _: ImplicitMembers = .implicit.getAnotherOther() // expected-error {{member 'getAnotherOther()' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} + +let _: ImplicitMembers? = .implicit.another +let _: ImplicitMembers? = .implicit.anotherOptional + +let _: ImplicitMembers? = .optional +let _: ImplicitMembers? = .optional?.another +let _: ImplicitMembers? = .optional?.anotherOptional +let _: ImplicitMembers? = .optional?.getAnother() +let _: ImplicitMembers? = .optional?.getAnotherOptional() +let _: ImplicitMembers? = .optional?.anotherOptional?.another +let _: ImplicitMembers? = .optional?.getAnotherOptional()?.another +let _: ImplicitMembers? = .createOptional() +let _: ImplicitMembers? = .createOptional()?.another +let _: ImplicitMembers? = .createOptional()?.anotherOptional +let _: ImplicitMembers? = .createOptional()?.getAnother() +let _: ImplicitMembers? = .createOptional()?.getAnotherOptional() +let _: ImplicitMembers? = .createOptional()?.anotherOptional?.another +let _: ImplicitMembers? = .createOptional()?.getAnotherOptional()?.another +// FIXME: This should be allowed +// let _: ImplicitMembers? = .superOptional???.another + +let _: ImplicitMembers = .implicit[()] +let _: ImplicitMembers = .implicit[optional: ()]! +let _: ImplicitMembers? = .implicit[optional: ()] +let _: ImplicitMembers = .implicit[func: ()]() +let _: ImplicitMembers = .implicit[funcOptional: ()]()! +let _: ImplicitMembers? = .implicit[funcOptional: ()]() +let _: ImplicitMembers = .implicit[optionalFunc: ()]!() +let _: ImplicitMembers? = .implicit[optionalFunc: ()]?() +let _: ImplicitMembers = .implicit.another[()] +let _: ImplicitMembers = .implicit.another[optional: ()]! +let _: ImplicitMembers? = .implicit.another[optional: ()] +let _: ImplicitMembers = .implicit.another[func: ()]() +let _: ImplicitMembers = .implicit.another[funcOptional: ()]()! +let _: ImplicitMembers? = .implicit.another[funcOptional: ()]() +let _: ImplicitMembers = .implicit.another[optionalFunc: ()]!() +let _: ImplicitMembers? = .implicit.another[optionalFunc: ()]?() +let _: ImplicitMembers = .implicit[()].another +let _: ImplicitMembers = .implicit[optional: ()]!.another +let _: ImplicitMembers? = .implicit[optional: ()]?.another +let _: ImplicitMembers = .implicit[func: ()]().another +let _: ImplicitMembers = .implicit[funcOptional: ()]()!.another +let _: ImplicitMembers? = .implicit[funcOptional: ()]()?.another +let _: ImplicitMembers = .implicit[optionalFunc: ()]!().another +let _: ImplicitMembers? = .implicit[optionalFunc: ()]?().another + +func implicit(_ i: inout ImplicitMembers) { + if i == .implicit {} + if i == .implicit.another {} + if i == .implicit.getAnother() {} + if i == .optional?.another {} + if i == .optional!.another {} + if i == .createOptional()?.another {} +} + +struct ImplicitGeneric { + static var implicit: ImplicitGeneric { ImplicitGeneric() } + var another: ImplicitGeneric { ImplicitGeneric() } + func getAnother() -> ImplicitGeneric { + ImplicitGeneric() + } +} + +extension ImplicitGeneric where T == Int { + static var implicitInt: ImplicitGeneric { ImplicitGeneric() } + static var implicitString: ImplicitGeneric { ImplicitGeneric() } + var anotherInt: ImplicitGeneric { ImplicitGeneric() } + var anotherIntString: ImplicitGeneric { ImplicitGeneric() } + func getAnotherInt() -> ImplicitGeneric { + ImplicitGeneric() + } +} + +extension ImplicitGeneric where T == String { + static var implicitString: ImplicitGeneric { ImplicitGeneric() } + var anotherString: ImplicitGeneric { ImplicitGeneric() } + var anotherStringInt: ImplicitGeneric { ImplicitGeneric() } + func getAnotherString() -> ImplicitGeneric { + ImplicitGeneric() + } + func getAnotherStringInt() -> ImplicitGeneric { + ImplicitGeneric() + } +} + +func implicit(_ arg: ImplicitGeneric) {} + +implicit(.implicitInt) +implicit(.implicit.anotherInt) +implicit(.implicit.anotherInt.another) +implicit(.implicit.another.anotherInt) +implicit(.implicit.getAnotherInt()) +implicit(.implicit.another.getAnotherInt()) +implicit(.implicit.getAnother().anotherInt) +implicit(.implicit.getAnotherInt()) +implicit(.implicit.getAnother().getAnotherInt()) +implicit(.implicitString.anotherStringInt) +// Member types along the chain can have different generic arguments +implicit(.implicit.anotherIntString.anotherStringInt) + +implicit(.implicit.anotherString.anotherStringInt) // expected-error {{type of expression is ambiguous without more context}} +implicit(.implicit.getAnotherString().anotherStringInt) // expected-error {{type of expression is ambiguous without more context}} +implicit(.implicit.anotherString.getAnotherStringInt()) // expected-error {{type of expression is ambiguous without more context}} +implicit(.implicit.getAnotherString().getAnotherStringInt()) // expected-error {{type of expression is ambiguous without more context}} diff --git a/test/expr/delayed-ident/nested_type.swift b/test/expr/delayed-ident/nested_type.swift index da8a3fece6538..520d08c90dea9 100644 --- a/test/expr/delayed-ident/nested_type.swift +++ b/test/expr/delayed-ident/nested_type.swift @@ -4,12 +4,21 @@ class Base { class Derived : Base { init(x: Int) {} + class Sub: Derived { + init(y: Int) {} + } + typealias Ident = Derived + typealias Ident2 = Base } typealias Ident = Base } let _: Base = .Derived(x: 12) let _: Base = .Ident() +let _: Base = .Derived.Sub(y: 1) +let _: Base = .Derived.init(x: 3) +let _: Base = .Derived.Ident(x: 3) +let _: Base = .Derived.Ident2() // Typealias in protocol. protocol P { @@ -19,9 +28,19 @@ extension P { typealias Impl2 = ConcreteP } struct ConcreteP : P { + struct NestedP: P {} + typealias Same = ConcreteP } let _: P = .Impl1() let _: P = .Impl2() let _: ConcreteP = .Impl1() let _: ConcreteP = .Impl2() +let _: P = .Impl1.NestedP() +let _: P = .Impl2.NestedP() +let _: ConcreteP = .Impl1.Same() +let _: ConcreteP = .Impl2.Same() +let _: P = .Impl1.init() +let _: P = .Impl2.init() +let _: ConcreteP = .Impl1.init() +let _: ConcreteP = .Impl2.init() diff --git a/test/expr/delayed-ident/static_var.swift b/test/expr/delayed-ident/static_var.swift index cfe701754f791..3a01e7ff4137c 100644 --- a/test/expr/delayed-ident/static_var.swift +++ b/test/expr/delayed-ident/static_var.swift @@ -50,8 +50,7 @@ var _: HasClosure = .factoryOpt(3) // expected-error@-1 {{value of optional type '((Int) -> HasClosure)?' must be unwrapped to a value of type '(Int) -> HasClosure'}} // expected-note@-2 {{coalesce}} // expected-note@-3 {{force-unwrap}} -// FIXME: we should accept this -var _: HasClosure = .factoryOpt!(4) // expected-error {{cannot infer contextual base in reference to member 'factoryOpt'}} +var _: HasClosure = .factoryOpt!(4) infix operator =%: ComparisonPrecedence diff --git a/test/expr/postfix/dot/optional_context_member.swift b/test/expr/postfix/dot/optional_context_member.swift index b761c21657ab6..c3279781e40b0 100644 --- a/test/expr/postfix/dot/optional_context_member.swift +++ b/test/expr/postfix/dot/optional_context_member.swift @@ -27,9 +27,8 @@ func nonOptContext() -> Foo { // expected-error@-1 {{value of optional type 'Foo?' must be unwrapped to a value of type 'Foo'}} // expected-note@-2 {{coalesce}} // expected-note@-3 {{force-unwrap}} - // TODO - //case (): - // return .someOptFunc()! + case (): // expected-warning {{case is already handled by previous patterns; consider removing it}} + return .someOptFunc()! } } From 1069fe280fcfb252d08111b4483537a8b4f7a89f Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:02:05 -0400 Subject: [PATCH 039/120] Fixup diagnostics --- lib/Sema/CSDiagnostics.cpp | 20 ++++++----- lib/Sema/CSGen.cpp | 37 ++++++++------------- lib/Sema/CSSimplify.cpp | 29 +--------------- lib/Sema/ConstraintLocator.cpp | 5 +++ lib/Sema/ConstraintLocatorPathElts.def | 3 ++ lib/Sema/ConstraintSystem.cpp | 5 +++ lib/Sema/ConstraintSystem.h | 10 ------ test/expr/delayed-ident/member_chains.swift | 17 +++++++++- 8 files changed, 56 insertions(+), 70 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 5c04d1aefce5a..5ea518d6a6281 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2129,15 +2129,19 @@ bool ContextualFailure::diagnoseAsError() { return false; } - case ConstraintLocator::Member: - case ConstraintLocator::FunctionResult: - case ConstraintLocator::UnresolvedMember: { + case ConstraintLocator::UnresolvedMemberChainResult: { auto &solution = getSolution(); - auto locator = getConstraintLocator(anchor, - isExpr(anchor) - ? ConstraintLocator::UnresolvedMember - : ConstraintLocator::Member); - auto overload = getOverloadChoiceIfAvailable(locator); + auto member = anchor; + if (auto *CE = getAsExpr(anchor)) + member = CE->getFn(); + + auto kind = ConstraintLocator::Member; + if (isExpr(anchor)) + kind = ConstraintLocator::UnresolvedMember; + else if (isExpr(anchor)) + kind = ConstraintLocator::SubscriptMember; + auto overload = getOverloadChoiceIfAvailable(getConstraintLocator(member, + kind)); if (!(overload && overload->choice.isDecl())) return false; diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 91f8ad9bf8b77..cfe2e2bc2c2d7 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -849,7 +849,6 @@ namespace { } Type addUnresolvedMemberChainConstraints(Expr *expr, Type resultTy, - ConstraintLocator *resultLocator, unsigned additionalOptions = 0) { // If this is a member chain hanging off of an UnresolvedMemberExpr, // and we're at the last element of the chain, then the contextual type @@ -858,14 +857,16 @@ namespace { auto *chainBaseExpr = CS.getMemberChainBase(expr); if (auto *UME = dyn_cast(chainBaseExpr)) { // Create a new type variable representing the result of the chain. - auto chainResultTy = CS.createTypeVariable(resultLocator, + auto locator = CS.getConstraintLocator(expr, + ConstraintLocator::UnresolvedMemberChainResult); + auto chainResultTy = CS.createTypeVariable(locator, additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape); auto chainBaseTy = CS.getUnresolvedMemberBaseType(UME); // The result of this element of the chain must be convertible to the // contextual type, and the contextual type must be equal to the base. CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, - resultLocator); + locator); CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, CS.getConstraintLocator(UME, ConstraintLocator::MemberRefBase)); @@ -1487,10 +1488,9 @@ namespace { // If there is an argument, apply it. if (auto arg = expr->getArgument()) { // Create a new type variable for the result of the function. - auto outputLocator = CS.getConstraintLocator(expr, - ConstraintLocator::FunctionResult); - auto outputTy = CS.createTypeVariable(outputLocator, - TVO_CanBindToNoEscape); + auto outputTy = CS.createTypeVariable( + CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), + TVO_CanBindToNoEscape); // The function/enum case must be callable with the given argument. @@ -1509,13 +1509,12 @@ namespace { CS.getConstraintLocator(expr), {expr->getArgumentLabels(), expr->getUnlabeledTrailingClosureIndex()}); - return addUnresolvedMemberChainConstraints(expr, outputTy, - outputLocator); + return addUnresolvedMemberChainConstraints(expr, outputTy); } // Otherwise, add the usual constraints for an element of an unresolved // member chain. - return addUnresolvedMemberChainConstraints(expr, memberTy, memberLocator, + return addUnresolvedMemberChainConstraints(expr, memberTy, TVO_CanBindToLValue); } @@ -1586,7 +1585,6 @@ namespace { expr->getOuterAlternatives()); return addUnresolvedMemberChainConstraints(expr, resultTy, - CS.getConstraintLocator(expr, ConstraintLocator::Member), TVO_CanBindToLValue); } @@ -1750,10 +1748,8 @@ namespace { expr->getIndex(), decl, expr->getArgumentLabels(), expr->getUnlabeledTrailingClosureIndex()); - auto resultLocator = CS.getConstraintLocator(expr, - ConstraintLocator::FunctionResult); - return addUnresolvedMemberChainConstraints(expr, resultTy, resultLocator, + return addUnresolvedMemberChainConstraints(expr, resultTy, TVO_CanBindToLValue); } @@ -2938,13 +2934,8 @@ namespace { } // If the ApplyExpr is a CallExpr, add chain constraints as necessary. - if (isa(expr)) { - auto resultLocator = CS.getConstraintLocator(expr, - ConstraintLocator::FunctionResult); - - return addUnresolvedMemberChainConstraints(expr, resultType, - resultLocator); - } + if (isa(expr)) + return addUnresolvedMemberChainConstraints(expr, resultType); return resultType; } @@ -3256,7 +3247,7 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return addUnresolvedMemberChainConstraints(expr, objectTy, locator, + return addUnresolvedMemberChainConstraints(expr, objectTy, TVO_CanBindToLValue); } @@ -3292,7 +3283,7 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return addUnresolvedMemberChainConstraints(expr, objectTy, locator); + return addUnresolvedMemberChainConstraints(expr, objectTy); } Type visitOpenExistentialExpr(OpenExistentialExpr *expr) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 274fc1055697d..7a3ce7d18ee8f 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4135,20 +4135,6 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::FunctionResult: { - // FIXME: Is this necessary? - if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, - locator)) - break; - - // If this mismatch occurs at the tail of an unresolved member chain, - // there's a contextual mismatch. - if (isUnresolvedMemberChainTail(anchor)) { - auto *fix = IgnoreContextualType::create(*this, lhs, rhs, - getConstraintLocator(locator)); - conversionsOrFixes.push_back(fix); - break; - } - auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1}); // If this is a mismatch between contextual type and (trailing) // closure with explicitly specified result type let's record it @@ -4195,16 +4181,6 @@ bool ConstraintSystem::repairFailures( repairByInsertingExplicitCall(lhs, rhs)) return true; } - - // If this mismatch occurs at the tail of an unresolved member chain, - // there's a contextual mismatch. - if (isUnresolvedMemberChainTail(anchor)) { - auto *fix = IgnoreContextualType::create(*this, lhs, rhs, - getConstraintLocator(locator)); - conversionsOrFixes.push_back(fix); - break; - } - break; } @@ -4330,10 +4306,7 @@ bool ConstraintSystem::repairFailures( break; } - case ConstraintLocator::UnresolvedMember: { - if (!isExpr(anchor)) - break; - + case ConstraintLocator::UnresolvedMemberChainResult: { if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) break; diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index c5b4a864e4e05..708d90d7dc6de 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -81,6 +81,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::TernaryBranch: case ConstraintLocator::PatternMatch: case ConstraintLocator::ArgumentAttribute: + case ConstraintLocator::UnresolvedMemberChainResult: return 0; case ConstraintLocator::FunctionArgument: @@ -483,6 +484,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { break; } + + case UnresolvedMemberChainResult: + out << "unresolved chain result"; + break; } } out << ']'; diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index 67228bcbc5591..435d20b74ec9f 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -192,6 +192,9 @@ CUSTOM_LOCATOR_PATH_ELT(PatternMatch) /// of a problem. CUSTOM_LOCATOR_PATH_ELT(ArgumentAttribute) +/// The result of a chain of member accesses off an UnresolvedMemberExpr +SIMPLE_LOCATOR_PATH_ELT(UnresolvedMemberChainResult) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 0f518e4695ab8..322dc620c19de 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3859,6 +3859,11 @@ void constraints::simplifyLocator(ASTNode &anchor, break; } + case ConstraintLocator::UnresolvedMemberChainResult: { + path = path.slice(1); + continue; + } + default: // FIXME: Lots of other cases to handle. break; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index cb2f16f26696b..a9018f1a302e9 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3086,16 +3086,6 @@ class ConstraintSystem { return parent == nullptr || !isMemberChainMember(parent); } - /// Whether this node sits at the end of a member chain that hangs off an - /// \c UnresolvedMemberExpr at the base. - bool isUnresolvedMemberChainTail(ASTNode node) { - if (auto *expr = getAsExpr(node)) - return isMemberChainTail(expr) - && isa(getMemberChainBase(expr)); - else - return false; - } - public: /// Whether we should attempt to fix problems. diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index c8f468ace2e9f..9333047a27f12 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -50,6 +50,7 @@ struct ImplicitMembers: Equatable { subscript(func arg: Void) -> (() -> ImplicitMembers) { { ImplicitMembers() } } subscript(funcOptional arg: Void) -> (() -> ImplicitMembers?) { { ImplicitMembers() } } subscript(optionalFunc arg: Void) -> (() -> ImplicitMembers)? { { ImplicitMembers() } } + subscript(other arg: Void) -> Other { Other() } } let _: ImplicitMembers = .implicit @@ -100,18 +101,32 @@ let _: ImplicitMembers = .createOptional()!.anotherOptional! let _: ImplicitMembers = .optional!.getAnotherOptional()! let _: ImplicitMembers = .createOptional()!.getAnotherOptional()! +let _: ImplicitMembers = .optional // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{35-35= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{35-35=!}} +let _: ImplicitMembers = .implicit.anotherOptional // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{51-51= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{51-51=!}} +let _: ImplicitMembers = .createOptional() // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{43-43= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{43-43=!}} +let _: ImplicitMembers = .implicit.getAnotherOptional() // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{56-56= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{56-56=!}} +let _: ImplicitMembers = .implicit[optional: ()] // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{49-49= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{49-49=!}} +let _: ImplicitMembers = .implicit[funcOptional: ()]() // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{55-55= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{55-55=!}} + +// FIXME: Improve these diagnostics (should probably offer unwrapping, as above) +let _: ImplicitMembers = .implicit.anotherOptional?.another // expected-error{{cannot infer contextual base in reference to member 'implicit'}} expected-error{{cannot convert value of type 'Optional<_>' to specified type 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit[optionalFunc: ()]?() // expected-error{{cannot infer contextual base in reference to member 'implicit'}} expected-error{{cannot convert value of type 'Optional<_>' to specified type 'ImplicitMembers'}} + + let _: ImplicitMembers = .other.implicit let _: ImplicitMembers = .implicit.anotherOther.implicit let _: ImplicitMembers = .createOther().implicit let _: ImplicitMembers = .implicit.getAnotherOther().implicit +let _: ImplicitMembers = .implicit[other: ()].implicit let _: ImplicitMembers = .other // expected-error {{member 'other' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} -let _: ImplicitMembers = .implicit.anotherOther // expected-error {{member 'implicit' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit.anotherOther // expected-error {{member 'anotherOther' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMember // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMember'}} let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMethod() // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMethod'}} let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMember.another // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMember'}} let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMethod().another // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMethod'}} let _: ImplicitMembers = .implicit.getAnotherOther() // expected-error {{member 'getAnotherOther()' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit[other: ()] // expected-error {{member 'subscript(other:)' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} let _: ImplicitMembers? = .implicit.another let _: ImplicitMembers? = .implicit.anotherOptional From 3b5deab1140912a4458663d44b1416988d9c6683 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:02:14 -0400 Subject: [PATCH 040/120] More diagnostic improvements --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/CSDiagnostics.cpp | 5 +++++ lib/Sema/CSSimplify.cpp | 4 ++-- test/expr/delayed-ident/member_chains.swift | 10 +++++----- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5a0a494f535e6..e2176f6bd6c26 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -455,6 +455,9 @@ ERROR(cannot_convert_closure_result_nil,none, ERROR(cannot_convert_parent_type,none, "cannot convert parent type %0 to expected type %1", (Type, Type)) +ERROR(cannot_convert_chain_result_type,none, + "member chain produces result of type %0 but contextual base was inferred as %1", + (Type, Type)) NOTE(generic_argument_mismatch,none, "arguments to generic parameter %0 (%1 and %2) are expected to be equal", diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 5ea518d6a6281..225f8aeaf6ed1 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -755,6 +755,11 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() { break; } + case ConstraintLocator::UnresolvedMemberChainResult: { + diagnostic = diag::cannot_convert_chain_result_type; + break; + } + default: break; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 7a3ce7d18ee8f..3e93fadc44668 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -10056,10 +10056,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( impact = 10; } - if (locator->isLastElement()) { + if (locator->isLastElement()) { // If this is a contextual failure for an unresolved member, then increase // the impact to attempt other fixes first and avoid ambiguity. - impact = 5; + impact = 2; } if (recordFix(fix, impact)) diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index 9333047a27f12..dd13fdca5ddca 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -182,7 +182,7 @@ func implicit(_ i: inout ImplicitMembers) { if i == .createOptional()?.another {} } -struct ImplicitGeneric { +struct ImplicitGeneric { // expected-note4 {{arguments to generic parameter 'T' ('Int' and 'String') are expected to be equal}} static var implicit: ImplicitGeneric { ImplicitGeneric() } var another: ImplicitGeneric { ImplicitGeneric() } func getAnother() -> ImplicitGeneric { @@ -227,7 +227,7 @@ implicit(.implicitString.anotherStringInt) // Member types along the chain can have different generic arguments implicit(.implicit.anotherIntString.anotherStringInt) -implicit(.implicit.anotherString.anotherStringInt) // expected-error {{type of expression is ambiguous without more context}} -implicit(.implicit.getAnotherString().anotherStringInt) // expected-error {{type of expression is ambiguous without more context}} -implicit(.implicit.anotherString.getAnotherStringInt()) // expected-error {{type of expression is ambiguous without more context}} -implicit(.implicit.getAnotherString().getAnotherStringInt()) // expected-error {{type of expression is ambiguous without more context}} +implicit(.implicit.anotherString.anotherStringInt) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} +implicit(.implicit.getAnotherString().anotherStringInt) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} +implicit(.implicit.anotherString.getAnotherStringInt()) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} +implicit(.implicit.getAnotherString().getAnotherStringInt()) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} From 788d3f09530e5cf6d0534a7063e674e527d53a82 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:02:16 -0400 Subject: [PATCH 041/120] Fix missing call diagnostic --- lib/Sema/CSDiagnostics.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 225f8aeaf6ed1..5e2b733172a91 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2415,7 +2415,8 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { if (getLocator()->isLastElement()) return false; - if (getLocator()->isLastElement()) + if (getLocator() + ->isLastElement()) return false; auto *srcFT = getFromType()->getAs(); From aedde34fecc3db6365d3ce7606fe16a022cd2fbe Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:02:21 -0400 Subject: [PATCH 042/120] [Sema] Move unresolved base tracking into CSGen Remove the tracking of unresolved base types from the constraint system, and place it entirely within the generation phase. We have other ways of getting at the base types after generation. --- lib/Sema/CSApply.cpp | 35 ++------ lib/Sema/CSGen.cpp | 173 +++++++++++++++++++++++------------- lib/Sema/CSSimplify.cpp | 35 ++++---- lib/Sema/CSSolver.cpp | 13 --- lib/Sema/ConstraintSystem.h | 64 ------------- 5 files changed, 134 insertions(+), 186 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1e1775e464dd1..488fba7065882 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2753,30 +2753,16 @@ namespace { } Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { - // If constraint solving resolved the base type to an UnresolvedType, - // then we're in an ambiguity tolerant mode used for diagnostic - // generation. Just leave this as an unresolved member reference. - Type baseTy = simplifyType(solution.getUnresolvedMemberBaseType(expr)); - - if (baseTy->hasUnresolvedType()) { - cs.setType(expr, baseTy); - return expr; - } - auto &ctx = cs.getASTContext(); - - // Find the selected member. + // Find the selected member and base type. auto memberLocator = cs.getConstraintLocator( expr, ConstraintLocator::UnresolvedMember); auto selected = solution.getOverloadChoice(memberLocator); - - // If the member came by optional unwrapping, then unwrap the base type. - if (selected.choice.getKind() - == OverloadChoiceKind::DeclViaUnwrappedOptional) { - baseTy = baseTy->getOptionalObjectType(); - assert(baseTy - && "got unwrapped optional decl from non-optional base?!"); - } + + // Unresolved member lookup always happens in a metatype so dig out the + // instance type. + auto metaTy = selected.choice.getBaseType()->castTo(); + auto baseTy = cs.simplifyType(metaTy->getInstanceType()); // The base expression is simply the metatype of the base type. // FIXME: This location info is bogus. @@ -8449,15 +8435,6 @@ void Solution::setExprTypes(Expr *expr) const { expr->walk(SET); } -Type Solution::getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) const { - auto result = unresolvedMemberBaseTypes.find(expr); - if (result != unresolvedMemberBaseTypes.end()) - return result->second; - - auto &cs = getConstraintSystem(); - return cs.getUnresolvedMemberBaseType(expr); -} - /// MARK: SolutionResult implementation. SolutionResult SolutionResult::forSolved(Solution &&solution) { diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index cfe2e2bc2c2d7..75a29f088751b 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -74,6 +74,48 @@ static bool mergeRepresentativeEquivalenceClasses(ConstraintSystem &CS, return false; } +/// Find the next element in a chain of members. If this expression is (or +/// could be) the base of such a chain, this will return \c nullptr. +static Expr *getMemberChainSubExpr(Expr *expr) { + assert(expr && "getMemberChainSubExpr called with null expr!"); + if (auto *UDE = dyn_cast(expr)) { + return UDE->getBase(); + } else if (auto *CE = dyn_cast(expr)) { + return CE->getFn(); + } else if (auto *BOE = dyn_cast(expr)) { + return BOE->getSubExpr(); + } else if (auto *FVE = dyn_cast(expr)) { + return FVE->getSubExpr(); + } else if (auto *SE = dyn_cast(expr)) { + return SE->getBase(); + } else { + return nullptr; + } +} + +/// Returns the base of a chain of member accesses/method calls. Most +/// expressions do not participate in member chains, and just return \c this. +static Expr *getMemberChainBase(Expr *expr) { + if (auto *subExpr = getMemberChainSubExpr(expr)) + return getMemberChainBase(subExpr); + else + return expr; +} + +/// Whether this expression is a member of a member chain. +static bool isMemberChainMember(Expr *expr) { + return getMemberChainSubExpr(expr) != nullptr; +} +/// Whether this expression sits at the end of a chain of member accesses. +static bool isMemberChainTail(ConstraintSystem &cs, Expr *expr) { + assert(expr && "isMemberChainTail called with null expr!"); + // If this expression's parent is not itself part of a chain (or, this expr + // has no parent expr), this must be the tail of the chain. + Expr *parent = cs.getParentExpr(expr); + + return parent == nullptr || !isMemberChainMember(parent); +} + namespace { /// Internal struct for tracking information about types within a series @@ -792,6 +834,10 @@ namespace { /// Keep track of acceptable DiscardAssignmentExpr's. llvm::SmallPtrSet CorrectDiscardAssignmentExprs; + /// A map from the heads to the tails of each unresolved member chain found + /// during our walk. + llvm::MapVector UnresolvedBaseTypes; + /// Returns false and emits the specified diagnostic if the member reference /// base is a nil literal. Returns true otherwise. bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) { @@ -848,36 +894,6 @@ namespace { return tv; } - Type addUnresolvedMemberChainConstraints(Expr *expr, Type resultTy, - unsigned additionalOptions = 0) { - // If this is a member chain hanging off of an UnresolvedMemberExpr, - // and we're at the last element of the chain, then the contextual type - // (represented with a new type variable) must equal the base type. - if (CS.isMemberChainTail(expr)) { - auto *chainBaseExpr = CS.getMemberChainBase(expr); - if (auto *UME = dyn_cast(chainBaseExpr)) { - // Create a new type variable representing the result of the chain. - auto locator = CS.getConstraintLocator(expr, - ConstraintLocator::UnresolvedMemberChainResult); - auto chainResultTy = CS.createTypeVariable(locator, - additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape); - auto chainBaseTy = CS.getUnresolvedMemberBaseType(UME); - - // The result of this element of the chain must be convertible to the - // contextual type, and the contextual type must be equal to the base. - CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, - locator); - CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, - CS.getConstraintLocator(UME, ConstraintLocator::MemberRefBase)); - - return chainResultTy; - } - } - - // Otherwise, just use the proposed result type. - return resultTy; - } - /// Add constraints for a subscript operation. Type addSubscriptConstraints( Expr *anchor, Type baseTy, Expr *index, @@ -1452,6 +1468,44 @@ namespace { Type visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) { llvm_unreachable("Already typechecked"); } + + void setUnresolvedBaseType(UnresolvedMemberExpr *UME, Type ty) { + UnresolvedBaseTypes.insert({UME, ty}); + } + + Type addUnresolvedMemberChainConstraints(UnresolvedMemberExpr *base, + Expr *tail, + Type resultTy) { + assert(isa(tail) || + isa(tail) || + isa(tail) || + isa(tail) || + isa(tail) || + isa(tail) && + "Unexpected expression at end of unresolved member chain"); + // Copy any type variable options from the result of the tail member to + // the result of the entire chain. + unsigned additionalOptions = 0; + if (auto *tvt = resultTy->getAs()) + additionalOptions = tvt->getImpl().getRawOptions(); + + // The contextual type (represented with a new type variable) must equal + // the base type. + auto locator = CS.getConstraintLocator(tail, + ConstraintLocator::UnresolvedMemberChainResult); + auto chainResultTy = CS.createTypeVariable(locator, + additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape); + auto chainBaseTy = UnresolvedBaseTypes.find(base)->second; + + // The result of this element of the chain must be convertible to the + // contextual type, and the contextual type must be equal to the base. + CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, + locator); + CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, + CS.getConstraintLocator(base, ConstraintLocator::MemberRefBase)); + + return chainResultTy; + } virtual Type visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { auto baseLocator = CS.getConstraintLocator( @@ -1461,17 +1515,17 @@ namespace { expr->getArgument() ? FunctionRefKind::DoubleApply : FunctionRefKind::Compound; - auto memberLocator = CS.getConstraintLocator(expr, - ConstraintLocator::UnresolvedMember); + auto memberLocator + = CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember); // Since base type in this case is completely dependent on context it // should be marked as a potential hole. - auto baseTy = CS.createTypeVariable(baseLocator, - TVO_CanBindToHole | TVO_CanBindToNoEscape); - CS.setUnresolvedMemberBaseType(expr, baseTy); + auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape | + TVO_CanBindToHole); + setUnresolvedBaseType(expr, baseTy); - auto memberTy = CS.createTypeVariable(memberLocator, - TVO_CanBindToLValue | TVO_CanBindToNoEscape); + auto memberTy = CS.createTypeVariable( + memberLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); // An unresolved member expression '.member' is modeled as a value member // constraint @@ -1509,13 +1563,11 @@ namespace { CS.getConstraintLocator(expr), {expr->getArgumentLabels(), expr->getUnlabeledTrailingClosureIndex()}); - return addUnresolvedMemberChainConstraints(expr, outputTy); + return outputTy; } - // Otherwise, add the usual constraints for an element of an unresolved - // member chain. - return addUnresolvedMemberChainConstraints(expr, memberTy, - TVO_CanBindToLValue); + // Otherwise, the result is just the type of the member. + return memberTy; } Type visitUnresolvedDotExpr(UnresolvedDotExpr *expr) { @@ -1579,13 +1631,9 @@ namespace { return methodTy; } - auto resultTy = addMemberRefConstraints(expr, expr->getBase(), - expr->getName(), - expr->getFunctionRefKind(), - expr->getOuterAlternatives()); - - return addUnresolvedMemberChainConstraints(expr, resultTy, - TVO_CanBindToLValue); + return addMemberRefConstraints(expr, expr->getBase(), expr->getName(), + expr->getFunctionRefKind(), + expr->getOuterAlternatives()); } Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) { @@ -1744,13 +1792,10 @@ namespace { if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal)) return nullptr; - auto resultTy = addSubscriptConstraints(expr, CS.getType(base), - expr->getIndex(), - decl, expr->getArgumentLabels(), - expr->getUnlabeledTrailingClosureIndex()); - - return addUnresolvedMemberChainConstraints(expr, resultTy, - TVO_CanBindToLValue); + return addSubscriptConstraints(expr, CS.getType(base), + expr->getIndex(), + decl, expr->getArgumentLabels(), + expr->getUnlabeledTrailingClosureIndex()); } Type visitArrayExpr(ArrayExpr *expr) { @@ -2933,10 +2978,6 @@ namespace { resultType = fixedType; } - // If the ApplyExpr is a CallExpr, add chain constraints as necessary. - if (isa(expr)) - return addUnresolvedMemberChainConstraints(expr, resultType); - return resultType; } @@ -3247,8 +3288,7 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return addUnresolvedMemberChainConstraints(expr, objectTy, - TVO_CanBindToLValue); + return objectTy; } Type visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) { @@ -3283,7 +3323,7 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return addUnresolvedMemberChainConstraints(expr, objectTy); + return objectTy; } Type visitOpenExistentialExpr(OpenExistentialExpr *expr) { @@ -3818,6 +3858,13 @@ namespace { } if (auto type = CG.visit(expr)) { + if (isMemberChainTail(CG.getConstraintSystem(), expr)) { + auto *chainBase = getMemberChainBase(expr); + if (auto *UME = dyn_cast(chainBase)) { + type = CG.addUnresolvedMemberChainConstraints(UME, expr, type); + } + } + auto simplifiedType = CS.simplifyType(type); CS.setType(expr, simplifiedType); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 3e93fadc44668..922e25bdc45cf 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6777,24 +6777,25 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, // then the type we're constructing may not actually be the base of the // UnresolvedMemberExpr--instead, it will be the type of the nested type // member. - if (locatorEndsWith(locator, ConstraintLocator::ConstructorMember)) { - auto *baseLocator = cs.getConstraintLocator(UME, - ConstraintLocator::UnresolvedMember); - auto result = llvm::find_if( - cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { - return typeVar->getImpl().getLocator() == baseLocator; - }); - assert(result != cs.getTypeVariables().end()); - baseType = cs.simplifyType(*result); + // We need to find type variable which represents contextual base. + auto *baseLocator = cs.getConstraintLocator( + UME, locatorEndsWith(locator, ConstraintLocator::ConstructorMember) + ? ConstraintLocator::UnresolvedMember + : ConstraintLocator::MemberRefBase); + + // FIXME: Type variables responsible for contextual base could be cached + // in the constraint system to speed up lookup. + auto result = llvm::find_if( + cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { + return typeVar->getImpl().getLocator() == baseLocator; + }); - // Otherwise, this an explicit reference to an initializer member - // (`.init(...)`). - } else { - // Constraint for member base is formed as '$T.Type[.getRValueType(); + // Constraint for member base is formed as '$T.Type[.(anchor)) { // Key path can't refer to initializers e.g. `\Type.init` return AllowInvalidRefInKeyPath::forRef(cs, init, locator); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index ba10f3c128c73..8f0afc7b18b81 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -176,10 +176,6 @@ Solution ConstraintSystem::finalize() { solution.nodeTypes.insert(nodeType); } - for (auto &baseType : UnresolvedMemberBaseTypes) { - solution.unresolvedMemberBaseTypes.insert(baseType); - } - // Remember contextual types. solution.contextualTypes.assign( contextualTypes.begin(), contextualTypes.end()); @@ -255,11 +251,6 @@ void ConstraintSystem::applySolution(const Solution &solution) { setType(nodeType.first, nodeType.second); } - // Add the unresolved member base types back. - for (auto &baseType : solution.unresolvedMemberBaseTypes) { - setUnresolvedMemberBaseType(baseType.first, baseType.second); - } - // Add the contextual types. for (const auto &contextualType : solution.contextualTypes) { if (!getContextualTypeInfo(contextualType.first)) { @@ -484,7 +475,6 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numOpenedExistentialTypes = cs.OpenedExistentialTypes.size(); numDefaultedConstraints = cs.DefaultedConstraints.size(); numAddedNodeTypes = cs.addedNodeTypes.size(); - numUnresolvedMemberBaseTypes = cs.UnresolvedMemberBaseTypes.size(); numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); @@ -563,9 +553,6 @@ ConstraintSystem::SolverScope::~SolverScope() { } truncate(cs.addedNodeTypes, numAddedNodeTypes); - // Remove any unresolved member base types. - truncate(cs.UnresolvedMemberBaseTypes, numUnresolvedMemberBaseTypes); - // Remove any conformances checked along the current path. truncate(cs.CheckedConformances, numCheckedConformances); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index a9018f1a302e9..bc95ed45c0b55 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1080,9 +1080,6 @@ class Solution { /// The node -> type mappings introduced by this solution. llvm::DenseMap nodeTypes; - /// The unresolved member base types introduced by this solution. - llvm::MapVector unresolvedMemberBaseTypes; - /// Contextual types introduced by this solution. std::vector> contextualTypes; @@ -1198,9 +1195,6 @@ class Solution { /// "resolved" concrete type. Type getResolvedType(ASTNode node) const; - /// Retrieve the type of a particular \c UnresolvedMemberExpr's base. - Type getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) const; - /// Resolve type variables present in the raw type, using generic parameter /// types where possible. Type resolveInterfaceType(Type type) const; @@ -2056,9 +2050,6 @@ class ConstraintSystem { llvm::DenseMap OpenedParameterTypes; - /// Maps \c UnresolvedMemberExprs to their (implicit) base types. - llvm::MapVector UnresolvedMemberBaseTypes; - /// The set of constraint restrictions used to reach the /// current constraint system. /// @@ -2580,8 +2571,6 @@ class ConstraintSystem { unsigned numAddedNodeTypes; - unsigned numUnresolvedMemberBaseTypes; - unsigned numCheckedConformances; unsigned numDisabledConstraints; @@ -2884,17 +2873,6 @@ class ConstraintSystem { return CTP_Unused; } - void setUnresolvedMemberBaseType(UnresolvedMemberExpr *expr, Type type) { - assert(expr && "Expected non-null expression"); - assert(type && "Expected non-null type"); - UnresolvedMemberBaseTypes[expr] = type; - } - - Type getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) { - assert(expr && "Expected non-null expression"); - return UnresolvedMemberBaseTypes.find(expr)->second; - } - void setSolutionApplicationTarget( SolutionApplicationTargetsKey key, SolutionApplicationTarget target) { assert(solutionApplicationTargets.count(key) == 0 && @@ -3044,48 +3022,6 @@ class ConstraintSystem { bool isDeclUnavailable(const Decl *D, ConstraintLocator *locator = nullptr) const; - /// Find the next element in a chain of members. If this expression is (or - /// could be) the base of such a chain, this will return \c nullptr. - Expr *getMemberChainSubExpr(Expr *expr) { - assert(expr && "isMemberChainSubExpr called with null expr!"); - if (auto *UDE = dyn_cast(expr)) { - return UDE->getBase(); - } else if (auto *CE = dyn_cast(expr)) { - return CE->getFn(); - } else if (auto *BOE = dyn_cast(expr)) { - return BOE->getSubExpr(); - } else if (auto *FVE = dyn_cast(expr)) { - return FVE->getSubExpr(); - } else if (auto *SE = dyn_cast(expr)) { - return SE->getBase(); - } else { - return nullptr; - } - } - - /// Returns the base of a chain of member accesses/method calls. Most - /// expressions do not participate in member chains, and just return \c this. - Expr *getMemberChainBase(Expr *expr) { - if (auto *subExpr = getMemberChainSubExpr(expr)) - return getMemberChainBase(subExpr); - else - return expr; - } - - /// Whether this expression is a member of a member chain. - bool isMemberChainMember(Expr *expr) { - return getMemberChainSubExpr(expr) != nullptr; - } - /// Whether this expression sits at the end of a chain of member accesses. - bool isMemberChainTail(Expr *expr) { - assert(expr && "isMemberChainTail called with null expr!"); - // If this expression's parent is not itself part of a chain (or, this expr - // has no parent expr), this must be the tail of the chain. - Expr *parent = getParentExpr(expr); - - return parent == nullptr || !isMemberChainMember(parent); - } - public: /// Whether we should attempt to fix problems. From 4e9b7b20dbc5d98e6f504116a19d80889cb28568 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:02:23 -0400 Subject: [PATCH 043/120] [Sema] Inject implicit ParenExpr for unresolved member chains In order to give unresolved member chain result types visibility in the AST, we inject an implicit ParenExpr in CSGen that lives only for the duration of type checking, and gets removed during solution application. --- include/swift/AST/Expr.h | 13 +++++++++++-- lib/Sema/CSApply.cpp | 12 ++++++++++++ lib/Sema/CSGen.cpp | 28 ++++++++++++++++++++-------- lib/Sema/CSSimplify.cpp | 3 +++ 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 6e9b24921b1ff..ca8e5d1fc9164 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -2048,13 +2048,14 @@ class DotSelfExpr : public IdentityExpr { /// has tuple type, of course). class ParenExpr : public IdentityExpr { SourceLoc LParenLoc, RParenLoc; - + bool IsUnresolvedMemberChainPlaceholder; public: ParenExpr(SourceLoc lploc, Expr *subExpr, SourceLoc rploc, bool hasTrailingClosure, Type ty = Type()) : IdentityExpr(ExprKind::Paren, subExpr, ty), - LParenLoc(lploc), RParenLoc(rploc) { + LParenLoc(lploc), RParenLoc(rploc), + IsUnresolvedMemberChainPlaceholder(false) { Bits.ParenExpr.HasTrailingClosure = hasTrailingClosure; assert(lploc.isValid() == rploc.isValid() && "Mismatched source location information"); @@ -2086,6 +2087,14 @@ class ParenExpr : public IdentityExpr { return hasTrailingClosure() ? Optional(0) : None; } + bool isUnresolvedMemberChainPlaceholder() const { + return IsUnresolvedMemberChainPlaceholder; + } + + void setIsUnresolvedMemberChainPlaceholder() { + IsUnresolvedMemberChainPlaceholder = true; + } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Paren; } }; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 488fba7065882..63cde612d292e 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3259,6 +3259,18 @@ namespace { } Expr *visitParenExpr(ParenExpr *expr) { + // If this was an implicit ParenExpr inserted to give an unresolved member + // chain result type visibility in the AST (see + // ConstraintWalker::walkToExprPost), remove it from the AST now that we + // have a solution. + if (expr->isUnresolvedMemberChainPlaceholder()) { + auto *subExpr = expr->getSubExpr(); + auto type = simplifyType(cs.getType(expr)); + subExpr = coerceToType(subExpr, type, cs.getConstraintLocator(subExpr)); + cs.setType(subExpr, type); + return subExpr; + } + return simplifyExprType(expr); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 75a29f088751b..818484e8ff5c0 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1493,16 +1493,18 @@ namespace { // the base type. auto locator = CS.getConstraintLocator(tail, ConstraintLocator::UnresolvedMemberChainResult); - auto chainResultTy = CS.createTypeVariable(locator, - additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape); + auto tvo = additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape; + auto chainResultTy = CS.createTypeVariable(locator, tvo); auto chainBaseTy = UnresolvedBaseTypes.find(base)->second; // The result of this element of the chain must be convertible to the // contextual type, and the contextual type must be equal to the base. + CS.addConstraint(ConstraintKind::Conversion, resultTy, chainBaseTy, + CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, locator); CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, - CS.getConstraintLocator(base, ConstraintLocator::MemberRefBase)); + locator); return chainResultTy; } @@ -3858,17 +3860,27 @@ namespace { } if (auto type = CG.visit(expr)) { + + auto simplifiedType = CS.simplifyType(type); + + CS.setType(expr, simplifiedType); + + // If this is the end of a member chain, inject an implicit ParenExpr + // to link the contextual type to the (implicit) base of the chain. if (isMemberChainTail(CG.getConstraintSystem(), expr)) { auto *chainBase = getMemberChainBase(expr); if (auto *UME = dyn_cast(chainBase)) { - type = CG.addUnresolvedMemberChainConstraints(UME, expr, type); + auto chainTy = CG.addUnresolvedMemberChainConstraints(UME, expr, + simplifiedType); + auto *PE = new (CS.getASTContext()) ParenExpr(SourceLoc(), expr, + SourceLoc(), false); + PE->setImplicit(); + PE->setIsUnresolvedMemberChainPlaceholder(); + CS.setType(PE, chainTy); + return PE; } } - auto simplifiedType = CS.simplifyType(type); - - CS.setType(expr, simplifiedType); - return expr; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 922e25bdc45cf..d24656ca0497e 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4306,6 +4306,9 @@ bool ConstraintSystem::repairFailures( break; } + case ConstraintLocator::RValueAdjustment: + return true; + case ConstraintLocator::UnresolvedMemberChainResult: { if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) From f5845666e6a59528ece6c943e281dbe9125faead Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 18:02:26 -0400 Subject: [PATCH 044/120] [AST] Introduce UnresolvedMemberChainResultExpr Introduce a new expression type for representing the result of an unresolved member chain. Use this expression type instead of an implicit ParenExpr for giving unresolved member chain result types representation in the AST during type checking. --- include/swift/AST/Expr.h | 28 ++++++++++++++++++---------- include/swift/AST/ExprNodes.def | 3 ++- lib/AST/ASTDumper.cpp | 7 ++++++- lib/AST/Expr.cpp | 2 ++ lib/Sema/CSApply.cpp | 25 +++++++++++++------------ lib/Sema/CSGen.cpp | 10 ++++------ 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index ca8e5d1fc9164..7ce334aba0771 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -2048,14 +2048,13 @@ class DotSelfExpr : public IdentityExpr { /// has tuple type, of course). class ParenExpr : public IdentityExpr { SourceLoc LParenLoc, RParenLoc; - bool IsUnresolvedMemberChainPlaceholder; + public: ParenExpr(SourceLoc lploc, Expr *subExpr, SourceLoc rploc, bool hasTrailingClosure, Type ty = Type()) : IdentityExpr(ExprKind::Paren, subExpr, ty), - LParenLoc(lploc), RParenLoc(rploc), - IsUnresolvedMemberChainPlaceholder(false) { + LParenLoc(lploc), RParenLoc(rploc) { Bits.ParenExpr.HasTrailingClosure = hasTrailingClosure; assert(lploc.isValid() == rploc.isValid() && "Mismatched source location information"); @@ -2087,15 +2086,24 @@ class ParenExpr : public IdentityExpr { return hasTrailingClosure() ? Optional(0) : None; } - bool isUnresolvedMemberChainPlaceholder() const { - return IsUnresolvedMemberChainPlaceholder; - } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Paren; } +}; - void setIsUnresolvedMemberChainPlaceholder() { - IsUnresolvedMemberChainPlaceholder = true; - } +/// Represents the result of a chain of accesses or calls hanging off of an +/// \c UnresolvedMemberExpr at the root. This is only used during type checking +/// to give the result type of such a chain representation in the AST. This +/// expression type is always implicit. +class UnresolvedMemberChainResultExpr : public IdentityExpr { +public: + UnresolvedMemberChainResultExpr(Expr *subExpr, Type ty = Type()) + : IdentityExpr(ExprKind::UnresolvedMemberChainResult, subExpr, ty, + /*isImplicit=*/true) {} - static bool classof(const Expr *E) { return E->getKind() == ExprKind::Paren; } + SWIFT_FORWARD_SOURCE_LOCS_TO(getSubExpr()) + + static bool classof(const Expr *E) { + return E->getKind() == ExprKind::UnresolvedMemberChainResult; + } }; /// AwaitExpr - An 'await' surrounding an expression, marking that the diff --git a/include/swift/AST/ExprNodes.def b/include/swift/AST/ExprNodes.def index 53ee2da310a42..7c98a83c0afd9 100644 --- a/include/swift/AST/ExprNodes.def +++ b/include/swift/AST/ExprNodes.def @@ -104,7 +104,8 @@ ABSTRACT_EXPR(Identity, Expr) EXPR(Paren, IdentityExpr) EXPR(DotSelf, IdentityExpr) EXPR(Await, IdentityExpr) - EXPR_RANGE(Identity, Paren, Await) + EXPR(UnresolvedMemberChainResult, IdentityExpr) + EXPR_RANGE(Identity, Paren, UnresolvedMemberChainResult) ABSTRACT_EXPR(AnyTry, Expr) EXPR(Try, AnyTryExpr) EXPR(ForceTry, AnyTryExpr) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index d8352e9c62e2c..fa9efd6ba4c2f 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2135,7 +2135,12 @@ class PrintExpr : public ExprVisitor { printRec(E->getSubExpr()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } - + void visitUnresolvedMemberChainResultExpr(UnresolvedMemberChainResultExpr *E){ + printCommon(E, "unresolved_member_chain_expr"); + OS << '\n'; + printRec(E->getSubExpr()); + PrintWithColorRAII(OS, ParenthesisColor) << ')'; + } void visitTupleExpr(TupleExpr *E) { printCommon(E, "tuple_expr"); if (E->hasTrailingClosure()) diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 95a290a0d303c..113b8e499a136 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -288,6 +288,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const { return cast(this) ->getSubExpr()->getReferencedDecl(stopAtParenExpr); + PASS_THROUGH_REFERENCE(UnresolvedMemberChainResult, getSubExpr); PASS_THROUGH_REFERENCE(DotSelf, getSubExpr); PASS_THROUGH_REFERENCE(Await, getSubExpr); PASS_THROUGH_REFERENCE(Try, getSubExpr); @@ -606,6 +607,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const { case ExprKind::Paren: case ExprKind::DotSelf: + case ExprKind::UnresolvedMemberChainResult: case ExprKind::Tuple: case ExprKind::Array: case ExprKind::Dictionary: diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 63cde612d292e..f20d43efe7c5a 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3259,21 +3259,22 @@ namespace { } Expr *visitParenExpr(ParenExpr *expr) { - // If this was an implicit ParenExpr inserted to give an unresolved member - // chain result type visibility in the AST (see - // ConstraintWalker::walkToExprPost), remove it from the AST now that we - // have a solution. - if (expr->isUnresolvedMemberChainPlaceholder()) { - auto *subExpr = expr->getSubExpr(); - auto type = simplifyType(cs.getType(expr)); - subExpr = coerceToType(subExpr, type, cs.getConstraintLocator(subExpr)); - cs.setType(subExpr, type); - return subExpr; - } - return simplifyExprType(expr); } + Expr *visitUnresolvedMemberChainResultExpr( + UnresolvedMemberChainResultExpr *expr) { + // Since this expression only exists to give the result type of an + // unresolved member chain visibility in the AST, remove it from the AST + // now that we have a solution and coerce the subexpr to the resulting + // type. + auto *subExpr = expr->getSubExpr(); + auto type = simplifyType(cs.getType(expr)); + subExpr = coerceToType(subExpr, type, cs.getConstraintLocator(subExpr)); + cs.setType(subExpr, type); + return subExpr; + } + Expr *visitTupleExpr(TupleExpr *expr) { return simplifyExprType(expr); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 818484e8ff5c0..ab1e145afa0be 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3872,12 +3872,10 @@ namespace { if (auto *UME = dyn_cast(chainBase)) { auto chainTy = CG.addUnresolvedMemberChainConstraints(UME, expr, simplifiedType); - auto *PE = new (CS.getASTContext()) ParenExpr(SourceLoc(), expr, - SourceLoc(), false); - PE->setImplicit(); - PE->setIsUnresolvedMemberChainPlaceholder(); - CS.setType(PE, chainTy); - return PE; + auto *resultExpr = + new (CS.getASTContext()) UnresolvedMemberChainResultExpr(expr); + CS.setType(resultExpr, chainTy); + return resultExpr; } } From d78b59c8832abb5fb6239e798d29de65d657e97c Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 31 Jul 2020 22:38:23 -0400 Subject: [PATCH 045/120] [Sema] Prefer LValueType during solution scoring When comparing solutions, prefer LValueType(T) to T since we can convert lvalues to rvalues via a load during solution application. --- lib/Sema/CSRanking.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 0d45d781c6212..c4c82327be892 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -1188,6 +1188,21 @@ SolutionCompareResult ConstraintSystem::compareSolutions( continue; } + // Prefer binding to an l-value over an r-value of the same type, since we + // can load from an l-value to produce an r-value during solution + // application. + if (type1->is() != type2->is()) { + auto rValTy1 = type1->getRValueType(); + auto rValTy2 = type2->getRValueType(); + if (rValTy1->isEqual(rValTy2)) { + if (type1->is()) + ++score1; + else + ++score2; + } + continue; + } + // FIXME: // This terrible hack is in place to support equality comparisons of non- // equatable option types to 'nil'. Until we have a way to constrain a type From e0e3babcd08ca6b43238177eec3e735c76afec94 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sun, 2 Aug 2020 23:38:47 -0400 Subject: [PATCH 046/120] [Sema] Change anchor of unresolved chain result type Instead of creating the type variable for the unresolved member chain at the location of the last member, we now create it at the associated UnresolvedMemberChainResultExpr. Previously, when the final element of a chain was a ForceValueExpr, the chain result type got caught up in the logic used to allow ForceValueExprs to properly infer lvalue types. By separating the result type variable from the last member of the chain, we make sure to keep that logic focused only on ForceValueExpr. --- lib/Sema/CSGen.cpp | 13 +++++--- lib/Sema/CSRanking.cpp | 15 --------- lib/Sema/ConstraintSystem.cpp | 2 ++ test/expr/delayed-ident/member_chains.swift | 34 +++++++++++++++++++-- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index ab1e145afa0be..41f42cf4b8ead 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1475,6 +1475,7 @@ namespace { Type addUnresolvedMemberChainConstraints(UnresolvedMemberExpr *base, Expr *tail, + UnresolvedMemberChainResultExpr *result, Type resultTy) { assert(isa(tail) || isa(tail) || @@ -1491,7 +1492,7 @@ namespace { // The contextual type (represented with a new type variable) must equal // the base type. - auto locator = CS.getConstraintLocator(tail, + auto locator = CS.getConstraintLocator(result, ConstraintLocator::UnresolvedMemberChainResult); auto tvo = additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape; auto chainResultTy = CS.createTypeVariable(locator, tvo); @@ -3865,15 +3866,17 @@ namespace { CS.setType(expr, simplifiedType); - // If this is the end of a member chain, inject an implicit ParenExpr - // to link the contextual type to the (implicit) base of the chain. + // If this is the end of an unresolved member chain, inject the + // appropriate constraints to link the contextual type to the (implicit) + // base of the chain. if (isMemberChainTail(CG.getConstraintSystem(), expr)) { auto *chainBase = getMemberChainBase(expr); + auto *resultExpr = + new (CS.getASTContext()) UnresolvedMemberChainResultExpr(expr); if (auto *UME = dyn_cast(chainBase)) { auto chainTy = CG.addUnresolvedMemberChainConstraints(UME, expr, + resultExpr, simplifiedType); - auto *resultExpr = - new (CS.getASTContext()) UnresolvedMemberChainResultExpr(expr); CS.setType(resultExpr, chainTy); return resultExpr; } diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index c4c82327be892..0d45d781c6212 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -1188,21 +1188,6 @@ SolutionCompareResult ConstraintSystem::compareSolutions( continue; } - // Prefer binding to an l-value over an r-value of the same type, since we - // can load from an l-value to produce an r-value during solution - // application. - if (type1->is() != type2->is()) { - auto rValTy1 = type1->getRValueType(); - auto rValTy2 = type2->getRValueType(); - if (rValTy1->isEqual(rValTy2)) { - if (type1->is()) - ++score1; - else - ++score2; - } - continue; - } - // FIXME: // This terrible hack is in place to support equality comparisons of non- // equatable option types to 'nil'. Until we have a way to constrain a type diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 322dc620c19de..dad11b3fd25ea 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3860,6 +3860,8 @@ void constraints::simplifyLocator(ASTNode &anchor, } case ConstraintLocator::UnresolvedMemberChainResult: { + auto *resultExpr = getAsExpr(anchor); + anchor = resultExpr->getSubExpr(); path = path.slice(1); continue; } diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index dd13fdca5ddca..7077c37e2ee87 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -26,6 +26,10 @@ struct ImplicitMembers: Equatable { static var superOptional: ImplicitMembers??? = ImplicitMembers() var another: ImplicitMembers { ImplicitMembers() } + var anotherMutable: ImplicitMembers { + get { ImplicitMembers() } + set {} + } func getAnother() -> ImplicitMembers { ImplicitMembers() @@ -36,6 +40,10 @@ struct ImplicitMembers: Equatable { } var anotherOptional: ImplicitMembers? { ImplicitMembers() } + var anotherOptionalMutable: ImplicitMembers? { + get { ImplicitMembers() } + set {} + } func getAnotherOptional() -> ImplicitMembers? { ImplicitMembers() @@ -45,8 +53,14 @@ struct ImplicitMembers: Equatable { ImplicitMembers() } - subscript(arg: Void) -> ImplicitMembers { ImplicitMembers() } - subscript(optional arg: Void) -> ImplicitMembers? { ImplicitMembers() } + subscript(arg: Void) -> ImplicitMembers { + get { ImplicitMembers() } + set {} + } + subscript(optional arg: Void) -> ImplicitMembers? { + get { ImplicitMembers() } + set {} + } subscript(func arg: Void) -> (() -> ImplicitMembers) { { ImplicitMembers() } } subscript(funcOptional arg: Void) -> (() -> ImplicitMembers?) { { ImplicitMembers() } } subscript(optionalFunc arg: Void) -> (() -> ImplicitMembers)? { { ImplicitMembers() } } @@ -182,6 +196,22 @@ func implicit(_ i: inout ImplicitMembers) { if i == .createOptional()?.another {} } +func testLValues() { + let local = ImplicitMembers(); + + .implicit = local; + .implicit.anotherMutable = local; + .implicit.anotherOptionalMutable? = local; + .implicit.anotherOptionalMutable! = local; + .implicit[()] = local; + .implicit[()].anotherMutable = local; + .optional?[optional: ()]?.anotherOptionalMutable! = local + + // FIXME: These should probably be allowed + //.implicit.anotherOptionalMutable = local; + //.optional = local; +} + struct ImplicitGeneric { // expected-note4 {{arguments to generic parameter 'T' ('Int' and 'String') are expected to be equal}} static var implicit: ImplicitGeneric { ImplicitGeneric() } var another: ImplicitGeneric { ImplicitGeneric() } From 4331e26bcdb85350efad20cd6cd7be5ee444a829 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 3 Aug 2020 17:31:32 -0400 Subject: [PATCH 047/120] [Sema] Move UnresolvedMemberChainResultExpr creation to walkToExprPre The old method of generating this placeholder expression caused issues when constraints had to be recursively regenerated during solving. Now, we generate UnresolvedMemberChainResultExprs in walkToExprPre, and use ASTWalker::Parent instead of ConstraintSystem::getParentExpr to find the parent of the chain tail. This also lets us generate the chain constraints as part of the normal ConstraintGenerator visit, rather than as an extra walkToExprPost step. --- lib/Sema/CSDiagnostics.cpp | 7 ++ lib/Sema/CSGen.cpp | 133 +++++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 57 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 5e2b733172a91..5b1049b46e067 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3461,6 +3461,13 @@ bool MissingMemberFailure::diagnoseInLiteralCollectionContext() const { if (!(parentExpr && isa(expr))) return false; + if (!isa(parentExpr)) + return false; + + parentExpr = findParentExpr(parentExpr); + if (!parentExpr) + return false; + auto parentType = getType(parentExpr); if (!parentType->isKnownStdlibCollectionType() && !parentType->is()) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 41f42cf4b8ead..702bbc0b57ecb 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -95,11 +95,11 @@ static Expr *getMemberChainSubExpr(Expr *expr) { /// Returns the base of a chain of member accesses/method calls. Most /// expressions do not participate in member chains, and just return \c this. -static Expr *getMemberChainBase(Expr *expr) { +static UnresolvedMemberExpr *getUnresolvedMemberChainBase(Expr *expr) { if (auto *subExpr = getMemberChainSubExpr(expr)) - return getMemberChainBase(subExpr); + return getUnresolvedMemberChainBase(subExpr); else - return expr; + return dyn_cast(expr); } /// Whether this expression is a member of a member chain. @@ -107,12 +107,10 @@ static bool isMemberChainMember(Expr *expr) { return getMemberChainSubExpr(expr) != nullptr; } /// Whether this expression sits at the end of a chain of member accesses. -static bool isMemberChainTail(ConstraintSystem &cs, Expr *expr) { +static bool isMemberChainTail(Expr *expr, Expr *parent) { assert(expr && "isMemberChainTail called with null expr!"); // If this expression's parent is not itself part of a chain (or, this expr // has no parent expr), this must be the tail of the chain. - Expr *parent = cs.getParentExpr(expr); - return parent == nullptr || !isMemberChainMember(parent); } @@ -834,10 +832,14 @@ namespace { /// Keep track of acceptable DiscardAssignmentExpr's. llvm::SmallPtrSet CorrectDiscardAssignmentExprs; - /// A map from the heads to the tails of each unresolved member chain found - /// during our walk. + /// A map from each UnresolvedMemberExpr to the respective (implicit) base + /// found during our walk. llvm::MapVector UnresolvedBaseTypes; + /// A map from the tail of each unresolved member chain to the respective + /// base of the chain. + llvm::MapVectorUnresolvedChainBases; + /// Returns false and emits the specified diagnostic if the member reference /// base is a nil literal. Returns true otherwise. bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) { @@ -1473,41 +1475,20 @@ namespace { UnresolvedBaseTypes.insert({UME, ty}); } - Type addUnresolvedMemberChainConstraints(UnresolvedMemberExpr *base, - Expr *tail, - UnresolvedMemberChainResultExpr *result, - Type resultTy) { - assert(isa(tail) || - isa(tail) || - isa(tail) || - isa(tail) || - isa(tail) || - isa(tail) && - "Unexpected expression at end of unresolved member chain"); - // Copy any type variable options from the result of the tail member to - // the result of the entire chain. - unsigned additionalOptions = 0; - if (auto *tvt = resultTy->getAs()) - additionalOptions = tvt->getImpl().getRawOptions(); - - // The contextual type (represented with a new type variable) must equal - // the base type. - auto locator = CS.getConstraintLocator(result, - ConstraintLocator::UnresolvedMemberChainResult); - auto tvo = additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape; - auto chainResultTy = CS.createTypeVariable(locator, tvo); - auto chainBaseTy = UnresolvedBaseTypes.find(base)->second; + Type getUnresolvedBaseType(UnresolvedMemberExpr *UME) { + auto result = UnresolvedBaseTypes.find(UME); + assert(result != UnresolvedBaseTypes.end()); + return result->second; + } - // The result of this element of the chain must be convertible to the - // contextual type, and the contextual type must be equal to the base. - CS.addConstraint(ConstraintKind::Conversion, resultTy, chainBaseTy, - CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); - CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, - locator); - CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, - locator); + void setUnresolvedChainBase(Expr *tail, UnresolvedMemberExpr *base) { + UnresolvedChainBases.insert({tail, base}); + } - return chainResultTy; + UnresolvedMemberExpr *getUnresolvedChainBase(Expr *tail) { + auto result = UnresolvedChainBases.find(tail); + assert(result != UnresolvedChainBases.end()); + return result->second; } virtual Type visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { @@ -1573,6 +1554,46 @@ namespace { return memberTy; } + Type visitUnresolvedMemberChainResultExpr( + UnresolvedMemberChainResultExpr *expr) { + auto *tail = expr->getSubExpr(); + assert(isa(tail) || + isa(tail) || + isa(tail) || + isa(tail) || + isa(tail) || + isa(tail) && + "Unexpected expression at end of unresolved member chain"); + + auto memberTy = CS.getType(tail); + auto *base = getUnresolvedChainBase(tail); + + // Copy any type variable options from the result of the tail member to + // the result of the entire chain. + unsigned additionalOptions = 0; + if (auto *tvt = memberTy->getAs()) + additionalOptions = tvt->getImpl().getRawOptions(); + + // The contextual type (represented with a new type variable) must equal + // the base type. + auto locator = CS.getConstraintLocator(expr, + ConstraintLocator::UnresolvedMemberChainResult); + auto tvo = additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape; + auto chainResultTy = CS.createTypeVariable(locator, tvo); + auto chainBaseTy = UnresolvedBaseTypes.find(base)->second; + + // The result of this element of the chain must be convertible to the + // contextual type, and the contextual type must be equal to the base. + CS.addConstraint(ConstraintKind::Conversion, memberTy, chainBaseTy, + CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); + CS.addConstraint(ConstraintKind::Conversion, memberTy, chainResultTy, + locator); + CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, + locator); + + return chainResultTy; + } + Type visitUnresolvedDotExpr(UnresolvedDotExpr *expr) { // If this is Builtin.type_join*, just return any type and move // on since we're going to discard this, and creating any type @@ -3819,6 +3840,20 @@ namespace { if (auto *assignment = dyn_cast(expr)) CG.markAcceptableDiscardExprs(assignment->getDest()); + // If we find an unresolved member chain, wrap it in an + // UnresolvedMemberChainResultExpr (unless this has already been done) + // and generate constraints for the wrapped expression. + auto *parent = Parent.getAsExpr(); + if (isMemberChainTail(expr, parent)) { + if (auto *UME = getUnresolvedMemberChainBase(expr)) { + if (!parent || !isa(parent)) { + CG.setUnresolvedChainBase(expr, UME); + auto &context = CG.getConstraintSystem().getASTContext(); + expr = new (context) UnresolvedMemberChainResultExpr(expr); + } + } + } + return { true, expr }; } @@ -3866,22 +3901,6 @@ namespace { CS.setType(expr, simplifiedType); - // If this is the end of an unresolved member chain, inject the - // appropriate constraints to link the contextual type to the (implicit) - // base of the chain. - if (isMemberChainTail(CG.getConstraintSystem(), expr)) { - auto *chainBase = getMemberChainBase(expr); - auto *resultExpr = - new (CS.getASTContext()) UnresolvedMemberChainResultExpr(expr); - if (auto *UME = dyn_cast(chainBase)) { - auto chainTy = CG.addUnresolvedMemberChainConstraints(UME, expr, - resultExpr, - simplifiedType); - CS.setType(resultExpr, chainTy); - return resultExpr; - } - } - return expr; } From aade4aa74b4b60942e0dc612da0490e17a88e8e7 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 3 Aug 2020 17:37:37 -0400 Subject: [PATCH 048/120] [Localization] Add new diagnostics to localization file --- localization/diagnostics/en.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/localization/diagnostics/en.yaml b/localization/diagnostics/en.yaml index b346b96b115cc..b53a3154d8856 100644 --- a/localization/diagnostics/en.yaml +++ b/localization/diagnostics/en.yaml @@ -2224,6 +2224,10 @@ - id: expected_parens_in_contextual_member msg: "member %0 is a function; did you mean to call it?" +- id: expected_parens_in_contextual_member_type + msg: >- + member %0 is a function that produces expected type %1; did you mean to call it? + - id: expected_result_in_contextual_member msg: "member %0 in %2 produces result of type %1, but context expects %2" @@ -2518,6 +2522,10 @@ - id: cannot_convert_parent_type msg: "cannot convert parent type %0 to expected type %1" +- id: cannot_convert_chain_result_type + msg: >- + member chain produces result of type %0 but contextual base was inferred as %1 + - id: generic_argument_mismatch msg: "arguments to generic parameter %0 (%1 and %2) are expected to be equal" From 5394e6b2d60c56dfc5d453924f0feb5726f111f0 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 3 Aug 2020 17:39:51 -0400 Subject: [PATCH 049/120] [Sema] Use getMetatypeInstanceType instead of casting to MetatypeType --- lib/Sema/CSApply.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index f20d43efe7c5a..ca4979d01fcfa 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2761,8 +2761,7 @@ namespace { // Unresolved member lookup always happens in a metatype so dig out the // instance type. - auto metaTy = selected.choice.getBaseType()->castTo(); - auto baseTy = cs.simplifyType(metaTy->getInstanceType()); + auto baseTy = selected.choice.getBaseType()->getMetatypeInstanceType(); // The base expression is simply the metatype of the base type. // FIXME: This location info is bogus. From c33ad11ff67e95b8c788b8623aad593e96120a96 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 3 Aug 2020 17:51:28 -0400 Subject: [PATCH 050/120] [Sema] Apply clang-format --- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSGen.cpp | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index ca4979d01fcfa..53afe327a3fd3 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -3262,7 +3262,7 @@ namespace { } Expr *visitUnresolvedMemberChainResultExpr( - UnresolvedMemberChainResultExpr *expr) { + UnresolvedMemberChainResultExpr *expr) { // Since this expression only exists to give the result type of an // unresolved member chain visibility in the AST, remove it from the AST // now that we have a solution and coerce the subexpr to the resulting diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 702bbc0b57ecb..1b00dd2b72652 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1505,7 +1505,7 @@ namespace { // Since base type in this case is completely dependent on context it // should be marked as a potential hole. auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape | - TVO_CanBindToHole); + TVO_CanBindToHole); setUnresolvedBaseType(expr, baseTy); auto memberTy = CS.createTypeVariable( @@ -1555,15 +1555,13 @@ namespace { } Type visitUnresolvedMemberChainResultExpr( - UnresolvedMemberChainResultExpr *expr) { + UnresolvedMemberChainResultExpr *expr) { auto *tail = expr->getSubExpr(); - assert(isa(tail) || - isa(tail) || - isa(tail) || - isa(tail) || + assert(isa(tail) || isa(tail) || + isa(tail) || isa(tail) || isa(tail) || isa(tail) && - "Unexpected expression at end of unresolved member chain"); + "Unexpected expression at end of unresolved member chain"); auto memberTy = CS.getType(tail); auto *base = getUnresolvedChainBase(tail); @@ -1576,16 +1574,17 @@ namespace { // The contextual type (represented with a new type variable) must equal // the base type. - auto locator = CS.getConstraintLocator(expr, - ConstraintLocator::UnresolvedMemberChainResult); + auto locator = CS.getConstraintLocator( + expr, ConstraintLocator::UnresolvedMemberChainResult); auto tvo = additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape; auto chainResultTy = CS.createTypeVariable(locator, tvo); auto chainBaseTy = UnresolvedBaseTypes.find(base)->second; // The result of this element of the chain must be convertible to the // contextual type, and the contextual type must be equal to the base. - CS.addConstraint(ConstraintKind::Conversion, memberTy, chainBaseTy, - CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); + CS.addConstraint( + ConstraintKind::Conversion, memberTy, chainBaseTy, + CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); CS.addConstraint(ConstraintKind::Conversion, memberTy, chainResultTy, locator); CS.addConstraint(ConstraintKind::Equal, chainBaseTy, chainResultTy, From 1e27f26b0126ef5c5b8af32d948c6723d73aa315 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 3 Aug 2020 18:12:38 -0400 Subject: [PATCH 051/120] [Sema] Always cache the unresolved chain base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even when we’ve already visited the expression for constraint generation (signified by the presence of an UnresolvedMemberChainResultExpr) we should still cache the chain base. --- lib/Sema/CSGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 1b00dd2b72652..0ddb8919ed0bf 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3845,8 +3845,8 @@ namespace { auto *parent = Parent.getAsExpr(); if (isMemberChainTail(expr, parent)) { if (auto *UME = getUnresolvedMemberChainBase(expr)) { + CG.setUnresolvedChainBase(expr, UME); if (!parent || !isa(parent)) { - CG.setUnresolvedChainBase(expr, UME); auto &context = CG.getConstraintSystem().getASTContext(); expr = new (context) UnresolvedMemberChainResultExpr(expr); } From ed941314fff2815621b96945b749d43000ec9bf1 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 4 Aug 2020 10:29:00 -0400 Subject: [PATCH 052/120] [Sema] Bail out early of solution application for UME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …when the deduced type is unresolved. Otherwise, we won’t be able to fetch the overload choice or do anything else useful. --- lib/Sema/CSApply.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 53afe327a3fd3..56f5154ebe4be 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2753,6 +2753,15 @@ namespace { } Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { + // If constraint solving resolved this to an UnresolvedType, then we're in + // an ambiguity tolerant mode used for diagnostic generation. Just leave + // this as an unresolved member reference. + Type resultTy = simplifyType(cs.getType(expr)); + if (resultTy->getRValueType()->is()) { + cs.setType(expr, resultTy); + return expr; + } + auto &ctx = cs.getASTContext(); // Find the selected member and base type. auto memberLocator = cs.getConstraintLocator( @@ -2762,6 +2771,7 @@ namespace { // Unresolved member lookup always happens in a metatype so dig out the // instance type. auto baseTy = selected.choice.getBaseType()->getMetatypeInstanceType(); + baseTy = simplifyType(baseTy); // The base expression is simply the metatype of the base type. // FIXME: This location info is bogus. @@ -2805,8 +2815,7 @@ namespace { diagnoseAmbiguousNominalMember(baseTy, result); } - return coerceToType(result, simplifyType(cs.getType(expr)), - cs.getConstraintLocator(expr)); + return coerceToType(result, resultTy, cs.getConstraintLocator(expr)); } /// Diagnose if the base type is optional, we're referring to a nominal From 26f0a805994bbcf3d65904860a72f9a336df8976 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 4 Aug 2020 10:30:40 -0400 Subject: [PATCH 053/120] [Sema] Transfer contextual type info when we inject UnresolvedMemberChainResultExpr If we create an UnresolvedMemberChainResultExpr at the top level, we may end up losing contextual type information that was already attached to the end of the chain. To avoid this, we fetch any existing contextual type info and transfer it to the newly injected expression. --- lib/Sema/CSGen.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 0ddb8919ed0bf..b2537c17eeaa3 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3847,8 +3847,14 @@ namespace { if (auto *UME = getUnresolvedMemberChainBase(expr)) { CG.setUnresolvedChainBase(expr, UME); if (!parent || !isa(parent)) { - auto &context = CG.getConstraintSystem().getASTContext(); + auto &cs = CG.getConstraintSystem(); + auto &context = cs.getASTContext(); + auto typeInfo = cs.getContextualTypeInfo(expr); expr = new (context) UnresolvedMemberChainResultExpr(expr); + if (typeInfo) { + cs.setContextualType(expr, (*typeInfo).typeLoc, + (*typeInfo).purpose); + } } } } From 74d004057e868d9e25071fa4b613a4d1b1c46245 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 4 Aug 2020 15:08:28 -0400 Subject: [PATCH 054/120] [Sema] Remove unnecessary impact adjustment --- lib/Sema/CSSimplify.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index d24656ca0497e..e051d9934bd2a 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -10060,12 +10060,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( impact = 10; } - if (locator->isLastElement()) { - // If this is a contextual failure for an unresolved member, then increase - // the impact to attempt other fixes first and avoid ambiguity. - impact = 2; - } - if (recordFix(fix, impact)) return SolutionKind::Error; From 189235678f0f007369d4207adbd0686f28832b9c Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 4 Aug 2020 18:43:38 -0400 Subject: [PATCH 055/120] [Sema] Move member chain transformation into precheck Move the analysis and transformation of unresolved member chains into the PreCheckExpression pass in order to make sure that contextual types get hooked up properly before we actually begin generating constraints. --- lib/Sema/CSDiagnostics.cpp | 10 +++- lib/Sema/CSGen.cpp | 76 +------------------------------ lib/Sema/TypeCheckConstraints.cpp | 46 +++++++++++++++++++ lib/Sema/TypeChecker.h | 5 ++ 4 files changed, 61 insertions(+), 76 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 5b1049b46e067..17e88bbf957e8 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1131,6 +1131,8 @@ void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt( bool needsParensInside = exprNeedsParensBeforeAddingNilCoalescing(DC, const_cast(expr)); auto parentExpr = findParentExpr(anchor); + if (parentExpr && isa(parentExpr)) + parentExpr = findParentExpr(parentExpr); bool needsParensOutside = exprNeedsParensAfterAddingNilCoalescing( DC, const_cast(expr), parentExpr); @@ -6325,6 +6327,12 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { // Member reference could be wrapped into a number of parens // e.g. `((.foo))`. auto *parentExpr = findParentExpr(anchor); + UnresolvedMemberChainResultExpr *resultExpr = nullptr; + if (parentExpr && isa(parentExpr)) { + resultExpr = cast(parentExpr); + parentExpr = findParentExpr(parentExpr); + } + do { // If we have found something which isn't a paren let's stop, // otherwise let's keep unwrapping until there are either no @@ -6333,7 +6341,7 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { break; } while ((parentExpr = findParentExpr(parentExpr))); - auto diagnostic = parentExpr || getContextualType(anchor) + auto diagnostic = parentExpr || (resultExpr && getContextualType(resultExpr)) ? diag::cannot_infer_base_of_unresolved_member : diag::unresolved_member_no_inference; diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index b2537c17eeaa3..a94584d47f506 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -74,46 +74,6 @@ static bool mergeRepresentativeEquivalenceClasses(ConstraintSystem &CS, return false; } -/// Find the next element in a chain of members. If this expression is (or -/// could be) the base of such a chain, this will return \c nullptr. -static Expr *getMemberChainSubExpr(Expr *expr) { - assert(expr && "getMemberChainSubExpr called with null expr!"); - if (auto *UDE = dyn_cast(expr)) { - return UDE->getBase(); - } else if (auto *CE = dyn_cast(expr)) { - return CE->getFn(); - } else if (auto *BOE = dyn_cast(expr)) { - return BOE->getSubExpr(); - } else if (auto *FVE = dyn_cast(expr)) { - return FVE->getSubExpr(); - } else if (auto *SE = dyn_cast(expr)) { - return SE->getBase(); - } else { - return nullptr; - } -} - -/// Returns the base of a chain of member accesses/method calls. Most -/// expressions do not participate in member chains, and just return \c this. -static UnresolvedMemberExpr *getUnresolvedMemberChainBase(Expr *expr) { - if (auto *subExpr = getMemberChainSubExpr(expr)) - return getUnresolvedMemberChainBase(subExpr); - else - return dyn_cast(expr); -} - -/// Whether this expression is a member of a member chain. -static bool isMemberChainMember(Expr *expr) { - return getMemberChainSubExpr(expr) != nullptr; -} -/// Whether this expression sits at the end of a chain of member accesses. -static bool isMemberChainTail(Expr *expr, Expr *parent) { - assert(expr && "isMemberChainTail called with null expr!"); - // If this expression's parent is not itself part of a chain (or, this expr - // has no parent expr), this must be the tail of the chain. - return parent == nullptr || !isMemberChainMember(parent); -} - namespace { /// Internal struct for tracking information about types within a series @@ -836,10 +796,6 @@ namespace { /// found during our walk. llvm::MapVector UnresolvedBaseTypes; - /// A map from the tail of each unresolved member chain to the respective - /// base of the chain. - llvm::MapVectorUnresolvedChainBases; - /// Returns false and emits the specified diagnostic if the member reference /// base is a nil literal. Returns true otherwise. bool isValidBaseOfMemberRef(Expr *base, Diag<> diagnostic) { @@ -1480,16 +1436,6 @@ namespace { assert(result != UnresolvedBaseTypes.end()); return result->second; } - - void setUnresolvedChainBase(Expr *tail, UnresolvedMemberExpr *base) { - UnresolvedChainBases.insert({tail, base}); - } - - UnresolvedMemberExpr *getUnresolvedChainBase(Expr *tail) { - auto result = UnresolvedChainBases.find(tail); - assert(result != UnresolvedChainBases.end()); - return result->second; - } virtual Type visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { auto baseLocator = CS.getConstraintLocator( @@ -1564,7 +1510,7 @@ namespace { "Unexpected expression at end of unresolved member chain"); auto memberTy = CS.getType(tail); - auto *base = getUnresolvedChainBase(tail); + auto *base = TypeChecker::getUnresolvedMemberChainBase(tail); // Copy any type variable options from the result of the tail member to // the result of the entire chain. @@ -3839,26 +3785,6 @@ namespace { if (auto *assignment = dyn_cast(expr)) CG.markAcceptableDiscardExprs(assignment->getDest()); - // If we find an unresolved member chain, wrap it in an - // UnresolvedMemberChainResultExpr (unless this has already been done) - // and generate constraints for the wrapped expression. - auto *parent = Parent.getAsExpr(); - if (isMemberChainTail(expr, parent)) { - if (auto *UME = getUnresolvedMemberChainBase(expr)) { - CG.setUnresolvedChainBase(expr, UME); - if (!parent || !isa(parent)) { - auto &cs = CG.getConstraintSystem(); - auto &context = cs.getASTContext(); - auto typeInfo = cs.getContextualTypeInfo(expr); - expr = new (context) UnresolvedMemberChainResultExpr(expr); - if (typeInfo) { - cs.setContextualType(expr, (*typeInfo).typeLoc, - (*typeInfo).purpose); - } - } - } - } - return { true, expr }; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 7eb6616d9a7d4..45fcb5f00dc24 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -477,6 +477,44 @@ static bool findNonMembers(ArrayRef lookupResults, return AllDeclRefs; } +/// Find the next element in a chain of members. If this expression is (or +/// could be) the base of such a chain, this will return \c nullptr. +static Expr *getMemberChainSubExpr(Expr *expr) { + assert(expr && "getMemberChainSubExpr called with null expr!"); + if (auto *UDE = dyn_cast(expr)) { + return UDE->getBase(); + } else if (auto *CE = dyn_cast(expr)) { + return CE->getFn(); + } else if (auto *BOE = dyn_cast(expr)) { + return BOE->getSubExpr(); + } else if (auto *FVE = dyn_cast(expr)) { + return FVE->getSubExpr(); + } else if (auto *SE = dyn_cast(expr)) { + return SE->getBase(); + } else { + return nullptr; + } +} + +UnresolvedMemberExpr *TypeChecker::getUnresolvedMemberChainBase(Expr *expr) { + if (auto *subExpr = getMemberChainSubExpr(expr)) + return getUnresolvedMemberChainBase(subExpr); + else + return dyn_cast(expr); +} + +/// Whether this expression is a member of a member chain. +static bool isMemberChainMember(Expr *expr) { + return getMemberChainSubExpr(expr) != nullptr; +} +/// Whether this expression sits at the end of a chain of member accesses. +static bool isMemberChainTail(Expr *expr, Expr *parent) { + assert(expr && "isMemberChainTail called with null expr!"); + // If this expression's parent is not itself part of a chain (or, this expr + // has no parent expr), this must be the tail of the chain. + return parent == nullptr || !isMemberChainMember(parent); +} + /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. @@ -1326,6 +1364,14 @@ namespace { if (auto *simplified = simplifyTypeConstructionWithLiteralArg(expr)) return simplified; + // If we find an unresolved member chain, wrap it in an + // UnresolvedMemberChainResultExpr (unless this has already been done). + auto *parent = Parent.getAsExpr(); + if (isMemberChainTail(expr, parent)) + if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) + if (!parent || !isa(parent)) + return new (ctx) UnresolvedMemberChainResultExpr(expr); + return expr; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 06e0786c18383..ddc5711d4ed5e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1207,6 +1207,11 @@ bool requirePointerArgumentIntrinsics(ASTContext &ctx, SourceLoc loc); /// Require that the library intrinsics for creating /// array literals exist. bool requireArrayLiteralIntrinsics(ASTContext &ctx, SourceLoc loc); + +/// Gets the \c UnresolvedMemberExpr at the base of a chain of member accesses. +/// If \c expr is not part of a member chain or the base is something other than +/// an \c UnresolvedMemberExpr, \c nullptr is returned. +UnresolvedMemberExpr *getUnresolvedMemberChainBase(Expr *expr); }; // namespace TypeChecker /// Temporary on-stack storage and unescaping for encoded diagnostic From 7b05352cf1715d1c4b79de6a2585ddc34c056d66 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 5 Aug 2020 11:42:04 -0400 Subject: [PATCH 056/120] [Sema] Improve invalid lvalue diagnostics for UnresolvedMemberExpr We previously were not properly handling the diagnostics for using an rvalue implicit member on the left hand side of an assignment. This adds the proper handling and extends it for member chains. --- lib/Sema/CSDiagnostics.cpp | 22 +++++++++++++++++++++ lib/Sema/CSSimplify.cpp | 3 +++ test/expr/delayed-ident/member_chains.swift | 20 ++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 17e88bbf957e8..1abc4d61e6d6a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1314,6 +1314,11 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { if (getContextualTypePurpose(diagExpr) == CTP_Condition) return false; + // If the failure happened at the end of an unresolved member chain, it should + // be diagnosed instead as though it happened at the last element. + if (auto chainExpr = dyn_cast(diagExpr)) + diagExpr = chainExpr->getSubExpr(); + if (auto assignExpr = dyn_cast(diagExpr)) { // Let's check whether this is an attempt to assign // variable or property to itself. @@ -1437,6 +1442,11 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { } } else if (isa(diagExpr)) { subElementDiagID = diag::assignment_subscript_has_immutable_base; + } else if (auto *UME = dyn_cast(diagExpr)) { + if (UME->hasArguments()) + subElementDiagID = diag::assignment_lhs_is_apply_expression; + else + subElementDiagID = diag::assignment_lhs_is_immutable_property; } else { subElementDiagID = diag::assignment_lhs_is_immutable_variable; } @@ -1903,6 +1913,18 @@ AssignmentFailure::resolveImmutableBase(Expr *expr) const { return resolveImmutableBase(MRE->getBase()); } + if (auto *UME = dyn_cast(expr)) { + auto loc = getConstraintLocator(UME, ConstraintLocator::UnresolvedMember); + auto member = getMemberRef(loc); + + // If we can resolve a member, we can determine whether it is settable in + // this context. + if (member && member->isDecl() && isImmutable(member->getDecl())) + return {expr, member}; + else + return {expr, None}; + } + if (auto *DRE = dyn_cast(expr)) return {expr, OverloadChoice(Type(), DRE->getDecl(), FunctionRefKind::Unapplied)}; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index e051d9934bd2a..b252b1773756f 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4314,6 +4314,9 @@ bool ConstraintSystem::repairFailures( locator)) break; + if (repairByTreatingRValueAsLValue(lhs, rhs)) + break; + // If there is a type mismatch here it's contextual e.g., // `let x: E = .foo(42)`, where `.foo` is a member of `E` // but produces an incorrect type. diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index 7077c37e2ee87..27d84ea831abb 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -15,6 +15,8 @@ struct ImplicitMembers: Equatable { } static var implicit = ImplicitMembers() + static let implicitLet = ImplicitMembers() // expected-note2 {{change 'let' to 'var' to make it mutable}} + static var implicitImmutable: ImplicitMembers { ImplicitMembers() } static func createImplicit() -> ImplicitMembers { ImplicitMembers() } @@ -61,6 +63,7 @@ struct ImplicitMembers: Equatable { get { ImplicitMembers() } set {} } + subscript(immutable arg: Void) -> ImplicitMembers { ImplicitMembers() } subscript(func arg: Void) -> (() -> ImplicitMembers) { { ImplicitMembers() } } subscript(funcOptional arg: Void) -> (() -> ImplicitMembers?) { { ImplicitMembers() } } subscript(optionalFunc arg: Void) -> (() -> ImplicitMembers)? { { ImplicitMembers() } } @@ -205,7 +208,22 @@ func testLValues() { .implicit.anotherOptionalMutable! = local; .implicit[()] = local; .implicit[()].anotherMutable = local; - .optional?[optional: ()]?.anotherOptionalMutable! = local + .optional?[optional: ()]?.anotherOptionalMutable! = local; + + .implicitLet = local; // expected-error {{cannot assign to property: 'implicitLet' is a 'let' constant}} + .implicitImmutable = local; // expected-error {{cannot assign to property: 'implicitImmutable' is a get-only property}} + .createImplicit() = local; // expected-error {{cannot assign to value: 'createImplicit' is a method}} + .implicit.another = local; // expected-error {{cannot assign to property: 'another' is a get-only property}} + .implicit[immutable: ()] = local; // expected-error {{cannot assign through subscript: subscript is get-only}} + .implicit.getAnother() = local; // expected-error {{expression is not assignable: function call returns immutable value}} + + .implicitLet.anotherMutable = local; // expected-error {{cannot assign to property: 'implicitLet' is a 'let' constant}} + .implicitImmutable.anotherMutable = local; // expected-error {{cannot assign to property: 'implicitImmutable' is a get-only property}} + .createImplicit().anotherMutable = local; // expected-error {{cannot assign to value: 'createImplicit' is a method}} + .implicit.another.anotherMutable = local; // expected-error {{cannot assign to property: 'another' is a get-only property}} + .implicit[immutable: ()].anotherMutable = local; // expected-error {{cannot assign to property: subscript is get-only}} + .implicit.getAnother().anotherMutable = local; // expected-error {{cannot assign to property: function call returns immutable value}} + // FIXME: These should probably be allowed //.implicit.anotherOptionalMutable = local; From 00555016704211e6d7ba7f12e675974f551b561c Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 6 Aug 2020 15:44:25 -0400 Subject: [PATCH 057/120] [Sema] Add FIXME for Optional binding inference bug --- lib/Sema/CSGen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index a94584d47f506..7804cd6c7fe4b 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1512,6 +1512,7 @@ namespace { auto memberTy = CS.getType(tail); auto *base = TypeChecker::getUnresolvedMemberChainBase(tail); + // FIXME: This is a workaround for SR-13357, should not be necessary. // Copy any type variable options from the result of the tail member to // the result of the entire chain. unsigned additionalOptions = 0; From 5de23f5cfc9d462b59095fb6ede9f6aa4744cb04 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 12 Aug 2020 17:00:07 -0400 Subject: [PATCH 058/120] [Sema] Rebase and address feedback for implicit member chains --- include/swift/AST/DiagnosticsSema.def | 6 ++-- include/swift/AST/Expr.h | 12 ++++++-- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSDiagnostics.cpp | 14 ++-------- lib/Sema/CSGen.cpp | 31 ++++++--------------- lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/TypeCheckConstraints.cpp | 2 +- test/expr/delayed-ident/member_chains.swift | 4 +-- 8 files changed, 31 insertions(+), 42 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index e2176f6bd6c26..55e30d4d1b338 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -113,7 +113,8 @@ ERROR(expected_argument_in_contextual_member,none, ERROR(expected_parens_in_contextual_member,none, "member %0 is a function; did you mean to call it?", (DeclName)) ERROR(expected_parens_in_contextual_member_type,none, - "member %0 is a function that produces expected type %1; did you mean to call it?", (DeclName, Type)) + "member %0 is a function that produces expected type %1; did you mean to " + "call it?", (DeclName, Type)) ERROR(expected_result_in_contextual_member,none, "member %0 in %2 produces result of type %1, but context expects %2", @@ -456,7 +457,8 @@ ERROR(cannot_convert_parent_type,none, "cannot convert parent type %0 to expected type %1", (Type, Type)) ERROR(cannot_convert_chain_result_type,none, - "member chain produces result of type %0 but contextual base was inferred as %1", + "member chain produces result of type %0 but contextual base was " + "inferred as %1", (Type, Type)) NOTE(generic_argument_mismatch,none, diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 7ce334aba0771..d292a00d17fbf 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -2094,10 +2094,18 @@ class ParenExpr : public IdentityExpr { /// to give the result type of such a chain representation in the AST. This /// expression type is always implicit. class UnresolvedMemberChainResultExpr : public IdentityExpr { + /// The base of this chain of member accesses. + UnresolvedMemberExpr *ChainBase; public: - UnresolvedMemberChainResultExpr(Expr *subExpr, Type ty = Type()) + UnresolvedMemberChainResultExpr(Expr *subExpr, UnresolvedMemberExpr *base, + Type ty = Type()) : IdentityExpr(ExprKind::UnresolvedMemberChainResult, subExpr, ty, - /*isImplicit=*/true) {} + /*isImplicit=*/true), + ChainBase(base) { + assert(base); + } + + UnresolvedMemberExpr *getChainBase() const { return ChainBase; } SWIFT_FORWARD_SOURCE_LOCS_TO(getSubExpr()) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 56f5154ebe4be..bcbb363713730 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2757,7 +2757,7 @@ namespace { // an ambiguity tolerant mode used for diagnostic generation. Just leave // this as an unresolved member reference. Type resultTy = simplifyType(cs.getType(expr)); - if (resultTy->getRValueType()->is()) { + if (resultTy->hasUnresolvedType()) { cs.setType(expr, resultTy); return expr; } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 1abc4d61e6d6a..066da31ad5341 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2160,17 +2160,9 @@ bool ContextualFailure::diagnoseAsError() { case ConstraintLocator::UnresolvedMemberChainResult: { auto &solution = getSolution(); - auto member = anchor; - if (auto *CE = getAsExpr(anchor)) - member = CE->getFn(); - - auto kind = ConstraintLocator::Member; - if (isExpr(anchor)) - kind = ConstraintLocator::UnresolvedMember; - else if (isExpr(anchor)) - kind = ConstraintLocator::SubscriptMember; - auto overload = getOverloadChoiceIfAvailable(getConstraintLocator(member, - kind)); + + auto overload = + getCalleeOverloadChoiceIfAvailable(getConstraintLocator(anchor)); if (!(overload && overload->choice.isDecl())) return false; diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 7804cd6c7fe4b..f03ec3297463a 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1503,32 +1503,20 @@ namespace { Type visitUnresolvedMemberChainResultExpr( UnresolvedMemberChainResultExpr *expr) { auto *tail = expr->getSubExpr(); - assert(isa(tail) || isa(tail) || - isa(tail) || isa(tail) || - isa(tail) || - isa(tail) && - "Unexpected expression at end of unresolved member chain"); - auto memberTy = CS.getType(tail); - auto *base = TypeChecker::getUnresolvedMemberChainBase(tail); - - // FIXME: This is a workaround for SR-13357, should not be necessary. - // Copy any type variable options from the result of the tail member to - // the result of the entire chain. - unsigned additionalOptions = 0; - if (auto *tvt = memberTy->getAs()) - additionalOptions = tvt->getImpl().getRawOptions(); + auto *base = expr->getChainBase(); + assert(base == TypeChecker::getUnresolvedMemberChainBase(tail)); - // The contextual type (represented with a new type variable) must equal - // the base type. + // The result type of the chain is is represented by a new type variable. auto locator = CS.getConstraintLocator( expr, ConstraintLocator::UnresolvedMemberChainResult); - auto tvo = additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape; - auto chainResultTy = CS.createTypeVariable(locator, tvo); - auto chainBaseTy = UnresolvedBaseTypes.find(base)->second; + auto chainResultTy = CS.createTypeVariable( + locator, + TVO_CanBindToLValue | TVO_CanBindToHole | TVO_CanBindToNoEscape); + auto chainBaseTy = getUnresolvedBaseType(base); - // The result of this element of the chain must be convertible to the - // contextual type, and the contextual type must be equal to the base. + // The result of the last element of the chain must be convertible to the + // whole chain, and the type of the whole chain must be equal to the base. CS.addConstraint( ConstraintKind::Conversion, memberTy, chainBaseTy, CS.getConstraintLocator(tail, ConstraintLocator::RValueAdjustment)); @@ -3828,7 +3816,6 @@ namespace { } if (auto type = CG.visit(expr)) { - auto simplifiedType = CS.simplifyType(type); CS.setType(expr, simplifiedType); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index dad11b3fd25ea..a933da4a6857c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3860,7 +3860,7 @@ void constraints::simplifyLocator(ASTNode &anchor, } case ConstraintLocator::UnresolvedMemberChainResult: { - auto *resultExpr = getAsExpr(anchor); + auto *resultExpr = castToExpr(anchor); anchor = resultExpr->getSubExpr(); path = path.slice(1); continue; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 45fcb5f00dc24..44bf6e22cba96 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1370,7 +1370,7 @@ namespace { if (isMemberChainTail(expr, parent)) if (auto *UME = TypeChecker::getUnresolvedMemberChainBase(expr)) if (!parent || !isa(parent)) - return new (ctx) UnresolvedMemberChainResultExpr(expr); + return new (ctx) UnresolvedMemberChainResultExpr(expr, UME); return expr; } diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index 27d84ea831abb..ed694ab8ad98c 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -126,8 +126,8 @@ let _: ImplicitMembers = .implicit[optional: ()] // expected-error {{value of op let _: ImplicitMembers = .implicit[funcOptional: ()]() // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{55-55= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{55-55=!}} // FIXME: Improve these diagnostics (should probably offer unwrapping, as above) -let _: ImplicitMembers = .implicit.anotherOptional?.another // expected-error{{cannot infer contextual base in reference to member 'implicit'}} expected-error{{cannot convert value of type 'Optional<_>' to specified type 'ImplicitMembers'}} -let _: ImplicitMembers = .implicit[optionalFunc: ()]?() // expected-error{{cannot infer contextual base in reference to member 'implicit'}} expected-error{{cannot convert value of type 'Optional<_>' to specified type 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit.anotherOptional?.another // expected-error{{type of expression is ambiguous without more context}} +let _: ImplicitMembers = .implicit[optionalFunc: ()]?() // expected-error{{type of expression is ambiguous without more context}} let _: ImplicitMembers = .other.implicit From 968112e420eea7d7f028456c8a873141d6b2c6ba Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sun, 16 Aug 2020 23:15:01 -0400 Subject: [PATCH 059/120] [Sema] Support code completion for heterogeneous unresolved member chains --- lib/Sema/TypeCheckConstraints.cpp | 2 + test/IDE/complete_unresolved_chains.swift | 79 +++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 test/IDE/complete_unresolved_chains.swift diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 44bf6e22cba96..5c16de3953a8c 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -491,6 +491,8 @@ static Expr *getMemberChainSubExpr(Expr *expr) { return FVE->getSubExpr(); } else if (auto *SE = dyn_cast(expr)) { return SE->getBase(); + } else if (auto *CCE = dyn_cast(expr)) { + return CCE->getBase(); } else { return nullptr; } diff --git a/test/IDE/complete_unresolved_chains.swift b/test/IDE/complete_unresolved_chains.swift new file mode 100644 index 0000000000000..bd89f737905a8 --- /dev/null +++ b/test/IDE/complete_unresolved_chains.swift @@ -0,0 +1,79 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_1 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_2 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_3 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_4 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_5 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_2 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_6 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=UNRESOLVED_CHAIN_7 | %FileCheck %s -check-prefix=UNRESOLVED_CHAIN_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DOUBLY_NESTED| %FileCheck %s -check-prefix=DOUBLY_NESTED + +struct ChainStruct1 { + static var chain2 = ChainStruct2() + static func chain2Func() -> ChainStruct2 { ChainStruct2() } +} + +struct ChainStruct2 { + var chainStruct1 = ChainStruct1() + var chainEnum = ChainEnum.case1 + var chainStruct2: ChainStruct2 { ChainStruct2() } + func chainStruct1Func() -> ChainStruct1 { ChainStruct1() } +} + +enum ChainEnum { + case case1 + var chainStruct2: ChainStruct2 { ChainStruct2() } + func chainStruct2Func() -> ChainStruct2 { ChainStruct2() } +} + +func testChains() { + let _: ChainStruct1 = .chain2.#^UNRESOLVED_CHAIN_1^# + let _: ChainStruct1 = .chain2.chainStruct2.#^UNRESOLVED_CHAIN_2^# + let _: ChainStruct1 = .chain2Func().#^UNRESOLVED_CHAIN_3^# + let _: ChainStruct1 = .chain2Func().#^UNRESOLVED_CHAIN_4^# + let _: ChainEnum = .case1.#^UNRESOLVED_CHAIN_5^# + let _: ChainEnum = .case1.chainStruct2.#^UNRESOLVED_CHAIN_6^# + let _: ChainEnum = .case1.chainStruct2.#^UNRESOLVED_CHAIN_7^# +} + +// UNRESOLVED_CHAIN_1: Begin completions, 5 items +// UNRESOLVED_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: chainStruct1[#ChainStruct1#]; +// UNRESOLVED_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal: chainEnum[#ChainEnum#]; +// UNRESOLVED_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; +// UNRESOLVED_CHAIN_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Identical]: chainStruct1Func()[#ChainStruct1#]; +// UNRESOLVED_CHAIN_1: End completions + +// UNRESOLVED_CHAIN_2: Begin completions, 5 items +// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; +// UNRESOLVED_CHAIN_2: End completions + +// UNRESOLVED_CHAIN_3: Begin completions, 5 items +// UNRESOLVED_CHAIN_3-DAG: Decl[InstanceVar]/CurrNominal: chainStruct1[#ChainStruct1#]; name=chainStruct1 +// UNRESOLVED_CHAIN_3-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: chainEnum[#ChainEnum#]; name=chainEnum +// UNRESOLVED_CHAIN_3-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; name=chainStruct2 +// UNRESOLVED_CHAIN_3-DAG: Decl[InstanceMethod]/CurrNominal: chainStruct1Func()[#ChainStruct1#]; name=chainStruct1Func() +// UNRESOLVED_CHAIN_3: End completions + +class Outer { + class Inner: Outer { + class InnerInner: Inner {} + static var outer = Outer() + static var inner = Inner() + static func makeOuter() -> Outer { Outer() } + static func makeInner() -> Inner { Inner() } + } +} + +func testDoublyNestedType() { + let _: Outer = .Inner.#^DOUBLY_NESTED^# +} + +// DOUBLY_NESTED: Begin completions, 8 items +// DOUBLY_NESTED-DAG: Decl[Class]/CurrNominal/TypeRelation[Convertible]: InnerInner[#Outer.Inner.InnerInner#]; +// DOUBLY_NESTED-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: outer[#Outer#]; +// DOUBLY_NESTED-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: inner[#Outer.Inner#]; +// DOUBLY_NESTED-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: makeOuter()[#Outer#]; +// DOUBLY_NESTED-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Convertible]: makeInner()[#Outer.Inner#]; +// DOUBLY_NESTED-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#Outer.Inner#]; +// DOUBLY_NESTED-DAG: Decl[Class]/Super/TypeRelation[Convertible]: Inner[#Outer.Inner#]; +// DOUBLY_NESTED: End completions + From db33dfa3a110aa6d6dde0c5176b2d8d89afe243c Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 20 Aug 2020 22:24:53 -0400 Subject: [PATCH 060/120] [IDE] Offer unresolved member completions with non-matching types Since the user can now write additional member accesses off of an UnresolvedMemberExpr, we should offer all available completions rather than just those that match the contextual type. --- lib/IDE/CodeCompletion.cpp | 10 ++- test/IDE/complete_annotation.swift | 6 +- test/IDE/complete_assignment.swift | 6 +- test/IDE/complete_call_arg.swift | 10 ++- test/IDE/complete_call_as_function.swift | 4 +- test/IDE/complete_constrained.swift | 4 +- test/IDE/complete_enum_elements.swift | 22 +++++- ...complete_property_delegate_attribute.swift | 3 +- test/IDE/complete_stmt_controlling_expr.swift | 8 ++- test/IDE/complete_string_interpolation.swift | 6 +- test/IDE/complete_unresolved_chains.swift | 8 ++- test/IDE/complete_unresolved_members.swift | 67 +++++++++++++------ 12 files changed, 115 insertions(+), 39 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 4d14814a55103..bd5c98c9a4e20 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -4284,14 +4284,18 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { if (!T->mayHaveMembers()) return; - DeclContext *DC = const_cast(CurrDeclContext); - // We can only say .foo where foo is a static member of the contextual // type and has the same type (or if the member is a function, then the // same result type) as the contextual type. FilteredDeclConsumer consumer(*this, [=](ValueDecl *VD, DeclVisibilityKind Reason) { - return isReferenceableByImplicitMemberExpr(CurrModule, DC, T, VD); + if (T->getOptionalObjectType() && + VD->getModuleContext()->isStdlibModule()) { + // In optional context, ignore '.init()', 'init(nilLiteral:)', + if (isa(VD)) + return false; + } + return true; }); auto baseType = MetatypeType::get(T); diff --git a/test/IDE/complete_annotation.swift b/test/IDE/complete_annotation.swift index d9dde1ac6c8e5..0e267618ca87a 100644 --- a/test/IDE/complete_annotation.swift +++ b/test/IDE/complete_annotation.swift @@ -95,9 +95,13 @@ func testPostfix(value: MyStruct) { func testImplicitMember() -> MyStruct { return .#^EXPR_IMPLICITMEMBER^# } -// EXPR_IMPLICITMEMBER: Begin completions, 3 items +// EXPR_IMPLICITMEMBER: Begin completions, 7 items // EXPR_IMPLICITMEMBER-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init(x: Int); typename=MyStruct; // EXPR_IMPLICITMEMBER-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: instance; typename=MyStruct; +// EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: labelNameParamName(_ self: MyStruct); typename=(label: (inout Int) throws -> MyStruct) -> Void; +// EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: labelName(_ self: MyStruct); typename=(label: (@autoclosure () -> Int) -> Int) -> Void; +// EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: sameName(_ self: MyStruct); typename=(label: inout Int) -> Void; +// EXPR_IMPLICITMEMBER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: paramName(_ self: MyStruct); typename=(Int) -> Void; // EXPR_IMPLICITMEMBER-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: create(x: Int); typename=MyStruct; // EXPR_IMPLICITMEMBER: End completions diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 3fa5753e0b30c..871ef3db1dd2c 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -127,17 +127,19 @@ func f2() { d = .#^ASSIGN_5^# } -// ASSIGN_5: Begin completions, 2 items +// ASSIGN_5: Begin completions, 3 items // ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case2[#D1#]; name=case2 // ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case1[#D1#]; name=case1 +// ASSIGN_5-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): D1#})[#(into: inout Hasher) -> Void#]; name=hash(self: D1) func f6() { var d : D2 d = .#^ASSIGN_6^# } -// ASSIGN_6: Begin completions, 2 items +// ASSIGN_6: Begin completions, 3 items // ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case3[#D2#]; name=case3 // ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case4[#D2#]; name=case4 +// ASSIGN_6-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): D2#})[#(into: inout Hasher) -> Void#]; name=hash(self: D2) func f7 (C : C2) { var i : Int diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 978810776e125..07e5d183f7b62 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -579,10 +579,12 @@ func testTupleShuffle() { // SHUFFLE_2-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s1[#String#]; name=s1 // SHUFFLE_2-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2 -// SHUFFLE_3: Begin completions, 3 items +// SHUFFLE_3: Begin completions, 4 items // SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo // SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar // SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// SHUFFLE_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: SimpleEnum) + class HasSubscript { subscript(idx: Int) -> String {} @@ -833,10 +835,11 @@ func testPamrameterFlags(_: Int, inoutArg: inout Int, autoclosureArg: @autoclosu func testTupleElement(arg: (SimpleEnum, SimpleEnum)) { testTupleElement(arg: (.foo, .#^TUPLEELEM_1^#)) -// TUPLEELEM_1: Begin completions, 3 items +// TUPLEELEM_1: Begin completions, 4 items // TUPLEELEM_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo // TUPLEELEM_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar // TUPLEELEM_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// TUPLEELEM_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: SimpleEnum) // TUPLEELEM_1: End completions testTupleElement(arg: (.foo, .bar, .#^TUPLEELEM_2^#)) // TUPLEELEM_2-NOT: Begin completions @@ -852,10 +855,11 @@ func testKeyPathThunkInBase() { func foo(_ fn: (TestKP) -> Int) -> TestKPResult { TestKPResult() } foo(\.value).testFunc(.#^KEYPATH_THUNK_BASE^#) -// KEYPATH_THUNK_BASE: Begin completions, 3 items +// KEYPATH_THUNK_BASE: Begin completions, 4 items // KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#SimpleEnum#]; name=foo // KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#SimpleEnum#]; name=bar // KEYPATH_THUNK_BASE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#SimpleEnum#]; name=baz +// KEYPATH_THUNK_BASE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SimpleEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: SimpleEnum) // KEYPATH_THUNK_BASE: End completions } diff --git a/test/IDE/complete_call_as_function.swift b/test/IDE/complete_call_as_function.swift index 836b3a9a7d84e..ebd371b95312c 100644 --- a/test/IDE/complete_call_as_function.swift +++ b/test/IDE/complete_call_as_function.swift @@ -111,10 +111,12 @@ func testCallAsFunctionOverloaded(fn: Functor) { fn(h: .left, v: .#^OVERLOADED_ARG2_VALUE^#) // FIXME: Should only suggest 'up' and 'down' (rdar://problem/60346573). -//OVERLOADED_ARG2_VALUE: Begin completions, 4 items +//OVERLOADED_ARG2_VALUE: Begin completions, 6 items //OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: up[#Functor.Vertical#]; //OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: down[#Functor.Vertical#]; +//OVERLOADED_ARG2_VALUE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Functor.Vertical#})[#(into: inout Hasher) -> Void#]; //OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: left[#Functor.Horizontal#]; //OVERLOADED_ARG2_VALUE-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: right[#Functor.Horizontal#]; +//OVERLOADED_ARG2_VALUE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Functor.Horizontal#})[#(into: inout Hasher) -> Void#]; //OVERLOADED_ARG2_VALUE: End completions } diff --git a/test/IDE/complete_constrained.swift b/test/IDE/complete_constrained.swift index bb85a6c711c02..cc655f4af2ea2 100644 --- a/test/IDE/complete_constrained.swift +++ b/test/IDE/complete_constrained.swift @@ -117,9 +117,11 @@ struct Vegetarian: EatsFruit, EatsVegetables { } func testVegetarian(chef: Chef) { chef.cook(.#^CONDITIONAL_OVERLOAD_ARG^#) -// CONDITIONAL_OVERLOAD_ARG: Begin completions, 2 items +// CONDITIONAL_OVERLOAD_ARG: Begin completions, 4 items // CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: apple[#Fruit#]; name=apple +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Fruit#})[#(into: inout Hasher) -> Void#]; name=hash(self: Fruit) // CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: broccoli[#Vegetable#]; name=broccoli +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Vegetable#})[#(into: inout Hasher) -> Void#]; name=hash(self: Vegetable) // CONDITIONAL_OVERLOAD_ARG: End completions var chefMeta: Chef.Type = Chef.self diff --git a/test/IDE/complete_enum_elements.swift b/test/IDE/complete_enum_elements.swift index 7f4097efb0a9c..533fd73ce6515 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -138,10 +138,18 @@ enum FooEnum: CaseIterable { // FOO_ENUM_DOT_INVALID-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}} // FOO_ENUM_DOT_INVALID-NEXT: End completions -// FOO_ENUM_DOT_ELEMENTS: Begin completions, 3 items +// FOO_ENUM_DOT_ELEMENTS: Begin completions, 11 items // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Foo1[#FooEnum#]{{; name=.+$}} // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Foo2[#FooEnum#]{{; name=.+$}} // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: alias1[#FooEnum#]; name=alias1 +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: FooEnum) +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#arrayLiteral: FooEnum...#})[#Array#]; name=AllCases(arrayLiteral: FooEnum...) +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases()[#Array#]; name=AllCases() +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#(s): Sequence#})[#Array#]; name=AllCases(s: Sequence) +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#repeating: FooEnum#}, {#count: Int#})[#Array#]; name=AllCases(repeating: FooEnum, count: Int) +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#unsafeUninitializedCapacity: Int#}, {#initializingWith: (inout UnsafeMutableBufferPointer, inout Int) throws -> Void##(inout UnsafeMutableBufferPointer, inout Int) throws -> Void#})[' rethrows'][#Array#]; name=AllCases(unsafeUninitializedCapacity: Int, initializingWith: (inout UnsafeMutableBufferPointer, inout Int) throws -> Void) rethrows +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#from: Decoder#})[' throws'][#Array#]; name=AllCases(from: Decoder) throws +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]; name=allCases // FOO_ENUM_DOT_ELEMENTS-NEXT: End completions enum BarEnum { @@ -454,9 +462,19 @@ func testWithInvalid1() { func testUnqualified1(x: QuxEnum) { _ = x == .Qux1 || x == .#^UNRESOLVED_2^#Qux2 - // UNRESOLVED_2: Begin completions, 2 items + // UNRESOLVED_2: Begin completions, 12 items // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: UInt#})[#Int#]; name=RawValue(bitPattern: UInt) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#(source): Float#})[#Int#]; name=RawValue(source: Float) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#(source): Double#})[#Int#]; name=RawValue(source: Double) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#(source): Float80#})[#Int#]; name=RawValue(source: Float80) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#from: Decoder#})[' throws'][#Int#]; name=RawValue(from: Decoder) throws + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: OpaquePointer?#})[#Int#]; name=RawValue(bitPattern: OpaquePointer?) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: ObjectIdentifier#})[#Int#]; name=RawValue(bitPattern: ObjectIdentifier) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: _Pointer?#})[#Int#]; name=RawValue(bitPattern: _Pointer?) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]; name=init(rawValue: Int) + // UNRESOLVED_2-DAG: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Invalid]: hash({#(self): QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: QuxEnum) // UNRESOLVED_2: End completions _ = (x == .Qux1#^UNRESOLVED_3^#) diff --git a/test/IDE/complete_property_delegate_attribute.swift b/test/IDE/complete_property_delegate_attribute.swift index 5cf00f41e1b1b..ba1de7930993d 100644 --- a/test/IDE/complete_property_delegate_attribute.swift +++ b/test/IDE/complete_property_delegate_attribute.swift @@ -34,9 +34,10 @@ struct TestStruct { @MyStruct(arg1: .#^ARG_MyEnum_DOT^# var test3 -// ARG_MyEnum_DOT: Begin completions, 2 items +// ARG_MyEnum_DOT: Begin completions, 3 items // ARG_MyEnum_DOT-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: east[#MyEnum#]; name=east // ARG_MyEnum_DOT-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: west[#MyEnum#]; name=west +// ARG_MyEnum_DOT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#]; // ARG_MyEnum_DOT: End completions @MyStruct(arg1: MyEnum.#^ARG_MyEnum_NOBINDING^#) diff --git a/test/IDE/complete_stmt_controlling_expr.swift b/test/IDE/complete_stmt_controlling_expr.swift index b76a9bfad2f14..ccbabf4c8395f 100644 --- a/test/IDE/complete_stmt_controlling_expr.swift +++ b/test/IDE/complete_stmt_controlling_expr.swift @@ -644,11 +644,17 @@ func testGuardCase(x:FooStruct?) { // FOOSTRUCT_LOCALVAL-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: boundVal[#FooStruct#]; // FOOSTRUCT_LOCALVAL: End completions -// OPTIONAL_FOOSTRUCT: Begin completions, 5 items +// OPTIONAL_FOOSTRUCT: Begin completions, 9 items // OPTIONAL_FOOSTRUCT-DAG: Keyword[nil]/None/Erase[1]: nil[#FooStruct?#]; name=nil // OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem: none[#Optional#]; name=none // OPTIONAL_FOOSTRUCT-DAG: Decl[EnumElement]/CurrNominal/IsSystem: some({#FooStruct#})[#Optional#]; name=some(FooStruct) // FIXME: 'FooStruct' members should not be shown. // OPTIONAL_FOOSTRUCT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init()[#FooStruct#]; name=init() // OPTIONAL_FOOSTRUCT-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init({#Int#})[#FooStruct#]; name=init(Int) +// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: boolGen({#(self): FooStruct#})[#() -> Bool#]; name=boolGen(self: FooStruct) +// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: intGen({#(self): FooStruct#})[#() -> Int#]; name=intGen(self: FooStruct) +// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional#})[#((FooStruct) throws -> U) -> U?#]; name=map(self: Optional) +// OPTIONAL_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional#})[#((FooStruct) throws -> U?) -> U?#]; name=flatMap(self: Optional) +// OPTIONAL_FOOSTRUCT-NOT: init({#(some): +// OPTIONAL_FOOSTRUCT-NOT: init({#nilLiteral: // OPTIONAL_FOOSTRUCT: End completions diff --git a/test/IDE/complete_string_interpolation.swift b/test/IDE/complete_string_interpolation.swift index 4187ca51cf480..1089516beb5f1 100644 --- a/test/IDE/complete_string_interpolation.swift +++ b/test/IDE/complete_string_interpolation.swift @@ -31,17 +31,19 @@ struct MsgInterpolation: StringInterpolationProtocol { var messenger = Messenger() func testMessenger(intVal: Int, fltVal: Float) { messenger.send(" \(intVal, format: .#^OVERLOAD_INT^#) ") -// OVERLOAD_INT: Begin completions, 4 items +// OVERLOAD_INT: Begin completions, 5 items // OVERLOAD_INT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: decimal[#MsgInterpolation.IntFormat#]; // OVERLOAD_INT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: hex[#MsgInterpolation.IntFormat#]; +// OVERLOAD_INT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MsgInterpolation.IntFormat#})[#(into: inout Hasher) -> Void#]; // OVERLOAD_INT-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: precision({#Int#})[#MsgInterpolation.FloatFormat#]; // OVERLOAD_INT-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: hex[#MsgInterpolation.FloatFormat#]; // OVERLOAD_INT: End completions messenger.send(" \(fltVal, format: .#^OVERLOAD_FLT^#) ") -// OVERLOAD_FLT: Begin completions, 4 items +// OVERLOAD_FLT: Begin completions, 5 items // OVERLOAD_FLT-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: decimal[#MsgInterpolation.IntFormat#]; // OVERLOAD_FLT-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: hex[#MsgInterpolation.IntFormat#]; +// OVERLOAD_FLT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MsgInterpolation.IntFormat#})[#(into: inout Hasher) -> Void#]; // OVERLOAD_FLT-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: precision({#Int#})[#MsgInterpolation.FloatFormat#]; // OVERLOAD_FLT-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: hex[#MsgInterpolation.FloatFormat#]; // OVERLOAD_FLT: End completions diff --git a/test/IDE/complete_unresolved_chains.swift b/test/IDE/complete_unresolved_chains.swift index bd89f737905a8..f1d2930ce7e17 100644 --- a/test/IDE/complete_unresolved_chains.swift +++ b/test/IDE/complete_unresolved_chains.swift @@ -36,6 +36,7 @@ func testChains() { } // UNRESOLVED_CHAIN_1: Begin completions, 5 items +// UNRESOLVED_CHAIN_1-DAG: Keyword[self]/CurrNominal: self[#ChainStruct2#]; name=self // UNRESOLVED_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: chainStruct1[#ChainStruct1#]; // UNRESOLVED_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal: chainEnum[#ChainEnum#]; // UNRESOLVED_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; @@ -43,7 +44,11 @@ func testChains() { // UNRESOLVED_CHAIN_1: End completions // UNRESOLVED_CHAIN_2: Begin completions, 5 items -// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; +// UNRESOLVED_CHAIN_2-DAG: Keyword[self]/CurrNominal: self[#ChainEnum#]; name=self +// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceVar]/CurrNominal: chainStruct2[#ChainStruct2#]; name=chainStruct2 +// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceMethod]/CurrNominal: chainStruct2Func()[#ChainStruct2#]; name=chainStruct2Func() +// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceVar]/CurrNominal: hashValue[#Int#]; name=hashValue +// UNRESOLVED_CHAIN_2-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#into: &Hasher#})[#Void#]; name=hash(into: &Hasher) // UNRESOLVED_CHAIN_2: End completions // UNRESOLVED_CHAIN_3: Begin completions, 5 items @@ -68,6 +73,7 @@ func testDoublyNestedType() { } // DOUBLY_NESTED: Begin completions, 8 items +// DOUBLY_NESTED-DAG: Keyword[self]/CurrNominal: self[#Outer.Inner.Type#]; name=self // DOUBLY_NESTED-DAG: Decl[Class]/CurrNominal/TypeRelation[Convertible]: InnerInner[#Outer.Inner.InnerInner#]; // DOUBLY_NESTED-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: outer[#Outer#]; // DOUBLY_NESTED-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: inner[#Outer.Inner#]; diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 7a895c550c665..80d50f2c3e4f1 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -226,7 +226,8 @@ class C2 { // UNRESOLVED_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option1[#SomeOptions1#]; name=Option1 // UNRESOLVED_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option2[#SomeOptions1#]; name=Option2 // UNRESOLVED_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: Option3[#SomeOptions1#]; name=Option3 -// UNRESOLVED_1-NOT: Not +// UNRESOLVED_1-DAG: Decl[StaticVar]/CurrNominal: NotOption[#Int#]; name=NotOption +// UNRESOLVED_1-NOT: NotStaticOption // UNRESOLVED_1_NOTIDEAL: Begin completions // UNRESOLVED_1_NOTIDEAL-NOT: SomeEnum1 @@ -234,7 +235,8 @@ class C2 { // UNRESOLVED_1_NOTIDEAL-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: Option1[#SomeOptions1#]; name=Option1 // UNRESOLVED_1_NOTIDEAL-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: Option2[#SomeOptions1#]; name=Option2 // UNRESOLVED_1_NOTIDEAL-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: Option3[#SomeOptions1#]; name=Option3 -// UNRESOLVED_1_NOTIDEAL-NOT: Not +// UNRESOLVED_1_NOTIDEAL-DAG: Decl[StaticVar]/CurrNominal: NotOption[#Int#]; name=NotOption +// UNRESOLVED_1_NOTIDEAL-NOT: NotStaticOption } class C3 { @@ -274,39 +276,49 @@ class C4 { var _: SomeEnum1??? = .#^UNRESOLVED_OPT_3^# } } -// UNRESOLVED_3: Begin completions, 2 items +// UNRESOLVED_3: Begin completions, 3 items // UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: North[#SomeEnum1#]; name=North // UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: South[#SomeEnum1#]; name=South +// UNRESOLVED_3-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(self: SomeEnum1) // UNRESOLVED_3-NOT: SomeOptions1 // UNRESOLVED_3-NOT: SomeOptions2 // UNRESOLVED_3-NOT: none // UNRESOLVED_3-NOT: some( -// UNRESOLVED_3_NOTIDEAL: Begin completions, 2 items +// UNRESOLVED_3_NOTIDEAL: Begin completions, 3 items // UNRESOLVED_3_NOTIDEAL-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: North[#SomeEnum1#]; name=North // UNRESOLVED_3_NOTIDEAL-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: South[#SomeEnum1#]; name=South +// UNRESOLVED_3_NOTIDEAL-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; name=hash(self: SomeEnum1) // UNRESOLVED_3_NOTIDEAL-NOT: SomeOptions1 // UNRESOLVED_3_NOTIDEAL-NOT: SomeOptions2 // UNRESOLVED_3_NOTIDEAL-NOT: none // UNRESOLVED_3_NOTIDEAL-NOT: some( -// UNRESOLVED_3_OPT: Begin completions, 5 items +// UNRESOLVED_3_OPT: Begin completions, 9 items // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: North[#SomeEnum1#]; // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: South[#SomeEnum1#]; +// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_3_OPT-DAG: Keyword[nil]/None/Erase[1]: nil[#SomeEnum1?#]; name=nil // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem: none[#Optional#]; name=none // UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem: some({#SomeEnum1#})[#Optional#]; +// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional#})[#((SomeEnum1) throws -> U) -> U?#]; +// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional#})[#((SomeEnum1) throws -> U?) -> U?#]; // UNRESOLVED_3_OPT-NOT: init({#(some): // UNRESOLVED_3_OPT-NOT: init({#nilLiteral: +// UNRESOLVED_3_OPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional#})[#(into: inout Hasher) -> Void#]; -// UNRESOLVED_3_OPTOPTOPT: Begin completions, 5 items +// UNRESOLVED_3_OPTOPTOPT: Begin completions, 9 items // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: North[#SomeEnum1#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: South[#SomeEnum1#]; +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_3_OPTOPTOPT-DAG: Keyword[nil]/None/Erase[1]: nil[#SomeEnum1???#]; name=nil // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem: none[#Optional#]; name=none // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/CurrNominal/IsSystem: some({#SomeEnum1??#})[#Optional#]; +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional#})[#((SomeEnum1??) throws -> U) -> U?#]; +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional#})[#((SomeEnum1??) throws -> U?) -> U?#]; // UNRESOLVED_3_OPTOPTOPT-NOT: init({#(some): // UNRESOLVED_3_OPTOPTOPT-NOT: init({#nilLiteral: +// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional#})[#(into: inout Hasher) -> Void#]; enum Somewhere { case earth, mars @@ -317,14 +329,18 @@ extension Optional where Wrapped == Somewhere { } func testOptionalWithCustomExtension() { var _: Somewhere? = .#^UNRESOLVED_OPT_4^# -// UNRESOLVED_OPT_4: Begin completions, 7 items +// UNRESOLVED_OPT_4: Begin completions, 11 items // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: earth[#Somewhere#]; // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Convertible]: mars[#Somewhere#]; +// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Somewhere#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_OPT_4-DAG: Keyword[nil]/None/Erase[1]: nil[#Somewhere?#]; name=nil // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/IsSystem: none[#Optional#]; name=none // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/CurrNominal/IsSystem: some({#Somewhere#})[#Optional#]; // UNRESOLVED_OPT_4-DAG: Decl[Constructor]/CurrNominal: init({#str: String#})[#Optional#]; name=init(str: String) // UNRESOLVED_OPT_4-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: nowhere[#Optional#]; name=nowhere +// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: map({#(self): Optional#})[#((Somewhere) throws -> U) -> U?#]; +// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: flatMap({#(self): Optional#})[#((Somewhere) throws -> U?) -> U?#]; +// UNRESOLVED_OPT_4-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: hash({#(self): Optional#})[#(into: inout Hasher) -> Void#]; // UNRESOLVED_OPT_4-NOT: init({#(some): // UNRESOLVED_OPT_4-NOT: init({#nilLiteral: // UNRESOLVED_OPT_4: End completions @@ -433,10 +449,11 @@ let _: [SomeEnum3:SomeEnum1] = [.Payload(.South):.South, .Payload(.#^UNRESOLVED_ func testAvail1(_ x: EnumAvail1) { testAvail1(.#^ENUM_AVAIL_1^#) } -// ENUM_AVAIL_1: Begin completions, 2 items +// ENUM_AVAIL_1: Begin completions, 3 items // ENUM_AVAIL_1-NOT: AAA // ENUM_AVAIL_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: aaa[#EnumAvail1#]; // ENUM_AVAIL_1-DAG: Decl[EnumElement]/ExprSpecific/NotRecommended/TypeRelation[Identical]: BBB[#EnumAvail1#]; +// ENUM_AVAIL_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): EnumAvail1#})[#(into: inout Hasher) -> Void#]; // ENUM_AVAIL_1-NOT: AAA // ENUM_AVAIL_1: End completions @@ -444,11 +461,11 @@ func testAvail2(_ x: OptionsAvail1) { testAvail2(.#^OPTIONS_AVAIL_1^#) } // OPTIONS_AVAIL_1: Begin completions -// ENUM_AVAIL_1-NOT: AAA +// OPTIONS_AVAIL_1-NOT: AAA // OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: aaa[#OptionsAvail1#]; // OPTIONS_AVAIL_1-DAG: Decl[StaticVar]/ExprSpecific/NotRecommended/TypeRelation[Identical]: BBB[#OptionsAvail1#]; // OPTIONS_AVAIL_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#rawValue: Int#})[#OptionsAvail1#] -// ENUM_AVAIL_1-NOT: AAA +// OPTIONS_AVAIL_1-NOT: AAA // OPTIONS_AVAIL_1: End completions func testWithLiteral1() { @@ -460,8 +477,9 @@ func testWithLiteral1() { } let s: S _ = s.takeEnum(thing: .#^WITH_LITERAL_1^#, other: 1.0) -// WITH_LITERAL_1: Begin completions, 1 items +// WITH_LITERAL_1: Begin completions, 2 items // WITH_LITERAL_1-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myCase[#S.MyEnum#]; +// WITH_LITERAL_1-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): S.MyEnum#})[#(into: inout Hasher) -> Void#]; // WITH_LITERAL_1-NEXT: End completions } func testWithLiteral2() { @@ -484,8 +502,9 @@ func testWithLiteral3() { func takeEnum(thing: MyEnum, other: Double) {} func test(s: S) { _ = s.takeEnum(thing: .#^WITH_LITERAL_3^#, other: 1.0) -// WITH_LITERAL_3: Begin completions, 1 items +// WITH_LITERAL_3: Begin completions, 2 items // WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myCase[#MyEnum#]; +// WITH_LITERAL_3-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): MyEnum#})[#(into: inout Hasher) -> Void#]; // WITH_LITERAL_3-NEXT: End completions } } @@ -521,11 +540,13 @@ func testNonOptSet() { let x: NonOptSet x = .#^NON_OPT_SET_1^# } -// NON_OPT_SET_1: Begin completions, 4 items +// NON_OPT_SET_1: Begin completions, 6 items // NON_OPT_SET_1-DAG: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: a[#NonOptSet#] +// NON_OPT_SET_1-DAG: Decl[StaticVar]/CurrNominal: wrongType[#Int#]; // NON_OPT_SET_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init({#x: Int#}, {#y: Int#})[#NonOptSet#] // NON_OPT_SET_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#NonOptSet#] // NON_OPT_SET_1-DAG: Decl[StaticMethod]/ExprSpecific/TypeRelation[Identical]: b()[#NonOptSet#] +// NON_OPT_SET_1-DAG: Decl[InstanceMethod]/ExprSpecific/TypeRelation[Identical]: notStatic({#(self): NonOptSet#})[#() -> NonOptSet#]; // NON_OPT_SET_1: End completions func testNonOptSet() { @@ -562,12 +583,12 @@ struct AnotherTy: MyProtocol {} func testSubType() { var _: BaseClass = .#^SUBTYPE_1^# } -// SUBTYPE_1: Begin completions, 3 items -// SUBTYPE_1-NOT: init(failable: +// SUBTYPE_1: Begin completions, 4 items // SUBTYPE_1-NOT: Concrete1( // SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#BaseClass#]; // SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: SubClass()[#BaseClass.SubClass#]; // SUBTYPE_1-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: subInstance[#BaseClass.SubClass#]; +// SUBTYPE_1-DAG: Decl[Constructor]/CurrNominal: init({#failable: Void#})[#BaseClass?#]; // SUBTYPE_1: End completions func testMemberTypealias() { @@ -618,11 +639,11 @@ struct HasCreator { func testHasStaticClosure() { let _: HasCreator = .#^STATIC_CLOSURE_1^# } -// STATIC_CLOSURE_1: Begin completions, 2 items +// STATIC_CLOSURE_1: Begin completions, 3 items // STATIC_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Identical]: init()[#HasCreator#]; // FIXME: Suggest 'create()[#HasCreateor#]', not 'create'. // STATIC_CLOSURE_1-DAG: Decl[StaticVar]/CurrNominal: create[#() -> HasCreator#]; -// STATIC_CLOSURE_1-NOT: create_curried +// STATIC_CLOSURE_1-DAG: Decl[StaticVar]/CurrNominal: create_curried[#() -> () -> HasCreator#]; // STATIC_CLOSURE_1: End completions struct HasOverloaded { @@ -633,11 +654,13 @@ struct HasOverloaded { } func testOverload(val: HasOverloaded) { let _ = val.takeEnum(.#^OVERLOADED_METHOD_1^#) -// OVERLOADED_METHOD_1: Begin completions, 4 items +// OVERLOADED_METHOD_1: Begin completions, 6 items // OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: South[#SomeEnum1#]; name=South // OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: North[#SomeEnum1#]; name=North +// OVERLOADED_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: East[#SomeEnum2#]; name=East // OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: West[#SomeEnum2#]; name=West +// OVERLOADED_METHOD_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): SomeEnum2#})[#(into: inout Hasher) -> Void#]; // OVERLOADED_METHOD_1: End completions let _ = HasOverloaded.init(e: .#^OVERLOADED_INIT_1^#) @@ -761,10 +784,10 @@ extension MyStruct where T: OtherProtocol { func receiveMyStructOfMyProtocol(value: MyStruct) {} func testTypeParamInContextType() { receiveMyStructOfMyProtocol(value: .#^TYPEPARAM_IN_CONTEXTTYPE_1^#) -// TYPEPARAM_IN_CONTEXTTYPE_1: Begin completions, 2 items -// TYPEPARAM_IN_CONTEXTTYPE_1-NOT: otherProtocolOption +// TYPEPARAM_IN_CONTEXTTYPE_1: Begin completions, 3 items // TYPEPARAM_IN_CONTEXTTYPE_1-DAG: Decl[Constructor]/CurrNominal: init()[#MyStruct#]; // TYPEPARAM_IN_CONTEXTTYPE_1-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Convertible]: myProtocolOption[#MyStruct#]; +// TYPEPARAM_IN_CONTEXTTYPE_1-DAG: Decl[StaticVar]/CurrNominal: otherProtocolOption[#MyStruct#]; // TYPEPARAM_IN_CONTEXTTYPE_1: End completions } @@ -795,11 +818,13 @@ func testClosureReturnTypeForOverloaded() { overloadedClosureRcv { .#^OVERLOADED_CLOSURE_RETURN^# } -// OVERLOADED_CLOSURE_RETURN: Begin completions, 4 items +// OVERLOADED_CLOSURE_RETURN: Begin completions, 6 items // OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: South[#SomeEnum1#]; // OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: North[#SomeEnum1#]; +// OVERLOADED_CLOSURE_RETURN-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): SomeEnum1#})[#(into: inout Hasher) -> Void#]; // OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: East[#SomeEnum2#]; // OVERLOADED_CLOSURE_RETURN-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: West[#SomeEnum2#]; +// OVERLOADED_CLOSURE_RETURN-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): SomeEnum2#})[#(into: inout Hasher) -> Void#]; // OVERLOADED_CLOSURE_RETURN: End completions } From 352adc3b5de1b7cb92319564ce9a52e26f2571a1 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 24 Aug 2020 18:10:27 -0400 Subject: [PATCH 061/120] Remove Argument from UnresolvedMemberExpr Instead, an expresison like `.foo()` is represented as an `UnresolvedMemberExpr` nested inside a `CallExpr`. --- include/swift/AST/Expr.h | 79 ++-------- include/swift/AST/Pattern.h | 3 + lib/AST/ASTDumper.cpp | 7 +- lib/AST/ASTWalker.cpp | 12 +- lib/AST/Expr.cpp | 55 ------- lib/IDE/ExprContextAnalysis.cpp | 146 ++++-------------- lib/IDE/ExprContextAnalysis.h | 4 - lib/IDE/SwiftSourceDocInfo.cpp | 4 - lib/Parse/ParseExpr.cpp | 56 +------ lib/Sema/CSApply.cpp | 33 +--- lib/Sema/CSDiagnostics.cpp | 16 +- lib/Sema/CSGen.cpp | 36 +---- lib/Sema/CSSimplify.cpp | 35 +++-- lib/Sema/ConstraintSystem.cpp | 36 +---- lib/Sema/TypeCheckConstraints.cpp | 7 - lib/Sema/TypeCheckPattern.cpp | 20 ++- test/IDE/complete_call_arg.swift | 44 +++++- test/expr/delayed-ident/member_chains.swift | 49 +++++- test/stmt/statements.swift | 2 +- tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp | 3 - 20 files changed, 190 insertions(+), 457 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index d292a00d17fbf..872e3da08a35f 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -249,17 +249,8 @@ class alignas(8) Expr { NumArgLabels : 16 ); - SWIFT_INLINE_BITFIELD_FULL(UnresolvedMemberExpr, Expr, 1+1+1+16, - /// Whether the UnresolvedMemberExpr has arguments. - HasArguments : 1, - /// Whether the UnresolvedMemberExpr also has source locations for the - /// argument label. - HasArgLabelLocs : 1, - /// Whether the last argument is a trailing closure. - HasTrailingClosure : 1, - : NumPadBits, - /// # of argument labels stored after the UnresolvedMemberExpr. - NumArgLabels : 16 + SWIFT_INLINE_BITFIELD_FULL(UnresolvedMemberExpr, Expr, 2, + FunctionRefKind : 2 ); SWIFT_INLINE_BITFIELD(OverloadSetRefExpr, Expr, 2, @@ -1841,71 +1832,35 @@ class DynamicSubscriptExpr final /// member, which is to be resolved with context sensitive type information into /// bar.foo. These always have unresolved type. class UnresolvedMemberExpr final - : public Expr, - public TrailingCallArguments { + : public Expr { SourceLoc DotLoc; DeclNameLoc NameLoc; DeclNameRef Name; - Expr *Argument; - - UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, - DeclNameRef name, Expr *argument, - ArrayRef argLabels, - ArrayRef argLabelLocs, - bool hasTrailingClosure, - bool implicit); public: - /// Create a new unresolved member expression with no arguments. - static UnresolvedMemberExpr *create(ASTContext &ctx, SourceLoc dotLoc, - DeclNameLoc nameLoc, DeclNameRef name, - bool implicit); - - /// Create a new unresolved member expression. - static UnresolvedMemberExpr *create(ASTContext &ctx, SourceLoc dotLoc, - DeclNameLoc nameLoc, DeclNameRef name, - SourceLoc lParenLoc, - ArrayRef args, - ArrayRef argLabels, - ArrayRef argLabelLocs, - SourceLoc rParenLoc, - ArrayRef trailingClosures, - bool implicit); + UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, DeclNameRef name, + bool implicit) + : Expr(ExprKind::UnresolvedMember, implicit), DotLoc(dotLoc), + NameLoc(nameLoc), Name(name) {} DeclNameRef getName() const { return Name; } DeclNameLoc getNameLoc() const { return NameLoc; } SourceLoc getDotLoc() const { return DotLoc; } - Expr *getArgument() const { return Argument; } - void setArgument(Expr *argument) { Argument = argument; } - /// Whether this reference has arguments. - bool hasArguments() const { - return Bits.UnresolvedMemberExpr.HasArguments; - } - - unsigned getNumArguments() const { - return Bits.UnresolvedMemberExpr.NumArgLabels; - } - - bool hasArgumentLabelLocs() const { - return Bits.UnresolvedMemberExpr.HasArgLabelLocs; - } + SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } - /// Whether this call with written with a trailing closure. - bool hasTrailingClosure() const { - return Bits.UnresolvedMemberExpr.HasTrailingClosure; - } + SourceLoc getStartLoc() const { return DotLoc; } + SourceLoc getEndLoc() const { return NameLoc.getSourceRange().End; } - /// Return the index of the unlabeled trailing closure argument. - Optional getUnlabeledTrailingClosureIndex() const { - return getArgument()->getUnlabeledTrailingClosureIndexOfPackedArgument(); + /// Retrieve the kind of function reference. + FunctionRefKind getFunctionRefKind() const { + return static_cast( + Bits.UnresolvedMemberExpr.FunctionRefKind); } - SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } - - SourceLoc getStartLoc() const { return DotLoc; } - SourceLoc getEndLoc() const { - return (Argument ? Argument->getEndLoc() : NameLoc.getSourceRange().End); + /// Set the kind of function reference. + void setFunctionRefKind(FunctionRefKind refKind) { + Bits.UnresolvedMemberExpr.FunctionRefKind = static_cast(refKind); } static bool classof(const Expr *E) { diff --git a/include/swift/AST/Pattern.h b/include/swift/AST/Pattern.h index 6424dd12a72f2..13630507bb3a5 100644 --- a/include/swift/AST/Pattern.h +++ b/include/swift/AST/Pattern.h @@ -554,6 +554,9 @@ class EnumElementPattern : public Pattern { bool hasUnresolvedOriginalExpr() const { return ElementDeclOrUnresolvedOriginalExpr.is(); } + void setUnresolvedOriginalExpr(Expr *e) { + ElementDeclOrUnresolvedOriginalExpr = e; + } DeclNameLoc getNameLoc() const { return NameLoc; } SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index fa9efd6ba4c2f..821916d202497 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -2108,11 +2108,8 @@ class PrintExpr : public ExprVisitor { void visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { printCommon(E, "unresolved_member_expr") << " name='" << E->getName() << "'"; - printArgumentLabels(E->getArgumentLabels()); - if (E->getArgument()) { - OS << '\n'; - printRec(E->getArgument()); - } + PrintWithColorRAII(OS, ExprModifierColor) + << " function_ref=" << getFunctionRefKindStr(E->getFunctionRefKind()); PrintWithColorRAII(OS, ParenthesisColor) << ')'; } void visitDotSelfExpr(DotSelfExpr *E) { diff --git a/lib/AST/ASTWalker.cpp b/lib/AST/ASTWalker.cpp index c6884f049b44e..ef2ba5afb7cbf 100644 --- a/lib/AST/ASTWalker.cpp +++ b/lib/AST/ASTWalker.cpp @@ -504,17 +504,7 @@ class Traversal : public ASTVisitorgetArgument()) { - if (auto arg = doIt(E->getArgument())) { - E->setArgument(arg); - return E; - } - - return nullptr; - } - return E; - } + Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *E) { return E; } Expr *visitOpaqueValueExpr(OpaqueValueExpr *E) { return E; } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 113b8e499a136..fe8023090e6f8 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1608,61 +1608,6 @@ DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, hasTrailingClosure, decl, implicit); } -UnresolvedMemberExpr::UnresolvedMemberExpr(SourceLoc dotLoc, - DeclNameLoc nameLoc, - DeclNameRef name, Expr *argument, - ArrayRef argLabels, - ArrayRef argLabelLocs, - bool hasTrailingClosure, - bool implicit) - : Expr(ExprKind::UnresolvedMember, implicit), - DotLoc(dotLoc), NameLoc(nameLoc), Name(name), Argument(argument) { - Bits.UnresolvedMemberExpr.HasArguments = (argument != nullptr); - Bits.UnresolvedMemberExpr.NumArgLabels = argLabels.size(); - Bits.UnresolvedMemberExpr.HasArgLabelLocs = !argLabelLocs.empty(); - Bits.UnresolvedMemberExpr.HasTrailingClosure = hasTrailingClosure; - initializeCallArguments(argLabels, argLabelLocs); -} - -UnresolvedMemberExpr *UnresolvedMemberExpr::create(ASTContext &ctx, - SourceLoc dotLoc, - DeclNameLoc nameLoc, - DeclNameRef name, - bool implicit) { - size_t size = totalSizeToAlloc({ }, { }); - - void *memory = ctx.Allocate(size, alignof(UnresolvedMemberExpr)); - return new (memory) UnresolvedMemberExpr(dotLoc, nameLoc, name, nullptr, - { }, { }, - /*hasTrailingClosure=*/false, - implicit); -} - -UnresolvedMemberExpr * -UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, - DeclNameLoc nameLoc, DeclNameRef name, - SourceLoc lParenLoc, - ArrayRef args, - ArrayRef argLabels, - ArrayRef argLabelLocs, - SourceLoc rParenLoc, - ArrayRef trailingClosures, - bool implicit) { - SmallVector argLabelsScratch; - SmallVector argLabelLocsScratch; - Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosures, implicit, - argLabelsScratch, argLabelLocsScratch); - - size_t size = totalSizeToAlloc(argLabels, argLabelLocs); - - void *memory = ctx.Allocate(size, alignof(UnresolvedMemberExpr)); - return new (memory) UnresolvedMemberExpr(dotLoc, nameLoc, name, arg, - argLabels, argLabelLocs, - trailingClosures.size() == 1, - implicit); -} - ArrayRef ApplyExpr::getArgumentLabels( SmallVectorImpl &scratch) const { // Unary operators and 'self' applications have a single, unlabeled argument. diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 787606e321891..23fa5971f7b3c 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -558,6 +558,31 @@ static void collectPossibleCalleesByQualifiedLookup( } } +/// For the given \p unresolvedMemberExpr, collect possible callee types and +/// declarations. +static bool collectPossibleCalleesForUnresolvedMember( + DeclContext &DC, UnresolvedMemberExpr *unresolvedMemberExpr, + SmallVectorImpl &candidates) { + auto collectMembers = [&](Type expectedTy) { + if (!expectedTy->mayHaveMembers()) + return; + collectPossibleCalleesByQualifiedLookup(DC, MetatypeType::get(expectedTy), + unresolvedMemberExpr->getName(), + candidates); + }; + + // Get the context of the expression itself. + ExprContextInfo contextInfo(&DC, unresolvedMemberExpr); + for (auto expectedTy : contextInfo.getPossibleTypes()) { + collectMembers(expectedTy); + // If this is an optional type, let's also check its base type. + if (auto baseTy = expectedTy->getOptionalObjectType()) { + collectMembers(baseTy->lookThroughAllOptionalTypes()); + } + } + return !candidates.empty(); +} + /// For the given \c callExpr, collect possible callee types and declarations. static bool collectPossibleCalleesForApply( DeclContext &DC, ApplyExpr *callExpr, @@ -599,6 +624,8 @@ static bool collectPossibleCalleesForApply( } else if (auto CRCE = dyn_cast(fnExpr)) { collectPossibleCalleesByQualifiedLookup( DC, CRCE->getArg(), DeclNameRef::createConstructor(), candidates); + } else if (auto *UME = dyn_cast(fnExpr)) { + collectPossibleCalleesForUnresolvedMember(DC, UME, candidates); } if (!candidates.empty()) @@ -662,39 +689,6 @@ static bool collectPossibleCalleesForSubscript( return !candidates.empty(); } -/// For the given \p unresolvedMemberExpr, collect possible callee types and -/// declarations. -static bool collectPossibleCalleesForUnresolvedMember( - DeclContext &DC, UnresolvedMemberExpr *unresolvedMemberExpr, - SmallVectorImpl &candidates) { - auto currModule = DC.getParentModule(); - - auto collectMembers = [&](Type expectedTy) { - if (!expectedTy->mayHaveMembers()) - return; - SmallVector members; - collectPossibleCalleesByQualifiedLookup(DC, MetatypeType::get(expectedTy), - unresolvedMemberExpr->getName(), - members); - for (auto member : members) { - if (isReferenceableByImplicitMemberExpr(currModule, &DC, expectedTy, - member.Decl)) - candidates.push_back(member); - } - }; - - // Get the context of the expression itself. - ExprContextInfo contextInfo(&DC, unresolvedMemberExpr); - for (auto expectedTy : contextInfo.getPossibleTypes()) { - collectMembers(expectedTy); - // If this is an optional type, let's also check its base type. - if (auto baseTy = expectedTy->getOptionalObjectType()) { - collectMembers(baseTy->lookThroughAllOptionalTypes()); - } - } - return !candidates.empty(); -} - /// Get index of \p CCExpr in \p Args. \p Args is usually a \c TupleExpr /// or \c ParenExpr. /// \returns \c true if success, \c false if \p CCExpr is not a part of \p Args. @@ -761,11 +755,6 @@ class ExprContextAnalyzer { if (!collectPossibleCalleesForSubscript(*DC, subscriptExpr, Candidates)) return false; Arg = subscriptExpr->getIndex(); - } else if (auto *unresolvedMemberExpr = dyn_cast(E)) { - if (!collectPossibleCalleesForUnresolvedMember(*DC, unresolvedMemberExpr, - Candidates)) - return false; - Arg = unresolvedMemberExpr->getArgument(); } else { llvm_unreachable("unexpected expression kind"); } @@ -840,7 +829,6 @@ class ExprContextAnalyzer { switch (Parent->getKind()) { case ExprKind::Call: case ExprKind::Subscript: - case ExprKind::UnresolvedMember: case ExprKind::Binary: case ExprKind::PrefixUnary: { analyzeApplyExpr(Parent); @@ -848,8 +836,10 @@ class ExprContextAnalyzer { } case ExprKind::Array: { if (auto type = ParsedExpr->getType()) { - recordPossibleType(type); - break; + if (!type->is()) { + recordPossibleType(type); + break; + } } // Check context types of the array literal expression. @@ -1144,7 +1134,6 @@ class ExprContextAnalyzer { case ExprKind::Assign: case ExprKind::Dictionary: case ExprKind::If: - case ExprKind::UnresolvedMember: return true; case ExprKind::Array: return (!Parent.getAsExpr() || @@ -1153,8 +1142,7 @@ class ExprContextAnalyzer { auto ParentE = Parent.getAsExpr(); return !ParentE || (!isa(ParentE) && !isa(ParentE) && - !isa(ParentE) && - !isa(ParentE)); + !isa(ParentE)); } case ExprKind::Closure: return isSingleExpressionBodyForCodeCompletion( @@ -1228,73 +1216,3 @@ ExprContextInfo::ExprContextInfo(DeclContext *DC, Expr *TargetExpr) { singleExpressionBody); Analyzer.Analyze(); } - -//===----------------------------------------------------------------------===// -// isReferenceableByImplicitMemberExpr(ModuleD, DeclContext, Type, ValueDecl) -//===----------------------------------------------------------------------===// - -bool swift::ide::isReferenceableByImplicitMemberExpr( - ModuleDecl *CurrModule, DeclContext *DC, Type T, ValueDecl *VD) { - - if (VD->isOperator()) - return false; - - if (T->getOptionalObjectType() && - VD->getModuleContext()->isStdlibModule()) { - // In optional context, ignore '.init()', 'init(nilLiteral:)', - if (isa(VD)) - return false; - // TODO: Ignore '.some()' and '.none' too *in expression - // context*. They are useful in pattern context though. - } - - // Enum element decls can always be referenced by implicit member - // expression. - if (isa(VD)) - return true; - - // Only non-failable constructors are implicitly referenceable. - if (auto CD = dyn_cast(VD)) { - return (!CD->isFailable() || CD->isImplicitlyUnwrappedOptional()); - } - - // Otherwise, check the result type matches the contextual type. - auto declTy = T->getTypeOfMember(CurrModule, VD); - if (declTy->is()) - return false; - - // Member types can also be implicitly referenceable as long as it's - // convertible to the contextual type. - if (auto CD = dyn_cast(VD)) { - declTy = declTy->getMetatypeInstanceType(); - - // Emit construction for the same type via typealias doesn't make sense - // because we are emitting all `.init()`s. - if (declTy->isEqual(T)) - return false; - - // Only non-protocol nominal type can be instantiated. - auto nominal = declTy->getAnyNominal(); - if (!nominal || isa(nominal)) - return false; - - return swift::isConvertibleTo(declTy, T, /*openArchetypes=*/true, *DC); - } - - // Only static member can be referenced. - if (!VD->isStatic()) - return false; - - if (isa(VD)) { - // Strip '(Self.Type) ->' and parameters. - declTy = declTy->castTo()->getResult(); - declTy = declTy->castTo()->getResult(); - } else if (auto FT = declTy->getAs()) { - // The compiler accepts 'static var factory: () -> T' for implicit - // member expression. - // FIXME: This emits just 'factory'. We should emit 'factory()' instead. - declTy = FT->getResult(); - } - return declTy->isEqual(T) || - swift::isConvertibleTo(declTy, T, /*openArchetypes=*/true, *DC); -} diff --git a/lib/IDE/ExprContextAnalysis.h b/lib/IDE/ExprContextAnalysis.h index 1e0b749ac065c..9c957353b68e0 100644 --- a/lib/IDE/ExprContextAnalysis.h +++ b/lib/IDE/ExprContextAnalysis.h @@ -114,10 +114,6 @@ class ExprContextInfo { } }; -/// Returns whether \p VD is referenceable with implicit member expression. -bool isReferenceableByImplicitMemberExpr( - ModuleDecl *CurrModule, DeclContext *DC, Type T, ValueDecl *VD); - } // namespace ide } // namespace swift diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index b372b1cba3e39..6f44a3078ac63 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -282,10 +282,6 @@ Stmt *NameMatcher::walkToStmtPost(Stmt *S) { } Expr *NameMatcher::getApplicableArgFor(Expr *E) { - if (auto *UME = dyn_cast(E)) { - if (auto *Arg = UME->getArgument()) - return Arg; - } if (ParentCalls.empty()) return nullptr; auto &Last = ParentCalls.back(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1d3a63a826d11..1a42b3832b09a 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1612,60 +1612,8 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { return makeParserErrorResult(new (Context) ErrorExpr(DotLoc)); SyntaxContext->createNodeInPlace(SyntaxKind::MemberAccessExpr); - // Check for a () suffix, which indicates a call when constructing - // this member. Note that this cannot be the start of a new line. - if (Tok.isFollowingLParen()) { - SourceLoc lParenLoc, rParenLoc; - SmallVector args; - SmallVector argLabels; - SmallVector argLabelLocs; - SmallVector trailingClosures; - - ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, - /*isPostfix=*/true, isExprBasic, - lParenLoc, args, argLabels, - argLabelLocs, - rParenLoc, - trailingClosures, - SyntaxKind::TupleExprElementList); - SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); - return makeParserResult( - status, - UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, - lParenLoc, args, argLabels, - argLabelLocs, rParenLoc, - trailingClosures, - /*implicit=*/false)); - } - - // Check for a trailing closure, if allowed. - if (Tok.is(tok::l_brace) && isValidTrailingClosure(isExprBasic, *this)) { - if (SyntaxContext->isEnabled()) { - // Add dummy blank argument list to the call expression syntax. - SyntaxContext->addSyntax( - ParsedSyntaxRecorder::makeBlankTupleExprElementList( - leadingTriviaLoc(), *SyntaxContext)); - } - - SmallVector trailingClosures; - auto result = parseTrailingClosures(isExprBasic, NameLoc.getSourceRange(), - trailingClosures); - if (trailingClosures.empty()) - return nullptr; - - SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); - // Handle .foo by just making an AST node. - return makeParserResult( - result, UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, - SourceLoc(), {}, {}, {}, - SourceLoc(), trailingClosures, - /*implicit=*/false)); - } - - // Handle .foo by just making an AST node. - return makeParserResult( - UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, - /*implicit=*/false)); + return makeParserResult(new (Context) UnresolvedMemberExpr( + DotLoc, NameLoc, Name, /*implicit=*/false)); } case tok::kw_super: // 'super' diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index bcbb363713730..25c85383a9e55 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -860,12 +860,9 @@ namespace { } // Similarly, ".foo(...)" really applies two argument lists. - if (auto *unresolvedMemberExpr = dyn_cast(prev)) { - if (unresolvedMemberExpr->hasArguments() || - unresolvedMemberExpr->hasTrailingClosure()) - return 2; - return 1; - } + if (isa(prev) && + isa(cast(prev)->getFn())) + return 2; return getArgCount(maxArgCount); }(); @@ -2786,30 +2783,6 @@ namespace { if (!result) return nullptr; - auto getType = [&](Expr *E) -> Type { return cs.getType(E); }; - - // If there was an argument, apply it. - if (auto arg = expr->getArgument()) { - // Get the callee locator. Note this may be different to the locator for - // the member being referenced for things like callAsFunction. - auto *calleeLoc = cs.getCalleeLocator(exprLoc); - - // Build and finish the apply. - ApplyExpr *apply = CallExpr::create( - ctx, result, arg, expr->getArgumentLabels(), - expr->getArgumentLabelLocs(), expr->hasTrailingClosure(), - /*implicit=*/expr->isImplicit(), Type(), getType); - result = finishApply(apply, Type(), exprLoc, calleeLoc); - - // FIXME: Application could fail, because some of the solutions - // are not expressible in AST (yet?), like certain tuple-to-tuple - // conversions. Better solution here would be not to form solutions - // which couldn't be applied by e.g. detecting situations like that - // and inserting fixes early. - if (!result) - return nullptr; - } - // Check for ambiguous member if the base is an Optional if (baseTy->getOptionalObjectType()) { diagnoseAmbiguousNominalMember(baseTy, result); diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 066da31ad5341..acb66a12e83bd 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1443,10 +1443,7 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() { } else if (isa(diagExpr)) { subElementDiagID = diag::assignment_subscript_has_immutable_base; } else if (auto *UME = dyn_cast(diagExpr)) { - if (UME->hasArguments()) - subElementDiagID = diag::assignment_lhs_is_apply_expression; - else - subElementDiagID = diag::assignment_lhs_is_immutable_property; + subElementDiagID = diag::assignment_lhs_is_immutable_property; } else { subElementDiagID = diag::assignment_lhs_is_immutable_variable; } @@ -4500,9 +4497,6 @@ MissingArgumentsFailure::getCallInfo(ASTNode anchor) const { return std::make_tuple(call->getFn(), call->getArg(), call->getNumArguments(), call->getUnlabeledTrailingClosureIndex()); - } else if (auto *UME = getAsExpr(anchor)) { - return std::make_tuple(UME, UME->getArgument(), UME->getNumArguments(), - UME->getUnlabeledTrailingClosureIndex()); } else if (auto *SE = getAsExpr(anchor)) { return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(), SE->getUnlabeledTrailingClosureIndex()); @@ -6341,6 +6335,14 @@ bool MissingContextualBaseInMemberRefFailure::diagnoseAsError() { // Member reference could be wrapped into a number of parens // e.g. `((.foo))`. auto *parentExpr = findParentExpr(anchor); + + // Look through immediate call of unresolved member (e.g., `.foo(0)`). + if (parentExpr && isa(parentExpr)) + parentExpr = findParentExpr(parentExpr); + + // FIXME: We should probably look through the entire member chain so that + // something like `let _ = .foo().bar` gets the "no contextual type" error + // rather than the "Cannot infer contextual base" error. UnresolvedMemberChainResultExpr *resultExpr = nullptr; if (parentExpr && isa(parentExpr)) { resultExpr = cast(parentExpr); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index f03ec3297463a..c83879fb2a3c1 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1441,10 +1441,6 @@ namespace { auto baseLocator = CS.getConstraintLocator( expr, ConstraintLocator::MemberRefBase); - FunctionRefKind functionRefKind = - expr->getArgument() ? FunctionRefKind::DoubleApply - : FunctionRefKind::Compound; - auto memberLocator = CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember); @@ -1466,37 +1462,9 @@ namespace { // member, i.e., an enum case or a static variable. auto baseMetaTy = MetatypeType::get(baseTy); CS.addUnresolvedValueMemberConstraint(baseMetaTy, expr->getName(), - memberTy, CurDC, functionRefKind, + memberTy, CurDC, + expr->getFunctionRefKind(), memberLocator); - - // If there is an argument, apply it. - if (auto arg = expr->getArgument()) { - // Create a new type variable for the result of the function. - auto outputTy = CS.createTypeVariable( - CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), - TVO_CanBindToNoEscape); - - // The function/enum case must be callable with the given argument. - - // FIXME: Redesign the AST so that an UnresolvedMemberExpr directly - // stores a list of arguments together with their inout-ness, instead of - // a single ParenExpr or TupleExpr argument. - SmallVector params; - AnyFunctionType::decomposeInput(CS.getType(arg), params); - - CS.addConstraint(ConstraintKind::ApplicableFunction, - FunctionType::get(params, outputTy), - memberTy, - CS.getConstraintLocator(expr, ConstraintLocator::ApplyFunction)); - - associateArgumentLabels( - CS.getConstraintLocator(expr), - {expr->getArgumentLabels(), - expr->getUnlabeledTrailingClosureIndex()}); - return outputTy; - } - - // Otherwise, the result is just the type of the member. return memberTy; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index b252b1773756f..96d6fc2cee266 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6755,22 +6755,27 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, } else if (auto *CE = getAsExpr(anchor)) { baseExpr = CE->getFn(); baseType = getType(baseExpr); - // If this is an initializer call without explicit mention - // of `.init` on metatype value. - if (auto *AMT = baseType->getAs()) { - auto instanceType = AMT->getInstanceType()->getWithoutParens(); - if (!cs.isTypeReference(baseExpr)) { - if (baseType->is() && - instanceType->isAnyExistentialType()) { - return AllowInvalidInitRef::onProtocolMetatype( - cs, baseType, init, cs.isStaticallyDerivedMetatype(baseExpr), - baseExpr->getSourceRange(), locator); - } + // FIXME: Historically, UnresolvedMemberExprs have allowed implicit + // construction through a metatype value, but this should probably be + // illegal. + if (!isa(baseExpr)) { + // If this is an initializer call without explicit mention + // of `.init` on metatype value. + if (auto *AMT = baseType->getAs()) { + auto instanceType = AMT->getInstanceType()->getWithoutParens(); + if (!cs.isTypeReference(baseExpr)) { + if (baseType->is() && + instanceType->isAnyExistentialType()) { + return AllowInvalidInitRef::onProtocolMetatype( + cs, baseType, init, cs.isStaticallyDerivedMetatype(baseExpr), + baseExpr->getSourceRange(), locator); + } - if (!instanceType->isExistentialType() || - instanceType->isAnyExistentialType()) { - return AllowInvalidInitRef::onNonConstMetatype(cs, baseType, init, - locator); + if (!instanceType->isExistentialType() || + instanceType->isAnyExistentialType()) { + return AllowInvalidInitRef::onNonConstMetatype(cs, baseType, init, + locator); + } } } } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index a933da4a6857c..e186f5d321bfe 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -562,21 +562,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( } if (auto *UME = getAsExpr(anchor)) { - auto *calleeLoc = - getConstraintLocator(UME, ConstraintLocator::UnresolvedMember); - - // Handle special cases for applies of non-function types. - // FIXME: Consider re-designing the AST such that an unresolved member expr - // with arguments uses a CallExpr, which would make this logic unnecessary - // and clean up a bunch of other special cases. Doing so may require a bit - // of hacking in CSGen though. - if (UME->hasArguments()) { - if (auto overload = getOverloadFor(calleeLoc)) { - if (auto *loc = getSpecialFnCalleeLoc(overload->boundType)) - return loc; - } - } - return calleeLoc; + return getConstraintLocator(UME, ConstraintLocator::UnresolvedMember); } if (isExpr(anchor)) @@ -3153,9 +3139,7 @@ static bool diagnoseAmbiguityWithContextualType( auto type = solution.simplifyType(overload.boundType); - if (isExpr(anchor) || isExpr(anchor) || - (isExpr(anchor) && - castToExpr(anchor)->hasArguments())) { + if (isExpr(anchor) || isExpr(anchor)) { auto fnType = type->castTo(); DE.diagnose( loc, diag::cannot_convert_candidate_result_to_contextual_type, @@ -3665,11 +3649,6 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } - if (auto *UME = getAsExpr(anchor)) { - anchor = UME->getArgument(); - path = path.slice(1); - continue; - } break; } @@ -3694,15 +3673,6 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } - // The unresolved member itself is the function. - if (auto unresolvedMember = getAsExpr(anchor)) { - if (unresolvedMember->getArgument()) { - anchor = unresolvedMember; - path = path.slice(1); - continue; - } - } - break; case ConstraintLocator::AutoclosureResult: @@ -3898,8 +3868,6 @@ Expr *constraints::getArgumentExpr(ASTNode node, unsigned index) { Expr *argExpr = nullptr; if (auto *AE = dyn_cast(expr)) argExpr = AE->getArg(); - else if (auto *UME = dyn_cast(expr)) - argExpr = UME->getArgument(); else if (auto *SE = dyn_cast(expr)) argExpr = SE->getIndex(); else diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 5c16de3953a8c..f0226a6d094e8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -1125,13 +1125,6 @@ namespace { } } - // If this is an unresolved member with a call argument (e.g., - // .some(x)), record the argument expression. - if (auto unresolvedMember = dyn_cast(expr)) { - if (auto arg = unresolvedMember->getArgument()) - CallArgs.insert(arg); - } - // FIXME(diagnostics): `InOutType` could appear here as a result // of successful re-typecheck of the one of the sub-expressions e.g. // `let _: Int = { (s: inout S) in s.bar() }`. On the first diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index b02220919667d..1e5c3a571df6f 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -446,18 +446,12 @@ class ResolvePattern : public ASTVisitorgetArgument()) { - subPattern = getSubExprPattern(arg); - } - if (ume->getName().getBaseName().isSpecial()) return nullptr; return new (Context) EnumElementPattern(ume->getDotLoc(), ume->getNameLoc(), ume->getName(), - subPattern, ume); + nullptr, ume); } // Member syntax 'T.Element' forms a pattern if 'T' is an enum and the @@ -547,6 +541,18 @@ class ResolvePattern : public ASTVisitor(ce->getFn())) return nullptr; + if (isa(ce->getFn())) { + auto *P = visit(ce->getFn()); + if (!P) + return nullptr; + + auto *EEP = cast(P); + EEP->setSubPattern(getSubExprPattern(ce->getArg())); + EEP->setUnresolvedOriginalExpr(ce); + + return P; + } + SmallVector components; if (!ExprToIdentTypeRepr(components, Context).visit(ce->getFn())) return nullptr; diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 07e5d183f7b62..bbd17ff20cd9e 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -78,11 +78,13 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_METHOD_AFTERPAREN_2 | %FileCheck %s -check-prefix=STATIC_METHOD_AFTERPAREN_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_METHOD_SECOND | %FileCheck %s -check-prefix=STATIC_METHOD_SECOND // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_METHOD_SKIPPED | %FileCheck %s -check-prefix=STATIC_METHOD_SKIPPED +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_METHOD_OVERLOADED | %FileCheck %s -check-prefix=STATIC_METHOD_OVERLOADED // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_AFTERPAREN_1 | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_AFTERPAREN_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_AFTERPAREN_2 | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_AFTERPAREN_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_AFTERPAREN_3 | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_AFTERPAREN_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_SECOND | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_SECOND // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_SKIPPED | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_SKIPPED +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_OVERLOADED | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_OVERLOADED // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_1_AFTERPAREN_1 | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_AFTERPAREN_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_1_AFTERPAREN_2 | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_AFTERPAREN_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_1_SECOND | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_SECOND @@ -91,6 +93,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_2_AFTERPAREN_2 | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_AFTERPAREN_2 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_2_SECOND | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_SECOND // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_2_SKIPPED | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_SKIPPED +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IMPLICIT_MEMBER_ARRAY_2_OVERLOADED | %FileCheck %s -check-prefix=IMPLICIT_MEMBER_OVERLOADED // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ARCHETYPE_GENERIC_1 | %FileCheck %s -check-prefix=ARCHETYPE_GENERIC_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=PARAM_WITH_ERROR_AUTOCLOSURE| %FileCheck %s -check-prefix=PARAM_WITH_ERROR_AUTOCLOSURE @@ -667,6 +670,8 @@ class TestStaticMemberCall { static func create2(_ arg1: Int, arg2: Int = 0, arg3: Int = 1, arg4: Int = 2) -> TestStaticMemberCall { return TestStaticMemberCall() } + static func createOverloaded(arg1: Int) -> TestStaticMemberCall { TestStaticMemberCall() } + static func createOverloaded(arg1: String) -> String { arg1 } } func testStaticMemberCall() { let _ = TestStaticMemberCall.create1(#^STATIC_METHOD_AFTERPAREN_1^#) @@ -695,24 +700,30 @@ func testStaticMemberCall() { // STATIC_METHOD_SKIPPED: Pattern/ExprSpecific: {#arg3: Int#}[#Int#]; // STATIC_METHOD_SKIPPED: Pattern/ExprSpecific: {#arg4: Int#}[#Int#]; // STATIC_METHOD_SKIPPED: End completions + + let _ = TestStaticMemberCall.createOverloaded(#^STATIC_METHOD_OVERLOADED^#) +// STATIC_METHOD_OVERLOADED: Begin completions, 2 items +// STATIC_METHOD_OVERLOADED-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// STATIC_METHOD_OVERLOADED-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: String#}[')'][#String#]; name=arg1: String +// STATIC_METHOD_OVERLOADED: End completions } func testImplicitMember() { let _: TestStaticMemberCall = .create1(#^IMPLICIT_MEMBER_AFTERPAREN_1^#) // IMPLICIT_MEMBER_AFTERPAREN_1: Begin completions, 1 items -// IMPLICIT_MEMBER_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// IMPLICIT_MEMBER_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int // IMPLICIT_MEMBER_AFTERPAREN_1: End completions let _: TestStaticMemberCall = .create2(#^IMPLICIT_MEMBER_AFTERPAREN_2^#) // IMPLICIT_MEMBER_AFTERPAREN_2: Begin completions -// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; -// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; +// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; +// IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; // IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem/TypeRelation[Identical]: Int[#Int#]; // IMPLICIT_MEMBER_AFTERPAREN_2-DAG: Literal[Integer]/None/TypeRelation[Identical]: 0[#Int#]; // IMPLICIT_MEMBER_AFTERPAREN_2: End completions let _: TestStaticMemberCall? = .create1(#^IMPLICIT_MEMBER_AFTERPAREN_3^#) // IMPLICIT_MEMBER_AFTERPAREN_3: Begin completions, 1 items -// IMPLICIT_MEMBER_AFTERPAREN_3: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// IMPLICIT_MEMBER_AFTERPAREN_3: Decl[StaticMethod]/CurrNominal/TypeRelation[Convertible]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int // IMPLICIT_MEMBER_AFTERPAREN_3: End completions let _: TestStaticMemberCall = .create2(1, #^IMPLICIT_MEMBER_SECOND^#) @@ -728,6 +739,12 @@ func testImplicitMember() { // IMPLICIT_MEMBER_SKIPPED: Pattern/ExprSpecific: {#arg3: Int#}[#Int#]; // IMPLICIT_MEMBER_SKIPPED: Pattern/ExprSpecific: {#arg4: Int#}[#Int#]; // IMPLICIT_MEMBER_SKIPPED: End completions + + let _: TestStaticMemberCall = .createOverloaded(#^IMPLICIT_MEMBER_OVERLOADED^#) +// IMPLICIT_MEMBER_OVERLOADED: Begin completions, 2 items +// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal/TypeRelation[Identical]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: Int +// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal: ['(']{#arg1: String#}[')'][#String#]; name=arg1: String +// IMPLICIT_MEMBER_OVERLOADED: End completions } func testImplicitMemberInArrayLiteral() { struct Receiver { @@ -741,8 +758,12 @@ func testImplicitMemberInArrayLiteral() { // Same as IMPLICIT_MEMBER_AFTERPAREN_1. ]) Receiver([ + .create1(x: 1), .create2(#^IMPLICIT_MEMBER_ARRAY_1_AFTERPAREN_2^#), // Same as IMPLICIT_MEMBER_AFTERPAREN_2. + ]) + Receiver([ + .create1(x: 1), .create2(1, #^IMPLICIT_MEMBER_ARRAY_1_SECOND^# // Same as IMPLICIT_MEMBER_SECOND. ]) @@ -751,15 +772,22 @@ func testImplicitMemberInArrayLiteral() { // Same as IMPLICIT_MEMBER_SKIPPED. .create1(x: 12) ]) + Receiver(arg1: 12, arg2: [ + .create1(x: 12), + .createOverloaded(#^IMPLICIT_MEMBER_ARRAY_1_OVERLOADED^#) + // Same as IMPLICIT_MEMBER_OVERLOADED. + ]) let _: [TestStaticMemberCall] = [ .create1(#^IMPLICIT_MEMBER_ARRAY_2_AFTERPAREN_1^#), - // Same as STATIC_METHOD_AFTERPAREN_1. + // Same as IMPLICIT_MEMBER_AFTERPAREN_1. .create2(#^IMPLICIT_MEMBER_ARRAY_2_AFTERPAREN_2^#), - // Same as STATIC_METHOD_AFTERPAREN_2. + // Same as IMPLICIT_MEMBER_AFTERPAREN_2. .create2(1, #^IMPLICIT_MEMBER_ARRAY_2_SECOND^#), - // Same as STATIC_METHOD_SECOND. + // Same as IMPLICIT_MEMBER_SECOND. .create2(1, arg3: 2, #^IMPLICIT_MEMBER_ARRAY_2_SKIPPED^#), - // Same as STATIC_METHOD_SKIPPED. + // Same as IMPLICIT_MEMBER_SKIPPED. + .createOverloaded(#^IMPLICIT_MEMBER_ARRAY_2_OVERLOADED^#), + // Same as IMPLICIT_MEMBER_OVERLOADED ] } diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index ed694ab8ad98c..aee57a5eae1f9 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -54,6 +54,11 @@ struct ImplicitMembers: Equatable { func getAnotherOptional(arg: Int) -> ImplicitMembers? { ImplicitMembers() } + + static func takesClosure(_: (Int) -> Void) -> ImplicitMembers { ImplicitMembers() } + static func takesArgClosure(_: Int, _: (Int) -> Void) -> ImplicitMembers { ImplicitMembers() } + func methodTakesClosure(_: (Int) -> Void) -> ImplicitMembers { ImplicitMembers() } + func methodTakesArgClosure(_: Int, _: (Int) -> Void) -> ImplicitMembers { ImplicitMembers() } subscript(arg: Void) -> ImplicitMembers { get { ImplicitMembers() } @@ -165,6 +170,13 @@ let _: ImplicitMembers? = .createOptional()?.getAnotherOptional()?.another // FIXME: This should be allowed // let _: ImplicitMembers? = .superOptional???.another +let _: ImplicitMembers = .takesClosure { _ in } +let _: ImplicitMembers = .takesArgClosure(0) { _ in } +let _: ImplicitMembers = .implicit.methodTakesClosure { _ in } +let _: ImplicitMembers = .implicit.methodTakesArgClosure(0) { _ in } +let _: ImplicitMembers? = .optional?.methodTakesClosure { _ in } +let _: ImplicitMembers? = .optional?.methodTakesArgClosure(0) { _ in } + let _: ImplicitMembers = .implicit[()] let _: ImplicitMembers = .implicit[optional: ()]! let _: ImplicitMembers? = .implicit[optional: ()] @@ -212,14 +224,14 @@ func testLValues() { .implicitLet = local; // expected-error {{cannot assign to property: 'implicitLet' is a 'let' constant}} .implicitImmutable = local; // expected-error {{cannot assign to property: 'implicitImmutable' is a get-only property}} - .createImplicit() = local; // expected-error {{cannot assign to value: 'createImplicit' is a method}} + .createImplicit() = local; // expected-error {{expression is not assignable: function call returns immutable value}} .implicit.another = local; // expected-error {{cannot assign to property: 'another' is a get-only property}} .implicit[immutable: ()] = local; // expected-error {{cannot assign through subscript: subscript is get-only}} .implicit.getAnother() = local; // expected-error {{expression is not assignable: function call returns immutable value}} .implicitLet.anotherMutable = local; // expected-error {{cannot assign to property: 'implicitLet' is a 'let' constant}} .implicitImmutable.anotherMutable = local; // expected-error {{cannot assign to property: 'implicitImmutable' is a get-only property}} - .createImplicit().anotherMutable = local; // expected-error {{cannot assign to value: 'createImplicit' is a method}} + .createImplicit().anotherMutable = local; // expected-error {{cannot assign to property: function call returns immutable value}} .implicit.another.anotherMutable = local; // expected-error {{cannot assign to property: 'another' is a get-only property}} .implicit[immutable: ()].anotherMutable = local; // expected-error {{cannot assign to property: subscript is get-only}} .implicit.getAnother().anotherMutable = local; // expected-error {{cannot assign to property: function call returns immutable value}} @@ -279,3 +291,36 @@ implicit(.implicit.anotherString.anotherStringInt) // expected-error {{member ch implicit(.implicit.getAnotherString().anotherStringInt) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} implicit(.implicit.anotherString.getAnotherStringInt()) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} implicit(.implicit.getAnotherString().getAnotherStringInt()) // expected-error {{member chain produces result of type 'ImplicitGeneric' but contextual base was inferred as 'ImplicitGeneric'}} + +// Implicit member syntax can be used to apply curried instance methods: +struct Curried { + func method() -> Curried { Curried() } + func method(with arg: Int) -> Curried { Curried() } + func method(with arg1: Int, and arg2: String) -> Curried { Curried() } + func takesClosure(_: (Int) -> Void) -> Curried { Curried() } + func takesArgClosure(_: Int, _: (Int) -> Void) -> Curried { Curried() } + static func curried(_ _self: Curried) -> () -> Curried{ return { _self } } + static func curriedWithArgs(_ _self: Curried) -> (Int, String) -> Curried { return { _, _ in _self } } +} + +let _: Curried = .method(Curried())() +let _: Curried = .method(Curried())(with: 0) +let _: Curried = .method(Curried())(with: 0, and: "") +let _: Curried = .takesClosure(Curried())() { _ in } +let _: Curried = .takesArgClosure(Curried())(0) { _ in } +let _: Curried = .curried(Curried())() +let _: Curried = .curriedWithArgs(Curried())(0, "") + + +struct CurriedGeneric { + func create(_: U.Type) -> CurriedGeneric { return CurriedGeneric() } +} + +extension CurriedGeneric where T == Int { + func createInt() -> Self { + return self + } +} + +let _: CurriedGeneric = .createInt(CurriedGeneric())() +let _: CurriedGeneric = .create(CurriedGeneric())(Int.self) diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index b2594eb8f3398..93de7f6efabc8 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -280,7 +280,7 @@ func RepeatWhileStmt4() { func brokenSwitch(_ x: Int) -> Int { switch x { - case .Blah(var rep): // expected-error{{pattern cannot match values of type 'Int'}} + case .Blah(var rep): // expected-error{{type 'Int' has no member 'Blah'}} return rep } } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index 6b0fe110a774c..d77474f197d5c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -1601,9 +1601,6 @@ class PlaceholderExpansionScanner { if (auto *CE = dyn_cast(E)) { // Call expression can have argument. Arg = CE->getArg(); - } else if (auto UME = dyn_cast(E)) { - // Unresolved member can have argument too. - Arg = UME->getArgument(); } if (!Arg) return false; From 30e52be19cca65a503fa2df6fb5d320fc2e1730d Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 26 Aug 2020 22:29:50 -0400 Subject: [PATCH 062/120] [AST] Properly initialize FunctionRefKind for UnresolvedMemberExpr --- include/swift/AST/Expr.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 872e3da08a35f..b53a279a93b4c 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1841,7 +1841,21 @@ class UnresolvedMemberExpr final UnresolvedMemberExpr(SourceLoc dotLoc, DeclNameLoc nameLoc, DeclNameRef name, bool implicit) : Expr(ExprKind::UnresolvedMember, implicit), DotLoc(dotLoc), - NameLoc(nameLoc), Name(name) {} + NameLoc(nameLoc), Name(name) { + // FIXME: Really, we should be setting this to `FunctionRefKind::Compound` + // if `NameLoc` is compound, but this would be a source break for cases like + // ``` + // struct S { + // static func makeS(_: Int) -> S! { S() } + // } + // + // let s: S = .makeS(_:)(0) + // ``` + // Instead, we should store compound-ness as a separate bit from applied/ + // unapplied. + Bits.UnresolvedMemberExpr.FunctionRefKind = + static_cast(FunctionRefKind::Unapplied); + } DeclNameRef getName() const { return Name; } DeclNameLoc getNameLoc() const { return NameLoc; } From d8878d2ab2dc035d7c10097b7f2536b14fd8ef29 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 26 Aug 2020 22:30:55 -0400 Subject: [PATCH 063/120] [Tests] Add IUO tests for implicit member chains --- test/expr/delayed-ident/member_chains.swift | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index aee57a5eae1f9..2691574dbd321 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -27,6 +27,10 @@ struct ImplicitMembers: Equatable { } static var superOptional: ImplicitMembers??? = ImplicitMembers() + static func createIUOArg(_: Int) -> ImplicitMembers { ImplicitMembers() } + var anotherIUO: ImplicitMembers! { ImplicitMembers() } + func getAnotherIUO() -> ImplicitMembers! { ImplicitMembers() } + var another: ImplicitMembers { ImplicitMembers() } var anotherMutable: ImplicitMembers { get { ImplicitMembers() } @@ -115,6 +119,16 @@ let _: ImplicitMembers = .implicit.another.another.another.another.another let _: ImplicitMembers = .implicit.getAnother().getAnother().getAnother().getAnother().getAnother() let _: ImplicitMembers = .implicit.getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0) +let _: ImplicitMembers = .implicit.another.getAnother().getAnother(arg: 0).anotherIUO +let _: ImplicitMembers = .createImplicit().another.getAnother().getAnother(arg: 0).anotherIUO +let _: ImplicitMembers = .init().another.getAnother().getAnother(arg: 0).anotherIUO + +let _: ImplicitMembers = .implicit.another.getAnother().getAnother(arg: 0).getAnotherIUO() +let _: ImplicitMembers = .createImplicit().another.getAnother().getAnother(arg: 0).getAnotherIUO() +let _: ImplicitMembers = .init().another.getAnother().getAnother(arg: 0).getAnotherIUO() + +let _: ImplicitMembers = .createIUOArg(_:)(0) + let _: ImplicitMembers = .optional! let _: ImplicitMembers = .optional!.another let _: ImplicitMembers = .createOptional()!.another @@ -122,6 +136,10 @@ let _: ImplicitMembers = .optional!.anotherOptional! let _: ImplicitMembers = .createOptional()!.anotherOptional! let _: ImplicitMembers = .optional!.getAnotherOptional()! let _: ImplicitMembers = .createOptional()!.getAnotherOptional()! +let _: ImplicitMembers = .implicit.getAnotherIUO() +let _: ImplicitMembers = .createImplicit().anotherIUO +let _: ImplicitMembers = .implicit.anotherIUO +let _: ImplicitMembers = .createImplicit().anotherIUO let _: ImplicitMembers = .optional // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{35-35= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{35-35=!}} let _: ImplicitMembers = .implicit.anotherOptional // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{51-51= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{51-51=!}} @@ -167,6 +185,8 @@ let _: ImplicitMembers? = .createOptional()?.getAnother() let _: ImplicitMembers? = .createOptional()?.getAnotherOptional() let _: ImplicitMembers? = .createOptional()?.anotherOptional?.another let _: ImplicitMembers? = .createOptional()?.getAnotherOptional()?.another +let _: ImplicitMembers? = .createOptional()?.getAnotherOptional()?.anotherIUO +let _: ImplicitMembers? = .createOptional()?.getAnotherOptional()?.getAnotherIUO() // FIXME: This should be allowed // let _: ImplicitMembers? = .superOptional???.another From b6c2f89414996e8212d479bde72b09a007b81920 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 25 Aug 2020 23:31:42 -0700 Subject: [PATCH 064/120] [AST] NFC: Fix a typo in the `HasTypeHole` description --- include/swift/AST/Types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index d5fae0c50be9f..e94790e1ea9c0 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -148,7 +148,7 @@ class RecursiveTypeProperties { /// This type contains an OpaqueTypeArchetype. HasOpaqueArchetype = 0x400, - /// This type constains a type hole. + /// This type contains a type hole. HasTypeHole = 0x800, Last_Property = HasTypeHole From 9de7b59388efe456e1b479072ee366917e0e89a0 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Wed, 26 Aug 2020 21:28:30 -0700 Subject: [PATCH 065/120] Subsume SWIFT_STDLIB_USE_NONATOMIC_RC into SWIFT_STDLIB_SINGLE_THREADED_RUNTIME (#33643) --- stdlib/CMakeLists.txt | 10 +--------- stdlib/cmake/modules/AddSwiftStdlib.cmake | 4 ---- stdlib/cmake/modules/SwiftSource.cmake | 2 +- stdlib/public/runtime/MetadataImpl.h | 2 +- test/lit.site.cfg.in | 4 ++-- utils/build-presets.ini | 15 --------------- utils/build-script-impl | 2 -- .../Runtime/weak-reference-racetests.swift | 2 +- validation-test/SILOptimizer/string_switch.swift | 2 +- validation-test/StdlibUnittest/AtomicInt.swift | 2 +- validation-test/StdlibUnittest/RaceTest.swift | 2 +- validation-test/lit.site.cfg.in | 4 ++-- validation-test/stdlib/ArrayBridging.swift | 2 +- validation-test/stdlib/CommandLine.swift | 2 +- validation-test/stdlib/DictionaryBridging.swift | 2 +- validation-test/stdlib/ErrorProtocol.swift | 2 +- 16 files changed, 15 insertions(+), 44 deletions(-) diff --git a/stdlib/CMakeLists.txt b/stdlib/CMakeLists.txt index fab91273b97ec..d692ef67d1d70 100644 --- a/stdlib/CMakeLists.txt +++ b/stdlib/CMakeLists.txt @@ -21,18 +21,10 @@ option(SWIFT_RUNTIME_MACHO_NO_DYLD "Build stdlib assuming the runtime environment uses Mach-O but does not support dynamic modules." FALSE) -option(SWIFT_STDLIB_USE_NONATOMIC_RC - "Build the standard libraries and overlays with nonatomic reference count operations enabled" - FALSE) - option(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME - "Build the standard libraries assuming that they will be used in an environment with only a single thread. If set, also forces SWIFT_STDLIB_USE_NONATOMIC_RC to On." + "Build the standard libraries assuming that they will be used in an environment with only a single thread." FALSE) -if(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) - set(SWIFT_STDLIB_USE_NONATOMIC_RC TRUE) -endif() - # # End of user-configurable options. # diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 2ce27dc08e0c6..afaf60ac15ede 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -306,10 +306,6 @@ function(_add_target_variant_c_compile_flags) list(APPEND result "-DSWIFT_RUNTIME_MACHO_NO_DYLD") endif() - if(SWIFT_STDLIB_USE_NONATOMIC_RC) - list(APPEND result "-DSWIFT_STDLIB_USE_NONATOMIC_RC") - endif() - if(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) list(APPEND result "-DSWIFT_STDLIB_SINGLE_THREADED_RUNTIME") endif() diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index 5041c931f06dd..170d891b2d1d5 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -404,7 +404,7 @@ function(_compile_swift_files list(APPEND swift_flags "-enable-library-evolution") endif() - if(SWIFT_STDLIB_USE_NONATOMIC_RC) + if(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME) list(APPEND swift_flags "-Xfrontend" "-assume-single-threaded") endif() diff --git a/stdlib/public/runtime/MetadataImpl.h b/stdlib/public/runtime/MetadataImpl.h index 1b12fc1000f26..ac455a8cc9938 100644 --- a/stdlib/public/runtime/MetadataImpl.h +++ b/stdlib/public/runtime/MetadataImpl.h @@ -136,7 +136,7 @@ template struct RetainableBoxBase { static constexpr size_t stride = sizeof(T); static constexpr bool isPOD = false; static constexpr bool isBitwiseTakable = true; -#ifdef SWIFT_STDLIB_USE_NONATOMIC_RC +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME static constexpr bool isAtomic = false; #else static constexpr bool isAtomic = true; diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index a6753e644779d..c4511d2110324 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -87,8 +87,8 @@ if "@SWIFT_AST_VERIFIER@" == "TRUE": if "@SWIFT_OPTIMIZED@" == "TRUE": config.available_features.add("optimized_stdlib") -if "@SWIFT_STDLIB_USE_NONATOMIC_RC@" == "TRUE": - config.available_features.add("nonatomic_rc") +if "@SWIFT_STDLIB_SINGLE_THREADED_RUNTIME@" == "TRUE": + config.available_features.add("single_threaded_runtime") if "@SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS@" == "TRUE": config.available_features.add('runtime_function_counters') diff --git a/utils/build-presets.ini b/utils/build-presets.ini index ad2ab73e7893f..3a0e27830069e 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -212,21 +212,6 @@ skip-test-ios-simulator skip-test-tvos-simulator skip-test-watchos-simulator -[preset: buildbot,tools=RA,stdlib=RD,single-thread] -mixin-preset= - mixin_buildbot_tools_RA_stdlib_RD - -dash-dash - -# Enable non-atomic build of the stdlib -swift-stdlib-use-nonatomic-rc=true - -# Don't run host tests for iOS, tvOS and watchOS platforms to make the build -# faster. -skip-test-ios-host -skip-test-tvos-host -skip-test-watchos-host - [preset: buildbot,tools=R,stdlib=RD] mixin-preset= mixin_buildbot_tools_R_stdlib_RD diff --git a/utils/build-script-impl b/utils/build-script-impl index 09c1893bc6f9a..078bca624b642 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -186,7 +186,6 @@ KNOWN_SETTINGS=( swift-primary-variant-sdk "" "default SDK for target binaries" swift-runtime-enable-leak-checker "0" "Enable leaks checking routines in the runtime" swift-stdlib-enable-assertions "1" "enable assertions in Swift" - swift-stdlib-use-nonatomic-rc "0" "build the Swift stdlib and overlays with nonatomic reference count operations enabled" swift-tools-enable-lto "" "enable LTO compilation of Swift tools. *NOTE* This does not include the swift standard library and runtime. Must be set to one of 'thin' or 'full'" extra-swift-args "" "Extra arguments to pass to swift modules which match regex. Assumed to be a flattened cmake list consisting of [module_regexp, args, module_regexp, args, ...]" report-statistics "0" "set to 1 to generate compilation statistics files for swift libraries" @@ -1755,7 +1754,6 @@ for host in "${ALL_HOSTS[@]}"; do -DSWIFT_ANALYZE_CODE_COVERAGE:STRING=$(toupper "${SWIFT_ANALYZE_CODE_COVERAGE}") -DSWIFT_STDLIB_BUILD_TYPE:STRING="${SWIFT_STDLIB_BUILD_TYPE}" -DSWIFT_STDLIB_ASSERTIONS:BOOL=$(true_false "${SWIFT_STDLIB_ENABLE_ASSERTIONS}") - -DSWIFT_STDLIB_USE_NONATOMIC_RC:BOOL=$(true_false "${SWIFT_STDLIB_USE_NONATOMIC_RC}") -DSWIFT_ENABLE_COMPATIBILITY_OVERRIDES:BOOL=$(true_false "${SWIFT_ENABLE_COMPATIBILITY_OVERRIDES}") -DSWIFT_STDLIB_SINGLE_THREADED_RUNTIME:BOOL=$(true_false "${SWIFT_STDLIB_SINGLE_THREADED_RUNTIME}") -DSWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS:BOOL=$(true_false "${SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS}") diff --git a/validation-test/Runtime/weak-reference-racetests.swift b/validation-test/Runtime/weak-reference-racetests.swift index 1d759c12c60ff..23fc108b6f9f0 100644 --- a/validation-test/Runtime/weak-reference-racetests.swift +++ b/validation-test/Runtime/weak-reference-racetests.swift @@ -1,7 +1,7 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: stress_test -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import StdlibUnittest diff --git a/validation-test/SILOptimizer/string_switch.swift b/validation-test/SILOptimizer/string_switch.swift index b30c80b7cc48e..555c1d972847c 100644 --- a/validation-test/SILOptimizer/string_switch.swift +++ b/validation-test/SILOptimizer/string_switch.swift @@ -4,7 +4,7 @@ // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib // REQUIRES: stress_test // REQUIRES: executable_test -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import StdlibUnittest diff --git a/validation-test/StdlibUnittest/AtomicInt.swift b/validation-test/StdlibUnittest/AtomicInt.swift index 00bf5d1c0f8c2..9bc3e8b3f1f8a 100644 --- a/validation-test/StdlibUnittest/AtomicInt.swift +++ b/validation-test/StdlibUnittest/AtomicInt.swift @@ -4,7 +4,7 @@ // RUN: %target-run %t.out // REQUIRES: executable_test // REQUIRES: stress_test -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import SwiftPrivate import StdlibUnittest diff --git a/validation-test/StdlibUnittest/RaceTest.swift b/validation-test/StdlibUnittest/RaceTest.swift index b3c296036f371..d0f1efc0ef750 100644 --- a/validation-test/StdlibUnittest/RaceTest.swift +++ b/validation-test/StdlibUnittest/RaceTest.swift @@ -2,7 +2,7 @@ // RUN: %target-build-swift -Xfrontend -disable-access-control -module-name a %s -o %t.out // RUN: %target-run %t.out | %FileCheck %s // REQUIRES: stress_test -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import SwiftPrivate import StdlibUnittest diff --git a/validation-test/lit.site.cfg.in b/validation-test/lit.site.cfg.in index 12aaec7150e00..649281bef0df2 100644 --- a/validation-test/lit.site.cfg.in +++ b/validation-test/lit.site.cfg.in @@ -87,8 +87,8 @@ if "@SWIFT_OPTIMIZED@" == "TRUE": if "@SWIFT_ENABLE_SOURCEKIT_TESTS@" == "TRUE": config.available_features.add('sourcekit') -if "@SWIFT_STDLIB_USE_NONATOMIC_RC@" == "TRUE": - config.available_features.add("nonatomic_rc") +if "@SWIFT_STDLIB_SINGLE_THREADED_RUNTIME@" == "TRUE": + config.available_features.add("single_threaded_runtime") if "@SWIFT_BUILT_STANDALONE@" == "TRUE": config.available_features.add('standalone_build') diff --git a/validation-test/stdlib/ArrayBridging.swift b/validation-test/stdlib/ArrayBridging.swift index 3fd92fa951dda..6c24bfe8dd0cf 100644 --- a/validation-test/stdlib/ArrayBridging.swift +++ b/validation-test/stdlib/ArrayBridging.swift @@ -9,7 +9,7 @@ // REQUIRES: objc_interop // REQUIRES: executable_test // REQUIRES: stress_test -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import StdlibUnittest import Foundation diff --git a/validation-test/stdlib/CommandLine.swift b/validation-test/stdlib/CommandLine.swift index 610ec08eee400..a43defe135d37 100644 --- a/validation-test/stdlib/CommandLine.swift +++ b/validation-test/stdlib/CommandLine.swift @@ -8,7 +8,7 @@ // RUN: %target-run %t/CommandLineStressTest foo bar baz qux quux corge grault garply waldo fred plugh xyzzy and thud // REQUIRES: executable_test // REQUIRES: stress_test -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime // This file is an empty stub to call into the command line stress test which // houses `main`. diff --git a/validation-test/stdlib/DictionaryBridging.swift b/validation-test/stdlib/DictionaryBridging.swift index 4a2d203e2ca79..eed83d32a67ec 100644 --- a/validation-test/stdlib/DictionaryBridging.swift +++ b/validation-test/stdlib/DictionaryBridging.swift @@ -8,7 +8,7 @@ // REQUIRES: stress_test // REQUIRES: objc_interop -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import StdlibUnittest import Foundation diff --git a/validation-test/stdlib/ErrorProtocol.swift b/validation-test/stdlib/ErrorProtocol.swift index 5e8a8b7862d38..6b2cf70ef7838 100644 --- a/validation-test/stdlib/ErrorProtocol.swift +++ b/validation-test/stdlib/ErrorProtocol.swift @@ -2,7 +2,7 @@ // REQUIRES: executable_test // REQUIRES: stress_test // REQUIRES: objc_interop -// UNSUPPORTED: nonatomic_rc +// UNSUPPORTED: single_threaded_runtime import SwiftPrivate import StdlibUnittest From 8f2e53c72d352543226a60d4faab6654bd8e238d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 27 Aug 2020 00:12:51 -0700 Subject: [PATCH 066/120] [AST] Make sure that hole types are always allocated in `ConstraintSolver` arena Since `HoleType` directly as well as other types which could contain holes are bound to a lifetime of constraint system that created them, we need to make sure that such types are always allocated using `ConstraintSolver` arena instead of a permanent one. --- lib/AST/ASTContext.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6c7e155e8b6d4..8a744f7d03d94 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1466,8 +1466,9 @@ bool ASTContext::hadError() const { /// Retrieve the arena from which we should allocate storage for a type. static AllocationArena getArena(RecursiveTypeProperties properties) { bool hasTypeVariable = properties.hasTypeVariable(); - return hasTypeVariable? AllocationArena::ConstraintSolver - : AllocationArena::Permanent; + bool hasHole = properties.hasTypeHole(); + return hasTypeVariable || hasHole ? AllocationArena::ConstraintSolver + : AllocationArena::Permanent; } void ASTContext::addSearchPath(StringRef searchPath, bool isFramework, @@ -2360,9 +2361,13 @@ Type ErrorType::get(Type originalType) { Type HoleType::get(ASTContext &ctx, OriginatorType originator) { assert(originator); - RecursiveTypeProperties properties = RecursiveTypeProperties::HasTypeHole; + auto properties = reinterpret_cast(originator.getOpaqueValue()) + ->getRecursiveProperties(); + properties |= RecursiveTypeProperties::HasTypeHole; + auto arena = getArena(properties); - return new (ctx, arena) HoleType(ctx, originator, properties); + return new (ctx, arena) + HoleType(ctx, originator, RecursiveTypeProperties::HasTypeHole); } BuiltinIntegerType *BuiltinIntegerType::get(BuiltinIntegerWidth BitWidth, From 8905f9735c4a7da3d25a44b4f229601ecda00403 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 27 Aug 2020 11:00:22 -0400 Subject: [PATCH 067/120] [Tests] Update completions for unresolved enum members --- test/IDE/complete_enum_elements.swift | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/IDE/complete_enum_elements.swift b/test/IDE/complete_enum_elements.swift index 533fd73ce6515..48d8b01d8b23a 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -138,7 +138,7 @@ enum FooEnum: CaseIterable { // FOO_ENUM_DOT_INVALID-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}} // FOO_ENUM_DOT_INVALID-NEXT: End completions -// FOO_ENUM_DOT_ELEMENTS: Begin completions, 11 items +// FOO_ENUM_DOT_ELEMENTS: Begin completions, 13 items // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Foo1[#FooEnum#]{{; name=.+$}} // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Foo2[#FooEnum#]{{; name=.+$}} // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/ExprSpecific/TypeRelation[Identical]: alias1[#FooEnum#]; name=alias1 @@ -149,6 +149,8 @@ enum FooEnum: CaseIterable { // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#repeating: FooEnum#}, {#count: Int#})[#Array#]; name=AllCases(repeating: FooEnum, count: Int) // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#unsafeUninitializedCapacity: Int#}, {#initializingWith: (inout UnsafeMutableBufferPointer, inout Int) throws -> Void##(inout UnsafeMutableBufferPointer, inout Int) throws -> Void#})[' rethrows'][#Array#]; name=AllCases(unsafeUninitializedCapacity: Int, initializingWith: (inout UnsafeMutableBufferPointer, inout Int) throws -> Void) rethrows // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#from: Decoder#})[' throws'][#Array#]; name=AllCases(from: Decoder) throws +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#repeating: FooEnum#}, {#count: Int#})[#FooEnum.AllCases#]; name=AllCases(repeating: FooEnum, count: Int) +// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[Constructor]/CurrNominal/IsSystem: AllCases({#(elements): Sequence#})[#FooEnum.AllCases#]; name=AllCases(elements: Sequence) // FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]; name=allCases // FOO_ENUM_DOT_ELEMENTS-NEXT: End completions @@ -462,17 +464,10 @@ func testWithInvalid1() { func testUnqualified1(x: QuxEnum) { _ = x == .Qux1 || x == .#^UNRESOLVED_2^#Qux2 - // UNRESOLVED_2: Begin completions, 12 items + // UNRESOLVED_2: Begin completions // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: UInt#})[#Int#]; name=RawValue(bitPattern: UInt) - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#(source): Float#})[#Int#]; name=RawValue(source: Float) - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#(source): Double#})[#Int#]; name=RawValue(source: Double) - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#(source): Float80#})[#Int#]; name=RawValue(source: Float80) - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#from: Decoder#})[' throws'][#Int#]; name=RawValue(from: Decoder) throws - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: OpaquePointer?#})[#Int#]; name=RawValue(bitPattern: OpaquePointer?) - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: ObjectIdentifier#})[#Int#]; name=RawValue(bitPattern: ObjectIdentifier) - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/IsSystem: RawValue({#bitPattern: _Pointer?#})[#Int#]; name=RawValue(bitPattern: _Pointer?) // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]; name=init(rawValue: Int) // UNRESOLVED_2-DAG: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Invalid]: hash({#(self): QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(self: QuxEnum) // UNRESOLVED_2: End completions From 4cd1b715bdfddc422ce7340cb56492ae7045b4e9 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 26 Aug 2020 14:31:00 -0400 Subject: [PATCH 068/120] [Runtime] Shrink ConcurrentReadableHashMap a bit. We're using a lot of space on the free lists. Each vector is three words, and we have two of them. Switch to a single linked list. We only need one list, as both kinds of pointers just get free()'d. A linked list optimizes for the common case where the list is empty. This takes us from six words to one. Also make ReaderCount, ElementCount, and ElementCapacity uint32_ts. The size_ts were unnecessarily large and this saves some space on 64-bit systems. While we're in there, add 0/NULL initialization to all elements. The current use in the runtime is unaffected (it's statically allocated) but the local variables used in the test were tripping over this. --- include/swift/Runtime/Concurrent.h | 64 +++++++++++---------- test/stdlib/symbol-visibility-linux.test-sh | 2 + 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/include/swift/Runtime/Concurrent.h b/include/swift/Runtime/Concurrent.h index 10269653284df..459459c1553b7 100644 --- a/include/swift/Runtime/Concurrent.h +++ b/include/swift/Runtime/Concurrent.h @@ -635,30 +635,49 @@ template struct ConcurrentReadableHashMap { std::atomic &at(size_t i) { return (&Mask)[i]; } }; + /// A simple linked list representing pointers that need to be freed. + struct FreeListNode { + FreeListNode *Next; + void *Ptr; + + static void add(FreeListNode **head, void *ptr) { + auto *newNode = new FreeListNode{*head, ptr}; + *head = newNode; + } + + static void freeAll(FreeListNode **head) { + auto *node = *head; + while (node) { + auto *next = node->Next; + free(node->Ptr); + delete node; + node = next; + } + *head = nullptr; + } + }; + /// The number of readers currently active, equal to the number of snapshot /// objects currently alive. - std::atomic ReaderCount; + std::atomic ReaderCount{0}; /// The number of elements in the elements array. - std::atomic ElementCount; + std::atomic ElementCount{0}; /// The array of elements. - std::atomic Elements; + std::atomic Elements{nullptr}; /// The array of indices. - std::atomic Indices; + std::atomic Indices{nullptr}; /// The writer lock, which must be taken before any mutation of the table. Mutex WriterLock; /// The maximum number of elements that the current elements array can hold. - size_t ElementCapacity; + uint32_t ElementCapacity{0}; - /// The list of element arrays to be freed once no readers are active. - std::vector ElementFreeList; - - /// The list of index arrays to be freed once no readers are active. - std::vector IndicesFreeList; + /// The list of pointers to be freed once no readers are active. + FreeListNode *FreeList{nullptr}; void incrementReaders() { ReaderCount.fetch_add(1, std::memory_order_acquire); @@ -668,24 +687,11 @@ template struct ConcurrentReadableHashMap { ReaderCount.fetch_sub(1, std::memory_order_release); } - /// Free all the arrays in the free lists. - void deallocateFreeList() { - for (auto *storage : ElementFreeList) - free(storage); - ElementFreeList.clear(); - ElementFreeList.shrink_to_fit(); - - for (auto *indices : IndicesFreeList) - free(indices); - IndicesFreeList.clear(); - IndicesFreeList.shrink_to_fit(); - } - /// Free all the arrays in the free lists if there are no active readers. If /// there are active readers, do nothing. void deallocateFreeListIfSafe() { if (ReaderCount.load(std::memory_order_acquire) == 0) - deallocateFreeList(); + FreeListNode::freeAll(&FreeList); } /// Grow the elements array, adding the old array to the free list and @@ -702,7 +708,7 @@ template struct ConcurrentReadableHashMap { ElemTy *newElements = static_cast(malloc(newSize)); if (elements) { memcpy(newElements, elements, elementCount * sizeof(ElemTy)); - ElementFreeList.push_back(elements); + FreeListNode::add(&FreeList, elements); } ElementCapacity = newCapacity; @@ -739,7 +745,7 @@ template struct ConcurrentReadableHashMap { Indices.store(newIndices, std::memory_order_release); - IndicesFreeList.push_back(indices); + FreeListNode::add(&FreeList, indices); return newIndices; } @@ -792,7 +798,7 @@ template struct ConcurrentReadableHashMap { ~ConcurrentReadableHashMap() { assert(ReaderCount.load(std::memory_order_acquire) == 0 && "deallocating ConcurrentReadableHashMap with outstanding snapshots"); - deallocateFreeList(); + FreeListNode::freeAll(&FreeList); } /// Readers take a snapshot of the hash map, then work with the snapshot. @@ -943,8 +949,8 @@ template struct ConcurrentReadableHashMap { Elements.store(nullptr, std::memory_order_relaxed); ElementCapacity = 0; - IndicesFreeList.push_back(indices); - ElementFreeList.push_back(elements); + FreeListNode::add(&FreeList, indices); + FreeListNode::add(&FreeList, elements); deallocateFreeListIfSafe(); } diff --git a/test/stdlib/symbol-visibility-linux.test-sh b/test/stdlib/symbol-visibility-linux.test-sh index 86a7a6dab377d..2472610c3e51b 100644 --- a/test/stdlib/symbol-visibility-linux.test-sh +++ b/test/stdlib/symbol-visibility-linux.test-sh @@ -31,6 +31,7 @@ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA80_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA88_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA104_cEEEvv \ +// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA168_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA216_cEEEvv \ // RUN: -e _ZN9__gnu_cxx12__to_xstringISscEET_PFiPT0_mPKS2_P13__va_list_tagEmS5_z \ // RUN: > %t/swiftCore-all.txt @@ -55,6 +56,7 @@ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA80_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA88_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA104_cEEEvv \ +// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA168_cEEEvv \ // RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA216_cEEEvv \ // RUN: -e _ZN9__gnu_cxx12__to_xstringISscEET_PFiPT0_mPKS2_P13__va_list_tagEmS5_z \ // RUN: > %t/swiftRemoteMirror-all.txt From 1fec2b558414d5042371fcd1c2180798cbd9f4c0 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Thu, 27 Aug 2020 10:25:32 -0700 Subject: [PATCH 069/120] Respect SWIFT_STDLIB_SINGLE_THREADED_RUNTIME and use nonatomic refcounting in swift_retain, swift_release, etc. (#33644) --- stdlib/public/runtime/HeapObject.cpp | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/stdlib/public/runtime/HeapObject.cpp b/stdlib/public/runtime/HeapObject.cpp index c409c23731e16..fc644c7a110c4 100644 --- a/stdlib/public/runtime/HeapObject.cpp +++ b/stdlib/public/runtime/HeapObject.cpp @@ -336,7 +336,11 @@ static HeapObject *_swift_retain_(HeapObject *object) { } HeapObject *swift::swift_retain(HeapObject *object) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + return swift_nonatomic_retain(object); +#else CALL_IMPL(swift_retain, (object)); +#endif } SWIFT_RUNTIME_EXPORT @@ -358,7 +362,11 @@ static HeapObject *_swift_retain_n_(HeapObject *object, uint32_t n) { } HeapObject *swift::swift_retain_n(HeapObject *object, uint32_t n) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + return swift_nonatomic_retain_n(object, n); +#else CALL_IMPL(swift_retain_n, (object, n)); +#endif } SWIFT_RUNTIME_EXPORT @@ -379,7 +387,11 @@ static void _swift_release_(HeapObject *object) { } void swift::swift_release(HeapObject *object) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + swift_nonatomic_release(object); +#else CALL_IMPL(swift_release, (object)); +#endif } SWIFT_RUNTIME_EXPORT @@ -399,7 +411,11 @@ static void _swift_release_n_(HeapObject *object, uint32_t n) { } void swift::swift_release_n(HeapObject *object, uint32_t n) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + swift_nonatomic_release_n(object, n); +#else CALL_IMPL(swift_release_n, (object, n)); +#endif } SWIFT_RUNTIME_EXPORT @@ -427,15 +443,22 @@ size_t swift::swift_weakRetainCount(HeapObject *object) { } HeapObject *swift::swift_unownedRetain(HeapObject *object) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + return static_cast(swift_nonatomic_unownedRetain(object)); +#else SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRetain); if (!isValidPointerForNativeRetain(object)) return object; object->refCounts.incrementUnowned(1); return object; +#endif } void swift::swift_unownedRelease(HeapObject *object) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + swift_nonatomic_unownedRelease(object); +#else SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRelease); if (!isValidPointerForNativeRetain(object)) return; @@ -450,6 +473,7 @@ void swift::swift_unownedRelease(HeapObject *object) { swift_slowDealloc(object, classMetadata->getInstanceSize(), classMetadata->getInstanceAlignMask()); } +#endif } void *swift::swift_nonatomic_unownedRetain(HeapObject *object) { @@ -479,15 +503,22 @@ void swift::swift_nonatomic_unownedRelease(HeapObject *object) { } HeapObject *swift::swift_unownedRetain_n(HeapObject *object, int n) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + return swift_nonatomic_unownedRetain_n(object, n); +#else SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRetain_n); if (!isValidPointerForNativeRetain(object)) return object; object->refCounts.incrementUnowned(n); return object; +#endif } void swift::swift_unownedRelease_n(HeapObject *object, int n) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + swift_nonatomic_unownedRelease_n(object, n); +#else SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRelease_n); if (!isValidPointerForNativeRetain(object)) return; @@ -501,6 +532,7 @@ void swift::swift_unownedRelease_n(HeapObject *object, int n) { swift_slowDealloc(object, classMetadata->getInstanceSize(), classMetadata->getInstanceAlignMask()); } +#endif } HeapObject *swift::swift_nonatomic_unownedRetain_n(HeapObject *object, int n) { @@ -533,8 +565,13 @@ static HeapObject *_swift_tryRetain_(HeapObject *object) { if (!isValidPointerForNativeRetain(object)) return nullptr; +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + if (object->refCounts.tryIncrementNonAtomic()) return object; + else return nullptr; +#else if (object->refCounts.tryIncrement()) return object; else return nullptr; +#endif } HeapObject *swift::swift_tryRetain(HeapObject *object) { @@ -557,6 +594,9 @@ void swift::swift_setDeallocating(HeapObject *object) { } HeapObject *swift::swift_unownedRetainStrong(HeapObject *object) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + return swift_nonatomic_unownedRetainStrong(object); +#else SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRetainStrong); if (!isValidPointerForNativeRetain(object)) return object; @@ -566,6 +606,7 @@ HeapObject *swift::swift_unownedRetainStrong(HeapObject *object) { if (! object->refCounts.tryIncrement()) swift::swift_abortRetainUnowned(object); return object; +#endif } HeapObject *swift::swift_nonatomic_unownedRetainStrong(HeapObject *object) { @@ -581,6 +622,9 @@ HeapObject *swift::swift_nonatomic_unownedRetainStrong(HeapObject *object) { } void swift::swift_unownedRetainStrongAndRelease(HeapObject *object) { +#ifdef SWIFT_STDLIB_SINGLE_THREADED_RUNTIME + swift_nonatomic_unownedRetainStrongAndRelease(object); +#else SWIFT_RT_TRACK_INVOCATION(object, swift_unownedRetainStrongAndRelease); if (!isValidPointerForNativeRetain(object)) return; @@ -594,6 +638,7 @@ void swift::swift_unownedRetainStrongAndRelease(HeapObject *object) { bool dealloc = object->refCounts.decrementUnownedShouldFree(1); assert(!dealloc && "retain-strong-and-release caused dealloc?"); (void) dealloc; +#endif } void swift::swift_nonatomic_unownedRetainStrongAndRelease(HeapObject *object) { From 102d6387d9b42ad13c4e66b6ef80469d606593bc Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Thu, 27 Aug 2020 10:25:51 -0700 Subject: [PATCH 070/120] [build] Don't execute dsymutil in parallel (#33654) `dsymutil` is already multithreaded and can be memory intensive -- execute it one at a time. Addresses rdar://63892559 --- utils/build-script-impl | 3 ++- utils/parser-lib/darwin-extract-symbols | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 078bca624b642..1e80510fa7ad2 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2978,12 +2978,13 @@ for host in "${ALL_HOSTS[@]}"; do # # Exclude shell scripts and static archives. # Exclude swift-api-digester dSYM to reduce debug toolchain size. + # Run sequentially -- dsymutil is multithreaded and can be memory intensive (cd "${host_symroot}" && find ./"${CURRENT_PREFIX}" -perm -0111 -type f -print | \ grep -v '.py$' | \ grep -v '.a$' | \ grep -v 'swift-api-digester' | \ - xargs -n 1 -P ${BUILD_JOBS} ${dsymutil_path}) + xargs -n 1 -P 1 ${dsymutil_path}) # Strip executables, shared libraries and static libraries in # `host_install_destdir`. diff --git a/utils/parser-lib/darwin-extract-symbols b/utils/parser-lib/darwin-extract-symbols index a4ee1e4c61e96..1e10f61c7c11b 100755 --- a/utils/parser-lib/darwin-extract-symbols +++ b/utils/parser-lib/darwin-extract-symbols @@ -46,11 +46,12 @@ function xcrun_find_tool() { # Run dsymutil on executables and shared libraries. # # Exclude shell scripts. +# Run sequentially -- dsymutil is multithreaded and can be memory intensive (cd "${INSTALL_SYMROOT}" && find ./"${INSTALL_PREFIX}" -perm -0111 -type f -print | \ grep -v crashlog.py | \ grep -v symbolication.py | \ - xargs -n 1 -P ${BUILD_JOBS} $(xcrun_find_tool dsymutil)) + xargs -n 1 -P 1 $(xcrun_find_tool dsymutil)) # Strip executables, shared libraries and static libraries in INSTALL_DIR. find "${INSTALL_DIR}${INSTALL_PREFIX}/" \ From 1a44db3dcfc1302f0d9beaafe30ccbde8d31847d Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 27 Aug 2020 10:58:45 -0700 Subject: [PATCH 071/120] ABIChecker: exclude decls with the @_alwaysEmitIntoClient attribute Removing or updating @_alwaysEmitIntoClient functions are never ABI-breaking, thus we should exclude them from the symbol set for ABI stability checking. rdar://67883661 --- test/api-digester/Inputs/cake.swift | 3 +++ test/api-digester/Outputs/cake.json | 19 +++++++++++++++++++ .../ModuleAnalyzerNodes.cpp | 5 +++++ .../swift-api-digester/swift-api-digester.cpp | 17 ++++++++++++++--- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/test/api-digester/Inputs/cake.swift b/test/api-digester/Inputs/cake.swift index 3de399487412f..b1728b6f61d48 100644 --- a/test/api-digester/Inputs/cake.swift +++ b/test/api-digester/Inputs/cake.swift @@ -139,3 +139,6 @@ public class UnavailableOnMac {} extension SwiftObjcClass { public func functionUnavailableOnMac() {} } + +@_alwaysEmitIntoClient +public func emitIntoClientFunc() {} diff --git a/test/api-digester/Outputs/cake.json b/test/api-digester/Outputs/cake.json index 8a487febb665b..08a230436a852 100644 --- a/test/api-digester/Outputs/cake.json +++ b/test/api-digester/Outputs/cake.json @@ -1340,6 +1340,25 @@ ], "hasMissingDesignatedInitializers": true }, + { + "kind": "Function", + "name": "emitIntoClientFunc", + "printedName": "emitIntoClientFunc()", + "children": [ + { + "kind": "TypeNominal", + "name": "Void", + "printedName": "()" + } + ], + "declKind": "Func", + "usr": "s:4cake18emitIntoClientFuncyyF", + "moduleName": "cake", + "declAttributes": [ + "AlwaysEmitIntoClient" + ], + "funcSelfKind": "NonMutating" + }, { "kind": "TypeDecl", "name": "Int", diff --git a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp index 9e91584e84659..813a7bd843097 100644 --- a/tools/swift-api-digester/ModuleAnalyzerNodes.cpp +++ b/tools/swift-api-digester/ModuleAnalyzerNodes.cpp @@ -1603,6 +1603,11 @@ SDKContext::shouldIgnore(Decl *D, const Decl* Parent) const { if (isa(VD)) return true; } + // Exclude decls with @_alwaysEmitIntoClient if we are checking ABI. + // These decls are considered effectively public because they are usable + // from inline, so we have to manually exclude them here. + if (D->getAttrs().hasAttribute()) + return true; } else { if (D->isPrivateStdlibDecl(false)) return true; diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index 2b089bf718c5f..deb532fd8e162 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -1057,6 +1057,17 @@ void swift::ide::api::SDKNodeTypeFunc::diagnose(SDKNode *Right) { } namespace { +static void diagnoseRemovedDecl(const SDKNodeDecl *D) { + if (D->getSDKContext().checkingABI()) { + // Don't complain about removing @_alwaysEmitIntoClient if we are checking ABI. + // We shouldn't include these decls in the ABI baseline file. This line is + // added so the checker is backward compatible. + if (D->hasDeclAttribute(DeclAttrKind::DAK_AlwaysEmitIntoClient)) + return; + } + D->emitDiag(SourceLoc(), diag::removed_decl, D->isDeprecated()); +} + // This is first pass on two given SDKNode trees. This pass removes the common part // of two versions of SDK, leaving only the changed part. class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { @@ -1241,7 +1252,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { TD->isProtocol()); } if (auto *Acc = dyn_cast(Left)) { - Acc->emitDiag(SourceLoc(), diag::removed_decl, Acc->isDeprecated()); + diagnoseRemovedDecl(Acc); } return; case NodeMatchReason::FuncToProperty: @@ -2084,7 +2095,7 @@ static bool diagnoseRemovedExtensionMembers(const SDKNode *Node) { if (DT->isExtension()) { for (auto *C: DT->getChildren()) { auto *MD = cast(C); - MD->emitDiag(SourceLoc(), diag::removed_decl, MD->isDeprecated()); + diagnoseRemovedDecl(MD); } return true; } @@ -2161,7 +2172,7 @@ void DiagnosisEmitter::handle(const SDKNodeDecl *Node, NodeAnnotation Anno) { } bool handled = diagnoseRemovedExtensionMembers(Node); if (!handled) - Node->emitDiag(SourceLoc(), diag::removed_decl, Node->isDeprecated()); + diagnoseRemovedDecl(Node); return; } case NodeAnnotation::Rename: { From 524cfae1b29a96b29dc9ea5d2e68cd46702af5e5 Mon Sep 17 00:00:00 2001 From: tbkka Date: Thu, 27 Aug 2020 11:06:40 -0700 Subject: [PATCH 072/120] [Dynamic Casting] Overhauled Runtime (#33561) * Dynamic Cast Rework: Runtime This is a completely refactored version of the core swift_dynamicCast runtime method. This fixes a number of bugs, especially in the handling of multiply-wrapped types such as Optional within Any. The result should be much closer to the behavior specified by `docs/DynamicCasting.md`. Most of the type-specific logic is simply copied over from the earlier implementation, but the overall structure has been changed to be uniformly recursive. In particular, this provides uniform handling of Optional, existentials, Any and other common "box" types along all paths. The consistent structure should also be easier to update in the future with new general types. Benchmarking does not show any noticable performance implications. **Temporarily**, the old implementation is still available. Setting the environment variable `SWIFT_OLD_DYNAMIC_CAST_RUNTIME` before launching a program will use the old runtime implementation. This is only to facilitate testing; once the new implementation is stable, I expect to completely remove the old implementation. --- stdlib/public/core/DebuggerSupport.swift | 9 +- stdlib/public/runtime/CMakeLists.txt | 1 + stdlib/public/runtime/Casting.cpp | 58 +- .../public/runtime/CompatibilityOverride.def | 17 +- stdlib/public/runtime/DynamicCast.cpp | 2197 +++++++++++++++++ test/{stdlib => Casting}/CastTraps.swift.gyb | 54 +- test/Casting/Casts.swift | 763 ++++++ .../SDK/protocol_lookup_foreign.swift | 58 +- test/Interpreter/tuple_casts.swift | 28 +- test/stdlib/Casts.swift | 236 -- validation-test/Casting/BoxingCasts.swift.gyb | 264 ++ 11 files changed, 3417 insertions(+), 268 deletions(-) create mode 100644 stdlib/public/runtime/DynamicCast.cpp rename test/{stdlib => Casting}/CastTraps.swift.gyb (61%) create mode 100644 test/Casting/Casts.swift delete mode 100644 test/stdlib/Casts.swift create mode 100644 validation-test/Casting/BoxingCasts.swift.gyb diff --git a/stdlib/public/core/DebuggerSupport.swift b/stdlib/public/core/DebuggerSupport.swift index 0e7095aebd783..82cfedd5eb4fb 100644 --- a/stdlib/public/core/DebuggerSupport.swift +++ b/stdlib/public/core/DebuggerSupport.swift @@ -152,7 +152,14 @@ public enum _DebuggerSupport { // yes, a type can lie and say it's a class when it's not since we only // check the displayStyle - but then the type would have a custom Mirror // anyway, so there's that... - let willExpand = mirror.displayStyle != .`class` || value is CustomReflectable? + let isNonClass = mirror.displayStyle != .`class` + let isCustomReflectable: Bool + if let value = value { + isCustomReflectable = value is CustomReflectable + } else { + isCustomReflectable = true + } + let willExpand = isNonClass || isCustomReflectable let count = mirror._children.count let bullet = isRoot && (count == 0 || !willExpand) ? "" diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index e29be3ff2033e..0b481bd2459b8 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -33,6 +33,7 @@ set(swift_runtime_sources CompatibilityOverride.cpp CygwinPort.cpp Demangle.cpp + DynamicCast.cpp Enum.cpp EnvironmentVariables.cpp ErrorObjectCommon.cpp diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 6db0739a585ed..99e08243588bb 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -339,6 +339,15 @@ _dynamicCastClassMetatype(const ClassMetadata *sourceType, return nullptr; } +#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +bool swift_unboxFromSwiftValueWithType(OpaqueValue *source, + OpaqueValue *result, + const Metadata *destinationType); +/// Nominal type descriptor for Swift.__SwiftValue +extern "C" const ClassDescriptor NOMINAL_TYPE_DESCR_SYM(s12__SwiftValueC); +#endif + /// Dynamically cast a class instance to a Swift class type. static const void *swift_dynamicCastClassImpl(const void *object, const ClassMetadata *targetType) { @@ -351,10 +360,28 @@ static const void *swift_dynamicCastClassImpl(const void *object, } #endif - auto isa = _swift_getClassOfAllocated(object); + auto srcType = _swift_getClassOfAllocated(object); - if (_dynamicCastClassMetatype(isa, targetType)) + if (_dynamicCastClassMetatype(srcType, targetType)) return object; + +#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class on Linux + if (srcType->getKind() == MetadataKind::Class + && targetType->getKind() == MetadataKind::Class) { + auto srcClassType = cast(srcType); + auto srcDescr = srcClassType->getDescription(); + if (srcDescr == &NOMINAL_TYPE_DESCR_SYM(s12__SwiftValueC)) { + auto srcValue = reinterpret_cast(&object); + void *result; + auto destLocation = reinterpret_cast(&result); + if (swift_unboxFromSwiftValueWithType(srcValue, destLocation, targetType)) { + swift_unknownObjectRelease(const_cast(object)); + return result; + } + } + } +#endif + return nullptr; } @@ -3262,3 +3289,30 @@ HeapObject *_swift_bridgeToObjectiveCUsingProtocolIfPossible( #define OVERRIDE_CASTING COMPATIBILITY_OVERRIDE #include "CompatibilityOverride.def" +// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX +// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX +// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX + +// A way for the new implementation to call the old one, so we +// can support switching between the two until the new one is +// fully settled. + +// XXX TODO XXX Once the new implementation is stable, remove the following, +// swift_dynamicCastImpl above, and all the other code above that only exists to +// support that. (Don't forget _dynamicCastToExistential!!) This file should +// be only ~1400 lines when you're done. + +extern "C" { + bool swift_dynamicCast_OLD(OpaqueValue *destLocation, + OpaqueValue *srcValue, + const Metadata *srcType, + const Metadata *destType, + DynamicCastFlags flags) + { + return swift_dynamicCastImpl(destLocation, srcValue, srcType, destType, flags); + } +} + +// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX +// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX +// XXX TODO XXX REMOVE XXX TRANSITION SHIM XXX diff --git a/stdlib/public/runtime/CompatibilityOverride.def b/stdlib/public/runtime/CompatibilityOverride.def index 92e32430251cd..40f6a055b3857 100644 --- a/stdlib/public/runtime/CompatibilityOverride.def +++ b/stdlib/public/runtime/CompatibilityOverride.def @@ -40,6 +40,7 @@ #ifdef OVERRIDE # define OVERRIDE_METADATALOOKUP OVERRIDE # define OVERRIDE_CASTING OVERRIDE +# define OVERRIDE_DYNAMICCASTING OVERRIDE # define OVERRIDE_OBJC OVERRIDE # define OVERRIDE_FOREIGN OVERRIDE # define OVERRIDE_PROTOCOLCONFORMANCE OVERRIDE @@ -52,6 +53,9 @@ # ifndef OVERRIDE_CASTING # define OVERRIDE_CASTING(...) # endif +# ifndef OVERRIDE_DYNAMICCASTING +# define OVERRIDE_DYNAMICCASTING(...) +# endif # ifndef OVERRIDE_OBJC # define OVERRIDE_OBJC(...) # endif @@ -69,12 +73,12 @@ # endif #endif -OVERRIDE_CASTING(dynamicCast, bool, , , swift::, - (OpaqueValue *dest, OpaqueValue *src, - const Metadata *srcType, - const Metadata *targetType, - DynamicCastFlags flags), - (dest, src, srcType, targetType, flags)) +OVERRIDE_DYNAMICCASTING(dynamicCast, bool, , , swift::, + (OpaqueValue *dest, OpaqueValue *src, + const Metadata *srcType, + const Metadata *targetType, + DynamicCastFlags flags), + (dest, src, srcType, targetType, flags)) OVERRIDE_CASTING(dynamicCastClass, const void *, , , swift::, @@ -212,6 +216,7 @@ OVERRIDE_FOREIGN(dynamicCastForeignClassUnconditional, const void *, , , swift:: #undef OVERRIDE #undef OVERRIDE_METADATALOOKUP #undef OVERRIDE_CASTING +#undef OVERRIDE_DYNAMICCASTING #undef OVERRIDE_OBJC #undef OVERRIDE_FOREIGN #undef OVERRIDE_PROTOCOLCONFORMANCE diff --git a/stdlib/public/runtime/DynamicCast.cpp b/stdlib/public/runtime/DynamicCast.cpp new file mode 100644 index 0000000000000..fa628e296fb2a --- /dev/null +++ b/stdlib/public/runtime/DynamicCast.cpp @@ -0,0 +1,2197 @@ +//===--- DynamicCast.cpp - Swift Language Dynamic Casting Support ---------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Implementations of the dynamic cast runtime functions. +// +//===----------------------------------------------------------------------===// + +#include "CompatibilityOverride.h" +#include "ErrorObject.h" +#include "Private.h" +#include "SwiftHashableSupport.h" +#include "swift/ABI/MetadataValues.h" +#include "swift/Basic/Lazy.h" +#include "swift/Runtime/Casting.h" +#include "swift/Runtime/Config.h" +#include "swift/Runtime/ExistentialContainer.h" +#include "swift/Runtime/HeapObject.h" +#if SWIFT_OBJC_INTEROP +#include "swift/Runtime/ObjCBridge.h" +#include "SwiftObject.h" +#include "SwiftValue.h" +#endif + +using namespace swift; +using namespace hashable_support; + +// +// The top-level driver code directly handles the most general cases +// (identity casts, _ObjectiveCBridgeable, _SwiftValue boxing) and +// recursively unwraps source and/or destination as appropriate. +// It calls "tryCastToXyz" functions to perform tailored operations +// for a particular destination type. +// +// For each kind of destination, there is a "tryCastToXyz" that +// accepts a source value and attempts to fit it into a destination +// storage location. This function should assume that: +// * The source and destination types are _not_ identical. +// * The destination is of the expected type. +// * The source is already fully unwrapped. If the source is an +// Existential or Optional that you cannot handle directly, do _not_ +// try to unwrap it. Just return failure and you will get called +// again with the unwrapped source. +// +// Each such function accepts the following arguments: +// * Destination location and type +// * Source value address and type +// * References to the types that will be used to report failure. +// The function can update these with specific failing types +// to improve the reported failure. +// * Bool indicating whether the compiler has asked us to "take" the +// value instead of copying. +// * Bool indicating whether it's okay to do type checks lazily on later +// access (this is permitted only for unconditional casts that will +// abort the program on failure anyway). +// +// The return value is one of the following: +// * Failure. In this case, the tryCastFunction should do nothing; your +// caller will either try another strategy or report the failure and +// do any necessary cleanup. +// * Success via "copy". You successfully copied the source value. +// * Success via "take". If "take" was requested and you can do so cheaply, +// perform the take and return SuccessViaTake. If "take" is not cheap, you +// should copy and return SuccessViaCopy. Top-level code will detect this +// and take care of destroying the source for you. +// +enum class DynamicCastResult { + Failure, /// The cast attempt "failed" (did nothing). + SuccessViaCopy, /// Cast succeeded, source is still valid. + SuccessViaTake, /// Cast succeeded, source is invalid +}; +static bool isSuccess(DynamicCastResult result) { + return result != DynamicCastResult::Failure; +} + +// All of our `tryCastXyz` functions have the following signature. +typedef DynamicCastResult (tryCastFunctionType)( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks +); + +// Forward-declare the main top-level `tryCast()` function +static tryCastFunctionType tryCast; + +/// Nominal type descriptor for Swift.AnyHashable +extern "C" const StructDescriptor STRUCT_TYPE_DESCR_SYM(s11AnyHashable); + +/// Nominal type descriptor for Swift.__SwiftValue +//extern "C" const StructDescriptor STRUCT_TYPE_DESCR_SYM(s12__SwiftValue); + +/// Nominal type descriptor for Swift.Array. +extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(Sa); + +/// Nominal type descriptor for Swift.Dictionary. +extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SD); + +/// Nominal type descriptor for Swift.Set. +extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(Sh); + +/// Nominal type descriptor for Swift.String. +extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS); + +static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue, + const Metadata *srcType, + const Metadata *destType) { + auto object = *reinterpret_cast(srcValue); + if (LLVM_LIKELY(object != nullptr)) { + return object; + } + + std::string srcTypeName = nameForMetadata(srcType); + std::string destTypeName = nameForMetadata(destType); + swift::fatalError(/* flags = */ 0, + "Found unexpected null pointer value" + " while trying to cast value of type '%s' (%p)" + " to '%s' (%p)\n", + srcTypeName.c_str(), srcType, + destTypeName.c_str(), destType); +} + +/******************************************************************************/ +/******************************* Bridge Helpers *******************************/ +/******************************************************************************/ + +#define _bridgeAnythingToObjectiveC \ + MANGLE_SYM(s27_bridgeAnythingToObjectiveCyyXlxlF) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API +HeapObject *_bridgeAnythingToObjectiveC( + OpaqueValue *src, const Metadata *srcType); + +#if SWIFT_OBJC_INTEROP +SWIFT_RUNTIME_EXPORT +id swift_dynamicCastMetatypeToObjectConditional(const Metadata *metatype); +#endif + +// protocol _ObjectiveCBridgeable { +struct _ObjectiveCBridgeableWitnessTable : WitnessTable { + static_assert(WitnessTableFirstRequirementOffset == 1, + "Witness table layout changed"); + + // associatedtype _ObjectiveCType : class + void *_ObjectiveCType; + + // func _bridgeToObjectiveC() -> _ObjectiveCType + SWIFT_CC(swift) + HeapObject *(*bridgeToObjectiveC)( + SWIFT_CONTEXT OpaqueValue *self, const Metadata *Self, + const _ObjectiveCBridgeableWitnessTable *witnessTable); + + // class func _forceBridgeFromObjectiveC(x: _ObjectiveCType, + // inout result: Self?) + SWIFT_CC(swift) + void (*forceBridgeFromObjectiveC)( + HeapObject *sourceValue, + OpaqueValue *result, + SWIFT_CONTEXT const Metadata *self, + const Metadata *selfType, + const _ObjectiveCBridgeableWitnessTable *witnessTable); + + // class func _conditionallyBridgeFromObjectiveC(x: _ObjectiveCType, + // inout result: Self?) -> Bool + SWIFT_CC(swift) + bool (*conditionallyBridgeFromObjectiveC)( + HeapObject *sourceValue, + OpaqueValue *result, + SWIFT_CONTEXT const Metadata *self, + const Metadata *selfType, + const _ObjectiveCBridgeableWitnessTable *witnessTable); +}; +// } + +extern "C" const ProtocolDescriptor +PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable); + +static const _ObjectiveCBridgeableWitnessTable * +findBridgeWitness(const Metadata *T) { + static const auto bridgeableProtocol + = &PROTOCOL_DESCR_SYM(s21_ObjectiveCBridgeable); + auto w = swift_conformsToProtocol(T, bridgeableProtocol); + return reinterpret_cast(w); +} + +/// Retrieve the bridged Objective-C type for the given type that +/// conforms to \c _ObjectiveCBridgeable. +MetadataResponse +_getBridgedObjectiveCType( + MetadataRequest request, + const Metadata *conformingType, + const _ObjectiveCBridgeableWitnessTable *wtable) +{ + // FIXME: Can we directly reference the descriptor somehow? + const ProtocolConformanceDescriptor *conformance = wtable->getDescription(); + const ProtocolDescriptor *protocol = conformance->getProtocol(); + auto assocTypeRequirement = protocol->getRequirements().begin(); + assert(assocTypeRequirement->Flags.getKind() == + ProtocolRequirementFlags::Kind::AssociatedTypeAccessFunction); + auto mutableWTable = (WitnessTable *)wtable; + return swift_getAssociatedTypeWitness( + request, mutableWTable, conformingType, + protocol->getRequirementBaseDescriptor(), + assocTypeRequirement); +} + +/// Dynamic cast from a class type to a value type that conforms to the +/// _ObjectiveCBridgeable, first by dynamic casting the object to the +/// class to which the value type is bridged, and then bridging +/// from that object to the value type via the witness table. +/// +/// Caveat: Despite the name, this is also used to bridge pure Swift +/// classes to Swift value types even when Obj-C is not being used. + +static DynamicCastResult +_tryCastFromClassToObjCBridgeable( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, void *srcObject, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks, + const _ObjectiveCBridgeableWitnessTable *destBridgeWitness, + const Metadata *targetBridgeClass) +{ + + // 2. Allocate a T? to receive the bridge result. + + // The extra byte is for the tag. + auto targetSize = destType->getValueWitnesses()->getSize() + 1; + auto targetAlignMask = destType->getValueWitnesses()->getAlignmentMask(); + + // Object that frees a buffer when it goes out of scope. + struct FreeBuffer { + void *Buffer = nullptr; + size_t size, alignMask; + FreeBuffer(size_t size, size_t alignMask) : + size(size), alignMask(alignMask) {} + + ~FreeBuffer() { + if (Buffer) + swift_slowDealloc(Buffer, size, alignMask); + } + } freeBuffer{targetSize, targetAlignMask}; + + // The extra byte is for the tag on the T? + const std::size_t inlineValueSize = 3 * sizeof(void*); + alignas(std::max_align_t) char inlineBuffer[inlineValueSize + 1]; + void *optDestBuffer; + if (destType->getValueWitnesses()->getStride() <= inlineValueSize) { + // Use the inline buffer. + optDestBuffer = inlineBuffer; + } else { + // Allocate a buffer. + optDestBuffer = swift_slowAlloc(targetSize, targetAlignMask); + freeBuffer.Buffer = optDestBuffer; + } + + // Initialize the buffer as an empty optional. + destType->vw_storeEnumTagSinglePayload((OpaqueValue *)optDestBuffer, + 1, 1); + + // 3. Bridge into the T? (Effectively a copy operation.) + bool success; + if (mayDeferChecks) { + destBridgeWitness->forceBridgeFromObjectiveC( + (HeapObject *)srcObject, (OpaqueValue *)optDestBuffer, + destType, destType, destBridgeWitness); + success = true; + } else { + success = destBridgeWitness->conditionallyBridgeFromObjectiveC( + (HeapObject *)srcObject, (OpaqueValue *)optDestBuffer, + destType, destType, destBridgeWitness); + } + + // If we succeeded, then take the value from the temp buffer. + if (success) { + destType->vw_initializeWithTake(destLocation, (OpaqueValue *)optDestBuffer); + // Bridge above is effectively a copy, so overall we're a copy. + return DynamicCastResult::SuccessViaCopy; + } + return DynamicCastResult::Failure; +} + +static DynamicCastResult +tryCastFromClassToObjCBridgeable( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + // We need the _ObjectiveCBridgeable conformance for the target + auto destBridgeWitness = findBridgeWitness(destType); + if (destBridgeWitness == nullptr) { + return DynamicCastResult::Failure; + } + + // 1. Sanity check whether the source object can cast to the + // type expected by the target. + + auto targetBridgedClass = + _getBridgedObjectiveCType(MetadataState::Complete, destType, + destBridgeWitness).Value; + void *srcObject = getNonNullSrcObject(srcValue, srcType, destType); + if (nullptr == swift_dynamicCastUnknownClass(srcObject, targetBridgedClass)) { + destFailureType = targetBridgedClass; + return DynamicCastResult::Failure; + } + + return _tryCastFromClassToObjCBridgeable( + destLocation, destType, + srcValue, srcType, srcObject, + destFailureType, srcFailureType, + takeOnSuccess, mayDeferChecks, + destBridgeWitness, targetBridgedClass); +} + +/// Dynamic cast from a value type that conforms to the +/// _ObjectiveCBridgeable protocol to a class type, first by bridging +/// the value to its Objective-C object representation and then by +/// dynamic casting that object to the resulting target type. +/// +/// Caveat: Despite the name, this is also used to bridge Swift value types +/// to Swift classes even when Obj-C is not being used. +static DynamicCastResult +tryCastFromObjCBridgeableToClass( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + auto srcBridgeWitness = findBridgeWitness(srcType); + if (srcBridgeWitness == nullptr) { + return DynamicCastResult::Failure; + } + + // Bridge the source value to an object. + auto srcBridgedObject = + srcBridgeWitness->bridgeToObjectiveC(srcValue, srcType, srcBridgeWitness); + + // Dynamic cast the object to the resulting class type. + if (auto cast = swift_dynamicCastUnknownClass(srcBridgedObject, destType)) { + *reinterpret_cast(destLocation) = cast; + return DynamicCastResult::SuccessViaCopy; + } else { + // We don't need the object anymore. + swift_unknownObjectRelease(srcBridgedObject); + return DynamicCastResult::Failure; + } +} + +/******************************************************************************/ +/****************************** SwiftValue Boxing *****************************/ +/******************************************************************************/ + +#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +bool swift_unboxFromSwiftValueWithType(OpaqueValue *source, + OpaqueValue *result, + const Metadata *destinationType); + +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +bool swift_swiftValueConformsTo(const Metadata *, const Metadata *); +#endif + +#if SWIFT_OBJC_INTEROP +// Try unwrapping a source holding an Obj-C SwiftValue container and +// recursively casting the contents. +static DynamicCastResult +tryCastUnwrappingObjCSwiftValueSource( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + id srcObject; + memcpy(&srcObject, srcValue, sizeof(id)); + auto srcSwiftValue = getAsSwiftValue(srcObject); + + if (srcSwiftValue == nullptr) { + return DynamicCastResult::Failure; + } + + const Metadata *srcInnerType; + const OpaqueValue *srcInnerValue; + std::tie(srcInnerType, srcInnerValue) + = getValueFromSwiftValue(srcSwiftValue); + // Note: We never `take` the contents from a SwiftValue box as + // it might have other references. Instead, let our caller + // destroy the reference if necessary. + return tryCast( + destLocation, destType, + const_cast(srcInnerValue), srcInnerType, + destFailureType, srcFailureType, + /*takeOnSuccess=*/ false, mayDeferChecks); +} +#endif + +/******************************************************************************/ +/****************************** Class Destination *****************************/ +/******************************************************************************/ + +static DynamicCastResult +tryCastToSwiftClass( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Class); + + auto destClassType = cast(destType); + switch (srcType->getKind()) { + case MetadataKind::Class: // Swift class => Swift class + case MetadataKind::ObjCClassWrapper: { // Obj-C class => Swift class + void *object = getNonNullSrcObject(srcValue, srcType, destType); + if (auto t = swift_dynamicCastClass(object, destClassType)) { + auto castObject = const_cast(t); + *(reinterpret_cast(destLocation)) = castObject; + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + swift_unknownObjectRetain(castObject); + return DynamicCastResult::SuccessViaCopy; + } + } else { + srcFailureType = srcType; + destFailureType = destType; + return DynamicCastResult::Failure; + } + } + + case MetadataKind::ForeignClass: // CF class => Swift class + // Top-level code will "unwrap" to an Obj-C class and try again. + return DynamicCastResult::Failure; + + default: + return DynamicCastResult::Failure; + } +} + +static DynamicCastResult +tryCastToObjectiveCClass( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::ObjCClassWrapper); +#if SWIFT_OBJC_INTEROP + auto destObjCType = cast(destType); + + switch (srcType->getKind()) { + case MetadataKind::Class: // Swift class => Obj-C class + case MetadataKind::ObjCClassWrapper: // Obj-C class => Obj-C class + case MetadataKind::ForeignClass: { // CF class => Obj-C class + auto srcObject = getNonNullSrcObject(srcValue, srcType, destType); + auto destObjCClass = destObjCType->Class; + if (auto resultObject + = swift_dynamicCastObjCClass(srcObject, destObjCClass)) { + *reinterpret_cast(destLocation) = resultObject; + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + objc_retain((id)const_cast(resultObject)); + return DynamicCastResult::SuccessViaCopy; + } + } + break; + } + + default: + break; + } +#endif + + return DynamicCastResult::Failure; +} + +static DynamicCastResult +tryCastToForeignClass( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ +#if SWIFT_OBJC_INTEROP + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::ForeignClass); + auto destClassType = cast(destType); + + switch (srcType->getKind()) { + case MetadataKind::Class: // Swift class => CF class + case MetadataKind::ObjCClassWrapper: // Obj-C class => CF class + case MetadataKind::ForeignClass: { // CF class => CF class + auto srcObject = getNonNullSrcObject(srcValue, srcType, destType); + if (auto resultObject + = swift_dynamicCastForeignClass(srcObject, destClassType)) { + *reinterpret_cast(destLocation) = resultObject; + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + objc_retain((id)const_cast(resultObject)); + return DynamicCastResult::SuccessViaCopy; + } + } + break; + } + default: + break; + } +#endif + + return DynamicCastResult::Failure; +} + +/******************************************************************************/ +/***************************** Enum Destination *******************************/ +/******************************************************************************/ + +static DynamicCastResult +tryCastToEnum( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + // Note: Optional is handled elsewhere + assert(destType->getKind() == MetadataKind::Enum); + + // Enum has no special cast support at present. + + return DynamicCastResult::Failure; +} + +/******************************************************************************/ +/**************************** Struct Destination ******************************/ +/******************************************************************************/ + +// internal func _arrayDownCastIndirect( +// _ source: UnsafePointer>, +// _ target: UnsafeMutablePointer>) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +void _swift_arrayDownCastIndirect(OpaqueValue *destination, + OpaqueValue *source, + const Metadata *sourceValueType, + const Metadata *targetValueType); + +// internal func _arrayDownCastConditionalIndirect( +// _ source: UnsafePointer>, +// _ target: UnsafeMutablePointer> +// ) -> Bool +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +bool _swift_arrayDownCastConditionalIndirect(OpaqueValue *destination, + OpaqueValue *source, + const Metadata *sourceValueType, + const Metadata *targetValueType); + +// internal func _setDownCastIndirect( +// _ source: UnsafePointer>, +// _ target: UnsafeMutablePointer>) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +void _swift_setDownCastIndirect(OpaqueValue *destination, + OpaqueValue *source, + const Metadata *sourceValueType, + const Metadata *targetValueType, + const void *sourceValueHashable, + const void *targetValueHashable); + +// internal func _setDownCastConditionalIndirect( +// _ source: UnsafePointer>, +// _ target: UnsafeMutablePointer> +// ) -> Bool +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +bool _swift_setDownCastConditionalIndirect(OpaqueValue *destination, + OpaqueValue *source, + const Metadata *sourceValueType, + const Metadata *targetValueType, + const void *sourceValueHashable, + const void *targetValueHashable); + +// internal func _dictionaryDownCastIndirect( +// _ source: UnsafePointer>, +// _ target: UnsafeMutablePointer>) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +void _swift_dictionaryDownCastIndirect(OpaqueValue *destination, + OpaqueValue *source, + const Metadata *sourceKeyType, + const Metadata *sourceValueType, + const Metadata *targetKeyType, + const Metadata *targetValueType, + const void *sourceKeyHashable, + const void *targetKeyHashable); + +// internal func _dictionaryDownCastConditionalIndirect( +// _ source: UnsafePointer>, +// _ target: UnsafeMutablePointer> +// ) -> Bool +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL +bool _swift_dictionaryDownCastConditionalIndirect(OpaqueValue *destination, + OpaqueValue *source, + const Metadata *sourceKeyType, + const Metadata *sourceValueType, + const Metadata *targetKeyType, + const Metadata *targetValueType, + const void *sourceKeyHashable, + const void *targetKeyHashable); + +#if SWIFT_OBJC_INTEROP +// Helper to memoize bridging conformance data for a particular +// Swift struct type. This is used to speed up the most common +// ObjC->Swift bridging conversions by eliminating repeeated +// protocol conformance lookups. + +// Currently used only for String, which may be the only +// type used often enough to justify the extra static memory. +struct ObjCBridgeMemo { +#if !NDEBUG + // Used in assert build to verify that we always get called with + // the same destType + const Metadata *destType; +#endif + const _ObjectiveCBridgeableWitnessTable *destBridgeWitness; + const Metadata *targetBridgedType; + Class targetBridgedObjCClass; + swift_once_t fetchWitnessOnce; + + DynamicCastResult tryBridge( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) + { + struct SetupData { + const Metadata *destType; + struct ObjCBridgeMemo *memo; + } setupData { destType, this }; + + swift_once(&fetchWitnessOnce, + [](void *data) { + struct SetupData *setupData = (struct SetupData *)data; + struct ObjCBridgeMemo *memo = setupData->memo; +#if !NDEBUG + memo->destType = setupData->destType; +#endif + memo->destBridgeWitness = findBridgeWitness(setupData->destType); + if (memo->destBridgeWitness == nullptr) { + memo->targetBridgedType = nullptr; + memo->targetBridgedObjCClass = nullptr; + } else { + memo->targetBridgedType = _getBridgedObjectiveCType( + MetadataState::Complete, setupData->destType, memo->destBridgeWitness).Value; + assert(memo->targetBridgedType->getKind() == MetadataKind::ObjCClassWrapper); + memo->targetBridgedObjCClass = memo->targetBridgedType->getObjCClassObject(); + assert(memo->targetBridgedObjCClass != nullptr); + } + }, (void *)&setupData); + + // Check that this always gets called with the same destType. + assert((destType == this->destType) && "ObjC casting memo used inconsistently"); + + // !! If bridging is not usable, stop here. + if (targetBridgedObjCClass == nullptr) { + return DynamicCastResult::Failure; + } + // Use the dynamic ISA type of the object always (Note that this + // also implicitly gives us the ObjC type for a CF object.) + void *srcObject = getNonNullSrcObject(srcValue, srcType, destType); + Class srcObjCType = object_getClass((id)srcObject); + // Fail if the ObjC object is not a subclass of the bridge class. + while (srcObjCType != targetBridgedObjCClass) { + srcObjCType = class_getSuperclass(srcObjCType); + if (srcObjCType == nullptr) { + return DynamicCastResult::Failure; + } + } + // The ObjC object is an acceptable type, so call the bridge function... + return _tryCastFromClassToObjCBridgeable( + destLocation, destType, srcValue, srcType, srcObject, + destFailureType, srcFailureType, + takeOnSuccess, mayDeferChecks, + destBridgeWitness, targetBridgedType); + } +}; +#endif + +static DynamicCastResult +tryCastToAnyHashable( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Struct); + assert(cast(destType)->Description + == &STRUCT_TYPE_DESCR_SYM(s11AnyHashable)); + + auto hashableConformance = reinterpret_cast( + swift_conformsToProtocol(srcType, &HashableProtocolDescriptor)); + if (hashableConformance) { + _swift_convertToAnyHashableIndirect(srcValue, destLocation, + srcType, hashableConformance); + return DynamicCastResult::SuccessViaCopy; + } else { + return DynamicCastResult::Failure; + } +} + +static DynamicCastResult +tryCastToArray( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Struct); + assert(cast(destType)->Description + == &NOMINAL_TYPE_DESCR_SYM(Sa)); + + switch (srcType->getKind()) { + case MetadataKind::Struct: { // Struct -> Array + const auto srcStructType = cast(srcType); + if (srcStructType->Description == &NOMINAL_TYPE_DESCR_SYM(Sa)) { // Array -> Array + auto sourceArgs = srcType->getGenericArgs(); + auto destArgs = destType->getGenericArgs(); + if (mayDeferChecks) { + _swift_arrayDownCastIndirect( + srcValue, destLocation, sourceArgs[0], destArgs[0]); + return DynamicCastResult::SuccessViaCopy; + } else { + auto result = _swift_arrayDownCastConditionalIndirect( + srcValue, destLocation, sourceArgs[0], destArgs[0]); + if (result) { + return DynamicCastResult::SuccessViaCopy; + } + } + } + break; + } + + default: + break; + } + + return DynamicCastResult::Failure; +} + +static DynamicCastResult +tryCastToDictionary( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Struct); + assert(cast(destType)->Description + == &NOMINAL_TYPE_DESCR_SYM(SD)); + + switch (srcType->getKind()) { + case MetadataKind::Struct: { // Struct -> Dictionary + const auto srcStructType = cast(srcType); + if (srcStructType->Description == &NOMINAL_TYPE_DESCR_SYM(SD)) { // Dictionary -> Dictionary + auto sourceArgs = srcType->getGenericArgs(); + auto destArgs = destType->getGenericArgs(); + if (mayDeferChecks) { + _swift_dictionaryDownCastIndirect( + srcValue, destLocation, sourceArgs[0], sourceArgs[1], + destArgs[0], destArgs[1], sourceArgs[2], destArgs[2]); + return DynamicCastResult::SuccessViaCopy; + } else { + auto result = _swift_dictionaryDownCastConditionalIndirect( + srcValue, destLocation, sourceArgs[0], sourceArgs[1], + destArgs[0], destArgs[1], sourceArgs[2], destArgs[2]); + if (result) { + return DynamicCastResult::SuccessViaCopy; + } + } + } + break; + } + + default: + break; + } + return DynamicCastResult::Failure; +} + +static DynamicCastResult +tryCastToSet( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Struct); + assert(cast(destType)->Description + == &NOMINAL_TYPE_DESCR_SYM(Sh)); + + switch (srcType->getKind()) { + + case MetadataKind::Struct: { // Struct -> Set + const auto srcStructType = cast(srcType); + if (srcStructType->Description == &NOMINAL_TYPE_DESCR_SYM(Sh)) { // Set -> Set + auto sourceArgs = srcType->getGenericArgs(); + auto destArgs = destType->getGenericArgs(); + if (mayDeferChecks) { + _swift_setDownCastIndirect(srcValue, destLocation, + sourceArgs[0], destArgs[0], sourceArgs[1], destArgs[1]); + return DynamicCastResult::SuccessViaCopy; + } else { + auto result = _swift_setDownCastConditionalIndirect( + srcValue, destLocation, + sourceArgs[0], destArgs[0], + sourceArgs[1], destArgs[1]); + if (result) { + return DynamicCastResult::SuccessViaCopy; + } + } + } + break; + } + + default: + break; + } + return DynamicCastResult::Failure; +} + +static DynamicCastResult +tryCastToString( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Struct); + assert(cast(destType)->Description + == &NOMINAL_TYPE_DESCR_SYM(SS)); + + switch (srcType->getKind()) { + case MetadataKind::ForeignClass: // CF -> String + case MetadataKind::ObjCClassWrapper: { // Obj-C -> String +#if SWIFT_OBJC_INTEROP + static ObjCBridgeMemo memo; + return memo.tryBridge( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, + takeOnSuccess, mayDeferChecks); +#endif + } + default: + return DynamicCastResult::Failure; + } + return DynamicCastResult::Failure; +} + +static DynamicCastResult +tryCastToStruct( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Struct); + + // There is no special cast handling at present for general Struct types. + + // Special logic for AnyHashable, Set, Dictionary, Array, and String + // is broken out above. See also selectCasterForDest() for the + // logic that chooses one of these functions. + + return DynamicCastResult::Failure; +} + +/******************************************************************************/ +/*************************** Optional Destination *****************************/ +/******************************************************************************/ + +static DynamicCastResult +tryCastToOptional( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Optional); + + // Nothing to do for the basic casting operation. + // Unwrapping is handled by top-level tryCast with assistance + // from utility functions below. + + return DynamicCastResult::Failure; +} + +// The nil value `T?.none` can be cast to any optional type. +// When the unwrapper sees a source value that is nil, it calls +// tryCastFromNil() to try to set the target optional to nil. +// +// This is complicated by the desire to preserve the nesting +// as far as possible. For example, we would like: +// Int?.none => Int??.some(.none) +// Int??.none => Any????.some(.some(.none)) +// Of course, if the target is shallower than the source, +// then we have to just set the outermost optional to nil. + +// This helper sets a nested optional to nil at a requested level: +static void +initializeToNilAtDepth(OpaqueValue *destLocation, const Metadata *destType, int depth) { + assert(destType->getKind() == MetadataKind::Optional); + auto destInnerType = cast(destType)->getGenericArgs()[0]; + if (depth > 0) { + initializeToNilAtDepth(destLocation, destInnerType, depth - 1); + // Set .some at each level as we unwind + destInnerType->vw_storeEnumTagSinglePayload( + destLocation, 0, 1); + } else { + // Set .none at the lowest level + destInnerType->vw_storeEnumTagSinglePayload( + destLocation, 1, 1); + } +} + +static void +copyNil(OpaqueValue *destLocation, const Metadata *destType, const Metadata *srcType) +{ + assert(srcType->getKind() == MetadataKind::Optional); + assert(destType->getKind() == MetadataKind::Optional); + + // Measure how deep the source nil is: Is it Int?.none or Int??.none or ... + auto srcInnerType = cast(srcType)->getGenericArgs()[0]; + int srcDepth = 1; + while (srcInnerType->getKind() == MetadataKind::Optional) { + srcInnerType = cast(srcInnerType)->getGenericArgs()[0]; + srcDepth += 1; + } + + // Measure how deep the destination optional is + auto destInnerType = cast(destType)->getGenericArgs()[0]; + int destDepth = 1; + while (destInnerType->getKind() == MetadataKind::Optional) { + destInnerType = cast(destInnerType)->getGenericArgs()[0]; + destDepth += 1; + } + + // Recursively set the destination to .some(.some(... .some(.none))) + auto targetDepth = std::max(destDepth - srcDepth, 0); + initializeToNilAtDepth(destLocation, destType, targetDepth); +} + +// Try unwrapping both source and dest optionals together. +// If the source is nil, then cast that to the destination. +static DynamicCastResult +tryCastUnwrappingOptionalBoth( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(destType->getKind() == MetadataKind::Optional); + assert(srcType->getKind() == MetadataKind::Optional); + + auto srcInnerType = cast(srcType)->getGenericArgs()[0]; + unsigned sourceEnumCase = srcInnerType->vw_getEnumTagSinglePayload( + srcValue, /*emptyCases=*/1); + auto sourceIsNil = (sourceEnumCase != 0); + if (sourceIsNil) { + copyNil(destLocation, destType, srcType); + return DynamicCastResult::SuccessViaCopy; // nil was essentially copied to dest + } else { + auto destEnumType = cast(destType); + const Metadata *destInnerType = destEnumType->getGenericArgs()[0]; + auto destInnerLocation = destLocation; // Single-payload enum layout + auto subcastResult = tryCast( + destInnerLocation, destInnerType, srcValue, srcInnerType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + destInnerType->vw_storeEnumTagSinglePayload( + destLocation, /*case*/ 0, /*emptyCases*/ 1); + } + return subcastResult; + } + return DynamicCastResult::Failure; +} + +// Try unwrapping just the destination optional. +// Note we do this even if both src and dest are optional. +// For example, Int -> Any? requires unwrapping the destination +// in order to inject the Int into the existential. +static DynamicCastResult +tryCastUnwrappingOptionalDestination( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(destType->getKind() == MetadataKind::Optional); + + auto destEnumType = cast(destType); + const Metadata *destInnerType = destEnumType->getGenericArgs()[0]; + auto destInnerLocation = destLocation; // Single-payload enum layout + auto subcastResult = tryCast( + destInnerLocation, destInnerType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + destInnerType->vw_storeEnumTagSinglePayload( + destLocation, /*case*/ 0, /*emptyCases*/ 1); + } + return subcastResult; +} + +// Try unwrapping just the source optional. +// Note we do this even if both target and dest are optional. +// For example, this is used when extracting the contents of +// an Optional. +static DynamicCastResult +tryCastUnwrappingOptionalSource( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType->getKind() == MetadataKind::Optional); + + auto srcInnerType = cast(srcType)->getGenericArgs()[0]; + unsigned sourceEnumCase = srcInnerType->vw_getEnumTagSinglePayload( + srcValue, /*emptyCases=*/1); + auto nonNil = (sourceEnumCase == 0); + if (nonNil) { + // Recurse with unwrapped source + return tryCast(destLocation, destType, srcValue, srcInnerType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + } + return DynamicCastResult::Failure; +} + +/******************************************************************************/ +/***************************** Tuple Destination ******************************/ +/******************************************************************************/ + +// The only thing that can be legally cast to a tuple is another tuple. +// Most of the logic below is required to handle element-wise casts of +// tuples that are compatible but not of the same type. + +static DynamicCastResult +tryCastToTuple( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Tuple); + const auto destTupleType = cast(destType); + + srcFailureType = srcType; + destFailureType = destType; + + // We cannot cast non-tuple data to a tuple + if (srcType->getKind() != MetadataKind::Tuple) { + return DynamicCastResult::Failure; + } + const auto srcTupleType = cast(srcType); + + // Tuples must have same number of elements + if (srcTupleType->NumElements != destTupleType->NumElements) { + return DynamicCastResult::Failure; + } + + // Common labels must match + const char *srcLabels = srcTupleType->Labels; + const char *destLabels = destTupleType->Labels; + while (srcLabels != nullptr + && destLabels != nullptr + && srcLabels != destLabels) + { + const char *srcSpace = strchr(srcLabels, ' '); + const char *destSpace = strchr(destLabels, ' '); + + // If we've reached the end of either label string, we're done. + if (srcSpace == nullptr || destSpace == nullptr) { + break; + } + + // If both labels are non-empty, and the labels mismatch, we fail. + if (srcSpace != srcLabels && destSpace != destLabels) { + unsigned srcLen = srcSpace - srcLabels; + unsigned destLen = destSpace - destLabels; + if (srcLen != destLen || + strncmp(srcLabels, destLabels, srcLen) != 0) + return DynamicCastResult::Failure; + } + + srcLabels = srcSpace + 1; + destLabels = destSpace + 1; + } + + // Compare the element types to see if they all match. + bool typesMatch = true; + auto numElements = srcTupleType->NumElements; + for (unsigned i = 0; typesMatch && i != numElements; ++i) { + if (srcTupleType->getElement(i).Type != destTupleType->getElement(i).Type) { + typesMatch = false; + } + } + + if (typesMatch) { + // The actual element types are identical, so we can use the + // fast value-witness machinery for the whole tuple. + if (takeOnSuccess) { + srcType->vw_initializeWithTake(destLocation, srcValue); + return DynamicCastResult::SuccessViaTake; + } else { + srcType->vw_initializeWithCopy(destLocation, srcValue); + return DynamicCastResult::SuccessViaCopy; + } + } else { + // Slow path casts each item separately. + for (unsigned j = 0, n = srcTupleType->NumElements; j != n; ++j) { + const auto &srcElt = srcTupleType->getElement(j); + const auto &destElt = destTupleType->getElement(j); + auto subcastResult = tryCast(destElt.findIn(destLocation), destElt.Type, + srcElt.findIn(srcValue), srcElt.Type, + destFailureType, srcFailureType, + false, mayDeferChecks); + if (subcastResult == DynamicCastResult::Failure) { + for (unsigned k = 0; k != j; ++k) { + const auto &elt = destTupleType->getElement(k); + elt.Type->vw_destroy(elt.findIn(destLocation)); + } + return DynamicCastResult::Failure; + } + } + // We succeeded by casting each item. + return DynamicCastResult::SuccessViaCopy; + } + +} + +/******************************************************************************/ +/**************************** Function Destination ****************************/ +/******************************************************************************/ + +// The only thing that can be legally cast to a function is another function. + +static DynamicCastResult +tryCastToFunction( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Function); + const auto destFuncType = cast(destType); + + // Function casts succeed on exact matches, or if the target type is + // throwier than the source type. + // + // TODO: We could also allow ABI-compatible variance, such as casting + // a dynamic Base -> Derived to Derived -> Base. We wouldn't be able to + // perform a dynamic cast that required any ABI adjustment without a JIT + // though. + + if (srcType->getKind() != MetadataKind::Function) { + return DynamicCastResult::Failure; + } + auto srcFuncType = cast(srcType); + + // Check that argument counts and convention match (but ignore + // "throws" for now). + if (srcFuncType->Flags.withThrows(false) + != destFuncType->Flags.withThrows(false)) { + return DynamicCastResult::Failure; + } + + // If the target type can't throw, neither can the source. + if (srcFuncType->isThrowing() && !destFuncType->isThrowing()) + return DynamicCastResult::Failure; + + // The result and argument types must match. + if (srcFuncType->ResultType != destFuncType->ResultType) + return DynamicCastResult::Failure; + if (srcFuncType->getNumParameters() != destFuncType->getNumParameters()) + return DynamicCastResult::Failure; + if (srcFuncType->hasParameterFlags() != destFuncType->hasParameterFlags()) + return DynamicCastResult::Failure; + for (unsigned i = 0, e = srcFuncType->getNumParameters(); i < e; ++i) { + if (srcFuncType->getParameter(i) != destFuncType->getParameter(i) || + srcFuncType->getParameterFlags(i) != destFuncType->getParameterFlags(i)) + return DynamicCastResult::Failure; + } + + // Everything matches, so we can take/copy the function reference. + if (takeOnSuccess) { + srcType->vw_initializeWithTake(destLocation, srcValue); + return DynamicCastResult::SuccessViaTake; + } else { + srcType->vw_initializeWithCopy(destLocation, srcValue); + return DynamicCastResult::SuccessViaCopy; + } +} + +/******************************************************************************/ +/************************** Existential Destination ***************************/ +/******************************************************************************/ + +/// Check whether a type conforms to the given protocols, filling in a +/// list of conformances. +static bool _conformsToProtocols(const OpaqueValue *value, + const Metadata *type, + const ExistentialTypeMetadata *existentialType, + const WitnessTable **conformances) { + if (auto *superclass = existentialType->getSuperclassConstraint()) { + if (!swift_dynamicCastMetatype(type, superclass)) + return false; + } + + if (existentialType->isClassBounded()) { + if (!Metadata::isAnyKindOfClass(type->getKind())) + return false; + } + + for (auto protocol : existentialType->getProtocols()) { + if (!swift::_conformsToProtocol(value, type, protocol, conformances)) + return false; + if (conformances != nullptr && protocol.needsWitnessTable()) { + assert(*conformances != nullptr); + ++conformances; + } + } + + return true; +} + +// Cast to unconstrained `Any` +static DynamicCastResult +tryCastToUnconstrainedOpaqueExistential( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Existential); + assert(cast(destType)->getRepresentation() + == ExistentialTypeRepresentation::Opaque); + auto destExistential + = reinterpret_cast(destLocation); + + // Fill in the type and value. + destExistential->Type = srcType; + auto *destBox = srcType->allocateBoxForExistentialIn(&destExistential->Buffer); + if (takeOnSuccess) { + srcType->vw_initializeWithTake(destBox, srcValue); + return DynamicCastResult::SuccessViaTake; + } else { + srcType->vw_initializeWithCopy(destBox, srcValue); + return DynamicCastResult::SuccessViaCopy; + } +} + +// Cast to constrained `Any` +static DynamicCastResult +tryCastToConstrainedOpaqueExistential( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Existential); + auto destExistentialType = cast(destType); + assert(destExistentialType->getRepresentation() + == ExistentialTypeRepresentation::Opaque); + auto destExistential + = reinterpret_cast(destLocation); + + // Check for protocol conformances and fill in the witness tables. + // TODO (rdar://17033499) If the source is an existential, we should + // be able to compare the protocol constraints more efficiently than this. + if (_conformsToProtocols(srcValue, srcType, destExistentialType, + destExistential->getWitnessTables())) { + return tryCastToUnconstrainedOpaqueExistential( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + } else { + return DynamicCastResult::Failure; + } +} + +static DynamicCastResult +tryCastToClassExistential( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Existential); + auto destExistentialType = cast(destType); + assert(destExistentialType->getRepresentation() + == ExistentialTypeRepresentation::Class); + auto destExistentialLocation + = reinterpret_cast(destLocation); + + MetadataKind srcKind = srcType->getKind(); + switch (srcKind) { + + case MetadataKind::Metatype: { +#if SWIFT_OBJC_INTEROP + // Get an object reference to the metatype and try fitting that into + // the existential... + auto metatypePtr = reinterpret_cast(srcValue); + auto metatype = *metatypePtr; + if (auto tmp = swift_dynamicCastMetatypeToObjectConditional(metatype)) { + auto value = reinterpret_cast(&tmp); + auto type = reinterpret_cast(tmp); + if (_conformsToProtocols(value, type, destExistentialType, + destExistentialLocation->getWitnessTables())) { + auto object = *(reinterpret_cast(value)); + destExistentialLocation->Value = object; + if (takeOnSuccess) { + // We copied the pointer without retain, so the source is no + // longer valid... + return DynamicCastResult::SuccessViaTake; + } else { + swift_unknownObjectRetain(object); + return DynamicCastResult::SuccessViaCopy; + } + } else { + // We didn't assign to destination, so the source reference + // is still valid and the reference count is still correct. + } + } +#endif + return DynamicCastResult::Failure; + } + + case MetadataKind::ObjCClassWrapper: + case MetadataKind::Class: + case MetadataKind::ForeignClass: { + auto object = getNonNullSrcObject(srcValue, srcType, destType); + if (_conformsToProtocols(srcValue, srcType, + destExistentialType, + destExistentialLocation->getWitnessTables())) { + destExistentialLocation->Value = object; + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + swift_unknownObjectRetain(object); + return DynamicCastResult::SuccessViaCopy; + } + } + return DynamicCastResult::Failure; + } + + default: + return DynamicCastResult::Failure; + } + + return DynamicCastResult::Failure; +} + +// SwiftValue boxing is a failsafe that we only want to invoke +// after other approaches have failed. This is why it's not +// integrated into tryCastToClassExistential() above. +static DynamicCastResult +tryCastToClassExistentialViaSwiftValue( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Existential); + auto destExistentialType = cast(destType); + assert(destExistentialType->getRepresentation() + == ExistentialTypeRepresentation::Class); + auto destExistentialLocation + = reinterpret_cast(destLocation); + + // Fail if the target has constraints that make it unsuitable for + // a __SwiftValue box. + // FIXME: We should not have different checks here for + // Obj-C vs non-Obj-C. The _SwiftValue boxes should conform + // to the exact same protocols on both platforms. + bool destIsConstrained = destExistentialType->NumProtocols != 0; + if (destIsConstrained) { +#if SWIFT_OBJC_INTEROP // __SwiftValue is an Obj-C class + if (!findSwiftValueConformances( + destExistentialType, destExistentialLocation->getWitnessTables())) { + return DynamicCastResult::Failure; + } +#else // __SwiftValue is a native class + if (!swift_swiftValueConformsTo(destType, destType)) { + return DynamicCastResult::Failure; + } +#endif + } + +#if SWIFT_OBJC_INTEROP + auto object = bridgeAnythingToSwiftValueObject( + srcValue, srcType, takeOnSuccess); + destExistentialLocation->Value = object; + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + return DynamicCastResult::SuccessViaCopy; + } +# else + // Note: Code below works correctly on both Obj-C and non-Obj-C platforms, + // but the code above is slightly faster on Obj-C platforms. + auto object = _bridgeAnythingToObjectiveC(srcValue, srcType); + destExistentialLocation->Value = object; + return DynamicCastResult::SuccessViaCopy; +#endif +} + +static DynamicCastResult +tryCastToErrorExistential( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Existential); + auto destExistentialType = cast(destType); + assert(destExistentialType->getRepresentation() + == ExistentialTypeRepresentation::Error); + auto destBoxAddr = reinterpret_cast(destLocation); + + MetadataKind srcKind = srcType->getKind(); + switch (srcKind) { + case MetadataKind::ForeignClass: // CF object => Error + case MetadataKind::ObjCClassWrapper: // Obj-C object => Error + case MetadataKind::Struct: // Struct => Error + case MetadataKind::Enum: // Enum => Error + case MetadataKind::Class: { // Class => Error + assert(destExistentialType->NumProtocols == 1); + const WitnessTable *errorWitness; + if (_conformsToProtocols( + srcValue, srcType, destExistentialType, &errorWitness)) { +#if SWIFT_OBJC_INTEROP + // If it already holds an NSError, just use that. + if (auto embedded = getErrorEmbeddedNSErrorIndirect( + srcValue, srcType, errorWitness)) { + *destBoxAddr = reinterpret_cast(embedded); + return DynamicCastResult::SuccessViaCopy; + } +#endif + + BoxPair destBox = swift_allocError( + srcType, errorWitness, srcValue, takeOnSuccess); + *destBoxAddr = reinterpret_cast(destBox.object); + if (takeOnSuccess) { + return DynamicCastResult::SuccessViaTake; + } else { + return DynamicCastResult::SuccessViaCopy; + } + } + return DynamicCastResult::Failure; + } + + default: + return DynamicCastResult::Failure; + } +} + +static DynamicCastResult +tryCastUnwrappingExistentialSource( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + auto srcExistentialType = cast(srcType); + + // Unpack the existential content + const Metadata *srcInnerType; + OpaqueValue *srcInnerValue; + switch (srcExistentialType->getRepresentation()) { + case ExistentialTypeRepresentation::Class: { + auto classContainer + = reinterpret_cast(srcValue); + srcInnerType = swift_getObjectType((HeapObject *)classContainer->Value); + srcInnerValue = reinterpret_cast(&classContainer->Value); + break; + } + case ExistentialTypeRepresentation::Opaque: { + auto opaqueContainer + = reinterpret_cast(srcValue); + srcInnerType = opaqueContainer->Type; + srcInnerValue = srcExistentialType->projectValue(srcValue); + break; + } + case ExistentialTypeRepresentation::Error: { + const SwiftError *errorBox + = *reinterpret_cast(srcValue); + auto srcErrorValue + = errorBox->isPureNSError() ? srcValue : errorBox->getValue(); + srcInnerType = errorBox->getType(); + srcInnerValue = const_cast(srcErrorValue); + break; + } + } + + srcFailureType = srcInnerType; + return tryCast(destLocation, destType, + srcInnerValue, srcInnerType, + destFailureType, srcFailureType, + takeOnSuccess & (srcInnerValue == srcValue), + mayDeferChecks); +} + +/******************************************************************************/ +/**************************** Opaque Destination ******************************/ +/******************************************************************************/ + +static DynamicCastResult +tryCastToOpaque( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Opaque); + + // There's nothing special we can do here, but we have to have this + // empty function in order for the general casting logic to run + // for these types. + + return DynamicCastResult::Failure; +} + +/******************************************************************************/ +/**************************** Metatype Destination ****************************/ +/******************************************************************************/ + +#if SWIFT_OBJC_INTEROP +/// Check whether an unknown class instance is actually a type/metatype object. +static const Metadata *_getUnknownClassAsMetatype(void *object) { + // Objective-C class metadata are objects, so an AnyObject (or + // NSObject) may refer to a class object. + + // Test whether the object's isa is a metaclass, which indicates that + // the object is a class. + + Class isa = object_getClass((id)object); + if (class_isMetaClass(isa)) { + return swift_getObjCClassMetadata((const ClassMetadata *)object); + } + + return nullptr; +} +#endif + +static DynamicCastResult +tryCastToMetatype( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::Metatype); + + const MetatypeMetadata *destMetatypeType = cast(destType); + MetadataKind srcKind = srcType->getKind(); + switch (srcKind) { + case MetadataKind::Metatype: + case MetadataKind::ExistentialMetatype: { + const Metadata *srcMetatype = *(const Metadata * const *) srcValue; + if (auto result = swift_dynamicCastMetatype( + srcMetatype, destMetatypeType->InstanceType)) { + *((const Metadata **) destLocation) = result; + return DynamicCastResult::SuccessViaCopy; + } + return DynamicCastResult::Failure; + } + + case MetadataKind::Class: + case MetadataKind::ObjCClassWrapper: { +#if SWIFT_OBJC_INTEROP + // Some classes are actually metatypes + void *object = getNonNullSrcObject(srcValue, srcType, destType); + if (auto metatype = _getUnknownClassAsMetatype(object)) { + auto srcInnerValue = reinterpret_cast(&metatype); + auto srcInnerType = swift_getMetatypeMetadata(metatype); + return tryCast(destLocation, destType, srcInnerValue, srcInnerType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + } +#endif + return DynamicCastResult::Failure; + } + + default: + return DynamicCastResult::Failure; + } +} + +/// Perform a dynamic cast of a metatype to an existential metatype type. +static DynamicCastResult +_dynamicCastMetatypeToExistentialMetatype( + OpaqueValue *destLocation, const ExistentialMetatypeMetadata *destType, + const Metadata *srcMetatype, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + // The instance type of an existential metatype must be either an + // existential or an existential metatype. + auto destMetatype + = reinterpret_cast(destLocation); + + // If it's an existential, we need to check for conformances. + auto targetInstanceType = destType->InstanceType; + if (auto targetInstanceTypeAsExistential = + dyn_cast(targetInstanceType)) { + // Check for conformance to all the protocols. + // TODO: collect the witness tables. + const WitnessTable **conformance + = destMetatype ? destMetatype->getWitnessTables() : nullptr; + if (!_conformsToProtocols(nullptr, srcMetatype, + targetInstanceTypeAsExistential, + conformance)) { + return DynamicCastResult::Failure; + } + + if (destMetatype) + destMetatype->Value = srcMetatype; + return DynamicCastResult::SuccessViaCopy; + } + + // Otherwise, we're casting to SomeProtocol.Type.Type. + auto targetInstanceTypeAsMetatype = + cast(targetInstanceType); + + // If the source type isn't a metatype, the cast fails. + auto srcMetatypeMetatype = dyn_cast(srcMetatype); + if (!srcMetatypeMetatype) { + return DynamicCastResult::Failure; + } + + // The representation of an existential metatype remains consistent + // arbitrarily deep: a metatype, followed by some protocols. The + // protocols are the same at every level, so we can just set the + // metatype correctly and then recurse, letting the recursive call + // fill in the conformance information correctly. + + // Proactively set the destination metatype so that we can tail-recur, + // unless we've already done so. There's no harm in doing this even if + // the cast fails. + if (destLocation) + *((const Metadata **) destLocation) = srcMetatype; + + // Recurse. + auto srcInstanceType = srcMetatypeMetatype->InstanceType; + return _dynamicCastMetatypeToExistentialMetatype( + nullptr, + targetInstanceTypeAsMetatype, + srcInstanceType, + destFailureType, + srcFailureType, + takeOnSuccess, mayDeferChecks); +} + +// "ExistentialMetatype" is the metatype for an existential type. +static DynamicCastResult +tryCastToExistentialMetatype( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + assert(srcType != destType); + assert(destType->getKind() == MetadataKind::ExistentialMetatype); + + auto destExistentialMetatypeType + = cast(destType); + MetadataKind srcKind = srcType->getKind(); + switch (srcKind) { + case MetadataKind::Metatype: // Metatype => ExistentialMetatype + case MetadataKind::ExistentialMetatype: { // ExistentialMetatype => ExistentialMetatype + const Metadata *srcMetatype = *(const Metadata * const *) srcValue; + return _dynamicCastMetatypeToExistentialMetatype( + destLocation, + destExistentialMetatypeType, + srcMetatype, + destFailureType, + srcFailureType, + takeOnSuccess, mayDeferChecks); + } + + case MetadataKind::ObjCClassWrapper: { + // Some Obj-C classes are actually metatypes +#if SWIFT_OBJC_INTEROP + void *srcObject = getNonNullSrcObject(srcValue, srcType, destType); + if (auto metatype = _getUnknownClassAsMetatype(srcObject)) { + return _dynamicCastMetatypeToExistentialMetatype( + destLocation, + destExistentialMetatypeType, + metatype, + destFailureType, + srcFailureType, + takeOnSuccess, mayDeferChecks); + } +#endif + return DynamicCastResult::Failure; + } + + default: + return DynamicCastResult::Failure; + } +} + +/******************************************************************************/ +/********************************** Dispatch **********************************/ +/******************************************************************************/ + +// A layer of functions that evaluate the source and/or destination types +// in order to invoke a tailored casting operation above. +// +// This layer also deals with general issues of unwrapping box types +// and invoking bridging conversions defined via the _ObjectiveCBridgeable +// protocol. +// +// Most of the caster functions above should be fairly simple: +// * They should deal with a single target type, +// * They should assume the source is fully unwrapped, +// * They should not try to report or cleanup failure, +// * If they can take, they should report the source was destroyed. + +// Based on the destination type alone, select a targeted casting function. +// This design avoids some redundant inspection of the destination type +// data, for example, when we unwrap source boxes. +static tryCastFunctionType *selectCasterForDest(const Metadata *destType) { + auto destKind = destType->getKind(); + switch (destKind) { + case MetadataKind::Class: + return tryCastToSwiftClass; + case MetadataKind::Struct: { + const auto targetDescriptor = cast(destType)->Description; + if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(SS)) { + return tryCastToString; + } + if (targetDescriptor == &STRUCT_TYPE_DESCR_SYM(s11AnyHashable)) { + return tryCastToAnyHashable; + } + if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(Sa)) { + return tryCastToArray; + } + if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(SD)) { + return tryCastToDictionary; + } + if (targetDescriptor == &NOMINAL_TYPE_DESCR_SYM(Sh)) { + return tryCastToSet; + } + return tryCastToStruct; + } + case MetadataKind::Enum: + return tryCastToEnum; + case MetadataKind::Optional: + return tryCastToOptional; + case MetadataKind::ForeignClass: + return tryCastToForeignClass; + case MetadataKind::Opaque: + return tryCastToOpaque; + case MetadataKind::Tuple: + return tryCastToTuple; + case MetadataKind::Function: + return tryCastToFunction; + case MetadataKind::Existential: { + auto existentialType = cast(destType); + switch (existentialType->getRepresentation()) { + case ExistentialTypeRepresentation::Opaque: + if (existentialType->NumProtocols == 0) { + return tryCastToUnconstrainedOpaqueExistential; // => Unconstrained Any + } else { + return tryCastToConstrainedOpaqueExistential; // => Non-class-constrained protocol + } + case ExistentialTypeRepresentation::Class: + return tryCastToClassExistential; // => AnyObject, with or without protocol constraints + case ExistentialTypeRepresentation::Error: // => Error existential + return tryCastToErrorExistential; + } + swift_runtime_unreachable( + "Unknown existential type representation in dynamic cast dispatch"); + } + case MetadataKind::Metatype: + return tryCastToMetatype; + case MetadataKind::ObjCClassWrapper: + return tryCastToObjectiveCClass; + case MetadataKind::ExistentialMetatype: + return tryCastToExistentialMetatype; + case MetadataKind::HeapLocalVariable: + case MetadataKind::HeapGenericLocalVariable: + case MetadataKind::ErrorObject: + // These are internal details of runtime-only structures, + // so will never appear in compiler-generated types. + // As such, they don't need support here. + swift_runtime_unreachable( + "Unexpected MetadataKind in dynamic cast dispatch"); + return nullptr; + default: + // If you see this message, then there is a new MetadataKind that I didn't + // know about when I wrote this code. Please figure out what it is, how to + // handle it, and add a case for it. + swift_runtime_unreachable( + "Unknown MetadataKind in dynamic cast dispatch"); + } +} + +// This top-level driver provides the general flow for all casting +// operations. It recursively unwraps source and destination as it +// searches for a suitable conversion. +static DynamicCastResult +tryCast( + OpaqueValue *destLocation, const Metadata *destType, + OpaqueValue *srcValue, const Metadata *srcType, + const Metadata *&destFailureType, const Metadata *&srcFailureType, + bool takeOnSuccess, bool mayDeferChecks) +{ + destFailureType = destType; + srcFailureType = srcType; + + //////////////////////////////////////////////////////////////////////// + // + // 1. If types match exactly, we can just move/copy the data. + // (The tryCastToXyz functions never see this trivial case.) + // + if (srcType == destType) { + if (takeOnSuccess) { + destType->vw_initializeWithTake(destLocation, srcValue); + return DynamicCastResult::SuccessViaTake; + } else { + destType->vw_initializeWithCopy(destLocation, srcValue); + return DynamicCastResult::SuccessViaCopy; + } + } + + auto destKind = destType->getKind(); + auto srcKind = srcType->getKind(); + + //////////////////////////////////////////////////////////////////////// + // + // 2. Try directly casting the current srcValue to the target type. + // (If the dynamic type is different, try that too.) + // + auto tryCastToDestType = selectCasterForDest(destType); + if (tryCastToDestType == nullptr) { + return DynamicCastResult::Failure; + } + auto castResult = tryCastToDestType(destLocation, destType, srcValue, + srcType, destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(castResult)) { + return castResult; + } + if (srcKind == MetadataKind::Class + || srcKind == MetadataKind::ObjCClassWrapper + || srcKind == MetadataKind::ForeignClass) { + auto srcObject = getNonNullSrcObject(srcValue, srcType, destType); + auto srcDynamicType = swift_getObjectType(srcObject); + if (srcDynamicType != srcType) { + srcFailureType = srcDynamicType; + auto castResult = tryCastToDestType( + destLocation, destType, srcValue, srcDynamicType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(castResult)) { + return castResult; + } + } + } + + //////////////////////////////////////////////////////////////////////// + // + // 3. Try recursively unwrapping _source_ boxes, including + // existentials, AnyHashable, SwiftValue, and Error. + // + switch (srcKind) { + + case MetadataKind::Class: { +#if !SWIFT_OBJC_INTEROP + // Try unwrapping native __SwiftValue implementation + if (swift_unboxFromSwiftValueWithType(srcValue, destLocation, destType)) { + return DynamicCastResult::SuccessViaCopy; + } +#endif + break; + } + + case MetadataKind::ObjCClassWrapper: { +#if SWIFT_OBJC_INTEROP + // Try unwrapping Obj-C __SwiftValue implementation + auto subcastResult = tryCastUnwrappingObjCSwiftValueSource( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } +#endif + +#if SWIFT_OBJC_INTEROP + // Try unwrapping Obj-C NSError container + auto innerFlags = DynamicCastFlags::Default; + if (tryDynamicCastNSErrorToValue( + destLocation, srcValue, srcType, destType, innerFlags)) { + return DynamicCastResult::SuccessViaCopy; + } +#endif + break; + } + + case MetadataKind::Struct: { + auto srcStructType = cast(srcType); + auto srcStructDescription = srcStructType->getDescription(); + + // Try unwrapping AnyHashable container + if (srcStructDescription == &STRUCT_TYPE_DESCR_SYM(s11AnyHashable)) { + if (_swift_anyHashableDownCastConditionalIndirect( + srcValue, destLocation, destType)) { + return DynamicCastResult::SuccessViaCopy; + } + } + break; + } + + case MetadataKind::Existential: { + auto subcastResult = tryCastUnwrappingExistentialSource( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + break; + } + + default: + break; + } + + //////////////////////////////////////////////////////////////////////// + // + // 4. Try recursively unwrapping Optionals. First try jointly unwrapping + // both source and destination, then just destination, then just source. + // Note that if both are optional, we try all three of these! + // For example, consider casting an Optional to + // Optional. If T conforms, we need to + // unwrap both. But if it doesn't, we unwrap just the destination + // in order to cast Optional to the protocol directly. + // + if (destKind == MetadataKind::Optional) { + if (srcKind == MetadataKind::Optional) { + auto subcastResult = tryCastUnwrappingOptionalBoth( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + } + auto subcastResult = tryCastUnwrappingOptionalDestination( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + } + + if (srcKind == MetadataKind::Optional) { + auto subcastResult = tryCastUnwrappingOptionalSource( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + } + + //////////////////////////////////////////////////////////////////////// + // + // 5. Finally, explore bridging conversions via ObjectiveCBridgeable, + // Error, and __SwiftValue boxing. + // + switch (destKind) { + + case MetadataKind::Optional: { + // Optional supports _ObjectiveCBridgeable from an unconstrained AnyObject + if (srcType->getKind() == MetadataKind::Existential) { + auto srcExistentialType = cast(srcType); + if ((srcExistentialType->getRepresentation() == ExistentialTypeRepresentation::Class) + && (srcExistentialType->NumProtocols == 0) + && (srcExistentialType->getSuperclassConstraint() == nullptr) + && (srcExistentialType->isClassBounded())) { + auto toObjCResult = tryCastFromClassToObjCBridgeable( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, false); + if (isSuccess(toObjCResult)) { + return toObjCResult; + } + } + } + + break; + } + + case MetadataKind::Existential: { + // Try general machinery for stuffing values into AnyObject: + auto destExistentialType = cast(destType); + if (destExistentialType->getRepresentation() == ExistentialTypeRepresentation::Class) { + // Some types have custom Objective-C bridging support... + auto subcastResult = tryCastFromObjCBridgeableToClass( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + + // Other types can be boxed into a __SwiftValue container... + auto swiftValueCastResult = tryCastToClassExistentialViaSwiftValue( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(swiftValueCastResult)) { + return swiftValueCastResult; + } + } + break; + } + + case MetadataKind::Class: + case MetadataKind::ObjCClassWrapper: + case MetadataKind::ForeignClass: { + // Try _ObjectiveCBridgeable to bridge _to_ a class type _from_ a + // struct/enum type. Note: Despite the name, this is used for both + // Swift-Swift and Swift-ObjC bridging + if (srcKind == MetadataKind::Struct || srcKind == MetadataKind::Enum) { + auto subcastResult = tryCastFromObjCBridgeableToClass( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + } + +#if SWIFT_OBJC_INTEROP + if (destKind == MetadataKind::ObjCClassWrapper) { + // If the destination type is an NSError or NSObject, and the source type + // is an Error, then the cast might succeed by NSError bridging. + if (auto srcErrorWitness = findErrorWitness(srcType)) { + if (destType == getNSErrorMetadata() + || destType == getNSObjectMetadata()) { + auto flags = DynamicCastFlags::Default; + auto error = dynamicCastValueToNSError(srcValue, srcType, + srcErrorWitness, flags); + *reinterpret_cast(destLocation) = error; + return DynamicCastResult::SuccessViaCopy; + } + } + } +#endif + + break; + } + + case MetadataKind::Struct: + case MetadataKind::Enum: { + // Use _ObjectiveCBridgeable to bridge _from_ a class type _to_ a + // struct/enum type. Note: Despite the name, this is used for both + // Swift-Swift and ObjC-Swift bridging + if (srcKind == MetadataKind::Class + || srcKind == MetadataKind::ObjCClassWrapper + || srcKind == MetadataKind::ForeignClass) { + auto subcastResult = tryCastFromClassToObjCBridgeable( + destLocation, destType, srcValue, srcType, + destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks); + if (isSuccess(subcastResult)) { + return subcastResult; + } + } + + // Note: In theory, if src and dest are both struct/enum types, we could see + // if the ObjC bridgeable class types matched and then do a two-step + // conversion from src -> bridge class -> dest. Such ambitious conversions + // might cause more harm than good, though. In particular, it could + // undermine code that uses a series of `as?` to quickly determine how to + // handle a particular object. + break; + } + + default: + break; + } + + return DynamicCastResult::Failure; +} + +/******************************************************************************/ +/****************************** Main Entrypoint *******************************/ +/******************************************************************************/ + +// XXX REMOVE ME XXX TODO XXX +// Declare the old entrypoint +SWIFT_RUNTIME_EXPORT +bool +swift_dynamicCast_OLD(OpaqueValue *destLocation, + OpaqueValue *srcValue, + const Metadata *srcType, + const Metadata *destType, + DynamicCastFlags flags); +// XXX REMOVE ME XXX TODO XXX + +/// ABI: Perform a dynamic cast to an arbitrary type. +static bool +swift_dynamicCastImpl(OpaqueValue *destLocation, + OpaqueValue *srcValue, + const Metadata *srcType, + const Metadata *destType, + DynamicCastFlags flags) +{ + // XXX REMOVE ME XXX TODO XXX TRANSITION SHIM + // XXX REMOVE ME XXX TODO XXX TRANSITION SHIM + // Support switching to the old implementation while the new one + // is still settling. Once the new implementation is stable, + // I'll rip the old one entirely out. + static bool useOldImplementation = false; // Default: NEW Implementation + static swift_once_t Predicate; + swift_once( + &Predicate, + [](void *) { + // Define SWIFT_OLD_DYNAMIC_CAST_RUNTIME=1 to use the old runtime + // dynamic cast logic. + auto useOld = getenv("SWIFT_OLD_DYNAMIC_CAST_RUNTIME"); + if (useOld) { + useOldImplementation = true; + } + }, nullptr); + if (useOldImplementation) { + return swift_dynamicCast_OLD(destLocation, srcValue, + srcType, destType, flags); + } + // XXX REMOVE ME XXX TODO XXX TRANSITION SHIM + // XXX REMOVE ME XXX TODO XXX TRANSITION SHIM + + // If the compiler has asked for a "take", we can + // move pointers without ref-counting overhead. + bool takeOnSuccess = flags & DynamicCastFlags::TakeOnSuccess; + // Unconditional casts are allowed to crash the program on failure. + // We can exploit that for performance: return a partial conversion + // immediately and do additional checks lazily when the results are + // actually accessed. + bool mayDeferChecks = flags & DynamicCastFlags::Unconditional; + + // Attempt the cast... + const Metadata *destFailureType = destType; + const Metadata *srcFailureType = srcType; + auto result = tryCast( + destLocation, destType, + srcValue, srcType, + destFailureType, srcFailureType, + takeOnSuccess, mayDeferChecks); + + switch (result) { + case DynamicCastResult::Failure: + if (flags & DynamicCastFlags::Unconditional) { + swift_dynamicCastFailure(srcFailureType, destFailureType); + } + if (flags & DynamicCastFlags::DestroyOnFailure) { + srcType->vw_destroy(srcValue); + } + return false; + case DynamicCastResult::SuccessViaCopy: + if (takeOnSuccess) { // We copied, but compiler asked for take. + srcType->vw_destroy(srcValue); + } + return true; + case DynamicCastResult::SuccessViaTake: + return true; + } +} + +#define OVERRIDE_DYNAMICCASTING COMPATIBILITY_OVERRIDE +#include "CompatibilityOverride.def" diff --git a/test/stdlib/CastTraps.swift.gyb b/test/Casting/CastTraps.swift.gyb similarity index 61% rename from test/stdlib/CastTraps.swift.gyb rename to test/Casting/CastTraps.swift.gyb index e5608874d028d..95c7cbbebfe31 100644 --- a/test/stdlib/CastTraps.swift.gyb +++ b/test/Casting/CastTraps.swift.gyb @@ -5,17 +5,17 @@ // FIXME: Casting.cpp has dozens of places to fail a cast. This test does not // attempt to enumerate them all. -// REQUIRES: objc_interop - import StdlibUnittest - +#if _runtime(_ObjC) import Foundation +#endif % types = [] % objectTypes = [] % protocolTypes = [] +% ObjCTypes = [] % types.append(['main.Class1', 'main.Class2']) % objectTypes.append(['main.Class1', 'main.Class2']) @@ -28,11 +28,15 @@ struct Struct2 { } % types.append(['main.ObjCClass1', 'main.ObjCClass2']) % objectTypes.append(['main.ObjCClass1', 'main.ObjCClass2']) +% ObjCTypes.extend(['main.ObjCClass1', 'main.ObjCClass2']) +#if _runtime(_ObjC) class ObjCClass1 : NSObject { } class ObjCClass2 : NSObject { } +#endif % types.append(['DateFormatter', 'NumberFormatter']) % objectTypes.append(['DateFormatter', 'NumberFormatter']) +% ObjCTypes.extend(['DateFormatter', 'NumberFormatter']) // non-Swift Objective-C class % protocolTypes.append('main.Proto1') @@ -40,6 +44,7 @@ class ObjCClass2 : NSObject { } protocol Proto1 { } protocol Proto2 { } % protocolTypes.append('URLSessionDelegate') +% ObjCTypes.append('URLSessionDelegate') // non-Swift Objective-C protocol @@ -51,6 +56,7 @@ var CastTrapsTestSuite = TestSuite("CastTraps") % for (t1, _) in types: % for (_, t2) in types: +% if t1 not in ObjCTypes and t2 not in ObjCTypes: CastTrapsTestSuite.test("${t1}__${t2}") .skip(.custom( { _isFastAssertConfiguration() }, @@ -67,6 +73,7 @@ CastTrapsTestSuite.test("${t1}__${t2}") _blackHole(r) } +% end % end % end @@ -76,6 +83,7 @@ CastTrapsTestSuite.test("${t1}__${t2}") % for (t1, _) in objectTypes: % for (t2) in protocolTypes: +% if t1 not in ObjCTypes and t2 not in ObjCTypes: CastTrapsTestSuite.test("${t1}__${t2}") .skip(.custom( { _isFastAssertConfiguration() }, @@ -92,7 +100,47 @@ CastTrapsTestSuite.test("${t1}__${t2}") _blackHole(r) } +% end % end % end +protocol P2 {} +if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +CastTrapsTestSuite.test("Unexpected null") + .crashOutputMatches("Found unexpected null pointer value while trying to cast value of type '") + .crashOutputMatches("Foo'") + .crashOutputMatches(" to '") + .crashOutputMatches("P2'") + .code +{ + class Foo {} + let n = UnsafeRawPointer(bitPattern: 0) + var o: Foo = unsafeBitCast(n, to: Foo.self) + let r = o as Any + expectCrashLater() + let s = r as? P2 + _blackHole(s) +} +} + + +#if _runtime(_ObjC) +if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +CastTrapsTestSuite.test("Unexpected Obj-C null") + .crashOutputMatches("Found unexpected null pointer value while trying to cast value of type '") + .crashOutputMatches("NSObject'") + .crashOutputMatches(" to '") + .crashOutputMatches("P2'") + .code +{ + let n = UnsafeRawPointer(bitPattern: 0) + var o: NSObject = unsafeBitCast(n, to: NSObject.self) + let r = o as Any + expectCrashLater() + let s = r as? P2 + _blackHole(s) +} +} +#endif + runAllTests() diff --git a/test/Casting/Casts.swift b/test/Casting/Casts.swift new file mode 100644 index 0000000000000..54eeef85adff6 --- /dev/null +++ b/test/Casting/Casts.swift @@ -0,0 +1,763 @@ +// Casts.swift - Tests for conversion between types. +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +// ----------------------------------------------------------------------------- +/// +/// Contains tests for non-trapping type conversions reported by users. +/// +// ----------------------------------------------------------------------------- +// RUN: %empty-directory(%t) +// +// RUN: %target-build-swift -swift-version 5 -g -Onone -Xfrontend -enable-experimental-concurrency -module-name a %s -o %t/a.swift5.Onone.out +// RUN: %target-codesign %t/a.swift5.Onone.out +// RUN: %target-run %t/a.swift5.Onone.out +// +// RUN: %target-build-swift -swift-version 5 -g -O -Xfrontend -enable-experimental-concurrency -module-name a %s -o %t/a.swift5.O.out +// RUN: %target-codesign %t/a.swift5.O.out +// RUN: %target-run %t/a.swift5.O.out +// +// REQUIRES: executable_test +// UNSUPPORTED: use_os_stdlib + +import StdlibUnittest +#if _runtime(_ObjC) +import Foundation +#endif + +func blackhole(_ t: T) { } + +private func runtimeCast(_ from: T, to: U.Type) -> U? { + return from as? U +} + +let CastsTests = TestSuite("Casts") + +// Test for SR-426: missing release for some types after failed conversion +CastsTests.test("No leak for failed tuple casts") { + let t: Any = (1, LifetimeTracked(0)) + expectFalse(t is Any.Type) +} + +protocol P {} +class ErrClass : Error { } + +CastsTests.test("No overrelease of existential boxes in failed casts") { + // Test for crash from SR-392 + // We fail casts of an existential box repeatedly + // to ensure it does not get over-released. + func bar(_ t: T) { + for _ in 0..<10 { + if case let a as P = t { + _ = a + } + } + } + + let err: Error = ErrClass() + bar(err) +} + +extension Int : P {} + +// Test for SR-7664: Inconsistent optional casting behaviour with generics +// Runtime failed to unwrap multiple levels of Optional when casting. +CastsTests.test("Multi-level optionals can be casted") { + func testSuccess(_ x: From, from: From.Type, to: To.Type) { + expectNotNil(x as? To) + } + func testFailure(_ x: From, from: From.Type, to: To.Type) { + expectNil(x as? To) + } + testSuccess(42, from: Int?.self, to: Int.self) + testSuccess(42, from: Int??.self, to: Int.self) + testSuccess(42, from: Int???.self, to: Int.self) + testSuccess(42, from: Int???.self, to: Int?.self) + testSuccess(42, from: Int???.self, to: Int??.self) + testSuccess(42, from: Int???.self, to: Int???.self) + testFailure(42, from: Int?.self, to: String.self) + testFailure(42, from: Int??.self, to: String.self) + testFailure(42, from: Int???.self, to: String.self) +} + +// Test for SR-9837: Optional.none not casting to Optional.none in generic context +CastsTests.test("Optional.none can be casted to Optional.none in generic context") { + func test(_ type: T.Type) -> T? { + return Any?.none as? T + } + + expectEqual(type(of: test(Bool.self)), Bool?.self) + expectEqual(type(of: test(Bool?.self)), Bool??.self) +} + +// Test for SR-3871: Cannot cast from ObjC existential without going through AnyObject +#if _runtime(_ObjC) +protocol P2 {} +CastsTests.test("Cast from ObjC existential to Protocol (SR-3871)") { + if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { + struct S: P2 {} + + class ObjCWrapper { + @objc dynamic let any: Any = S() + init() {} + } + let a = ObjCWrapper().any + expectTrue(a is P2) + // In SR-3871, the following cast failed (everything else here succeeded) + expectNotNil(a as? P2) + expectNotNil(a as? S) + let b = a as AnyObject + expectTrue(a is P2) + expectNotNil(b as? P2) + expectNotNil(b as? S) + } +} +#endif + +protocol P3 {} +CastsTests.test("Cast from Swift existential to Protocol") { + struct S: P3 {} + class SwiftWrapper { + let any: Any = S() + init() {} + } + let a = SwiftWrapper().any + expectTrue(a is P3) + expectNotNil(a as? P3) + expectNotNil(a as? S) + let b = a as AnyObject + expectTrue(b is P3) + expectNotNil(b as? P3) + expectNotNil(b as? S) +} + + +#if _runtime(_ObjC) +extension CFBitVector : P { + static func makeImmutable(from values: Array) -> CFBitVector { + return CFBitVectorCreate(/*allocator:*/ nil, values, values.count * 8) + } +} + +extension CFMutableBitVector { + static func makeMutable(from values: Array) -> CFMutableBitVector { + return CFBitVectorCreateMutableCopy( + /*allocator:*/ nil, + /*capacity:*/ 0, + CFBitVector.makeImmutable(from: values)) + } +} + +func isP(_ t: T) -> Bool { + return t is P +} + +CastsTests.test("Dynamic casts of CF types to protocol existentials (SR-2289)") +.skip(.custom({ + !_isDebugAssertConfiguration() + }, + reason: "This test behaves unpredictably in optimized mode.")) +.code { + expectTrue(isP(10 as Int)) + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + expectTrue(isP(CFBitVector.makeImmutable(from: [10, 20]))) + expectTrue(isP(CFMutableBitVector.makeMutable(from: [10, 20]))) + } +} +#endif + +// Another test for SR-3871, SR-5590, SR-6309, SR-8651: +// user type in a _SwiftValue in an Optional can't be cast to a protocol. +// Note: This uses the (misnamed) _bridgeAnythingToObjectiveC so it can +// test these paths on Linux as well. +protocol P6309 {} +CastsTests.test("Casting struct -> Obj-C -> Protocol fails (SR-3871, SR-5590, SR-6309, SR-8651)") { + struct S: P6309 { + let value: Int + let tracker = LifetimeTracked(13) + } + + let a: P6309 = S(value: 13) + + let b = _bridgeAnythingToObjectiveC(a) + let d = b as? Any + let e = d as? P6309 + expectNotNil(e) +} + + +protocol P4552 {} +if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +CastsTests.test("Casting Any(Optional(T)) -> Protocol fails (SR-4552)") { + struct S: P4552 { + let tracker = LifetimeTracked(13) + } + + let a = S() + let b: S? = a + let c = b as? Any + let d = c as? P4552 + expectNotNil(d) +} +} + +// rdar://27108240 (Optional casting bug (crash)) +protocol Key { + associatedtype Value +} +CastsTests.test("Cast to associated type") { + // Helper function to bypass compiler cast optimizations + func runtimeCast (_ x: From, to: To.Type) -> To? { + return x as? To + } + struct StringKey : Key { + typealias Value = String? + } + var string: String? + func value(forKey key: K.Type) { + let b = runtimeCast(string, to: K.Value.self) + expectNotNil(b) + let c = string as? K.Value + expectNotNil(c) + } + value(forKey: StringKey.self) +} + +#if _runtime(_ObjC) +// rdar://36288786 (Swift metatype stored in an Objective-C id property can't be typecast back to its original type) +CastsTests.test("Store Swift metatype in ObjC property and cast back to Any.Type") { + class MyObj { + var sVar: Any? = nil + @objc dynamic var objcVar: Any? = nil + } + + let a = MyObj() + + // Double values + a.sVar = 1.234 + a.objcVar = 1.234 + + let sValue1 = a.sVar as? Double + let objcValue1 = a.objcVar as? Double + expectEqual(sValue1, objcValue1) + + // Swift types + let b = Bool.self + a.sVar = b + a.objcVar = b + + let sValue2 = a.sVar as? Any.Type + let objcValue2 = a.objcVar as? Any.Type + expectTrue(sValue2 == b) + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + expectTrue(sValue2 == objcValue2) + expectTrue(objcValue2 == b) + } +} +#endif + +// rdar://37793004 ([dynamic casting] [SR-7049]: Enums don't cast back from AnyHashable) +CastsTests.test("Enums don't cast back from AnyHashable (SR-7049)") { + enum E { + case a + } + + // This works as expected. + let str: AnyHashable = "hello" + expectNotNil(str as? String) // Optional("hello") + expectNotNil(str as? String as Any) // Optional("hello") + + // This doesn't. + let ea: AnyHashable = E.a + expectNotNil(ea as? E) + expectNotNil(ea as? E as Any) + expectEqual((ea as? E), E.a) +} + +#if _runtime(_ObjC) +//rdar://39415812 ([dynamic casting] [SR-7432]: Can't see through boxed _SwiftValue when casting from @objc Type) +@objc(Exporter) +protocol Exporter: NSObjectProtocol { + var type: Any { get } + func export(item: Any) -> String? +} +CastsTests.test("Casts from @objc Type") { + struct User { var name: String } + + final class UserExporter: NSObject, Exporter { + var type: Any { return User.self } + func export(item: Any) -> String? { + let u = item as? User + return u?.name + } + } + + let user = User(name: "Kermit") + let exporter: Exporter = UserExporter() + + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + expectTrue(exporter.type is User.Type) + } + expectNotNil(exporter.export(item: user)) +} +#endif + +#if _runtime(_ObjC) +// rdar://44467533 (Swift master branch: conditional casts for _ObjectiveCBridgeable miscompile in swift-corelibs-foundation) +CastsTests.test("Conditional NSNumber -> Bool casts") { + let x = NSNumber(value: -1) as? Bool + expectNil(x) +} +#endif + +// rdar://45217461 ([dynamic casting] [SR-8964]: Type check operator (is) fails for Any! variable holding an Error (struct) value) +if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +CastsTests.test("Casts from Any(struct) to Error (SR-8964)") { + struct MyError: Error { } + + let a: Any! = MyError() + let b: Any = a + expectTrue(b is Error) +} +} + +#if _runtime(_ObjC) +// rdar://15494623 (Handle dynamic cast to archetype bound to ObjC existential) +CastsTests.test("Dynamic cast to ObjC protocol") { + func genericCast(x: NSObject, _: T.Type) -> T? { + return x as? T + } + + let n: NSNumber = 1 + let copying = genericCast(x: n, NSCopying.self) + expectNotNil(copying) +} +#endif + +// SR-6126 +if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +CastsTests.test("Nil handling for Optionals and Arrays (SR-6126)") { + func check(_ arg: Int??) -> String { + switch arg { + case .none: + return ".none" + case .some(.none): + return ".some(.none)" + case .some(.some): + return ".some(.some)" + } + } + + let x: Int? = .none + let y: [Int?] = [.none] + + let a = x as Int?? + let b = (x as? Int??)! + let b2 = runtimeCast(x, to: Int??.self)! + let c = Int?.none as Int?? + let d = (Int?.none as? Int??)! + let d2 = runtimeCast(Int?.none, to: Int??.self)! + let e = (y as [Int??]).first! + let f = (y as? [Int??])!.first! + let f2 = runtimeCast(y, to: [Int??].self)!.first! + let g = ([Int?.none] as [Int??]).first! + let h = ([Int?.none] as? [Int??])!.first! + let h2 = runtimeCast([Int?.none], to: [Int??].self)!.first! + + // Original reporter believes all of these should be .some(.none) + expectEqual(".some(.none)", check(a)) // Xcode 9.0: .some(.none) + expectEqual(".some(.none)", check(b)) // Xcode 9.0: .some(.none) + expectEqual(".some(.none)", check(b2)) + expectEqual(".some(.none)", check(c)) // Xcode 9.0: .some(.none) + expectEqual(".some(.none)", check(d)) // Xcode 9.0: .some(.none) + expectEqual(".some(.none)", check(d2)) + expectEqual(".some(.none)", check(e)) // Xcode 9.0: .none + expectEqual(".some(.none)", check(f)) // Xcode 9.0: .none + expectEqual(".some(.none)", check(f2)) + expectEqual(".some(.none)", check(g)) // Xcode 9.0: .some(.none) + expectEqual(".some(.none)", check(h)) // Xcode 9.0: .none + expectEqual(".some(.none)", check(h2)) +} +} + +protocol SwiftProtocol {} +CastsTests.test("Swift Protocol Metatypes don't self-conform") { + let a = SwiftProtocol.self + // `is P.Protocol` tests whether the argument is a subtype of P. + // In particular, the protocol identifier `P.self` is such a subtype. + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + expectNotNil(runtimeCast(a, to: SwiftProtocol.Protocol.self)) // Fixed by rdar://58991956 + } + expectNotNil(a as? SwiftProtocol.Protocol) + expectTrue(a is SwiftProtocol.Protocol) + blackhole(a as! SwiftProtocol.Protocol) // Should not trap + + // `is P.Type` tests conformance to P. Protocols cannot conform to + // protocols, so these always fail. + expectNil(runtimeCast(a, to: SwiftProtocol.Type.self)) + expectNil(a as? SwiftProtocol.Type) + expectFalse(a is SwiftProtocol.Type) +} + +CastsTests.test("Self-conformance for Any.self") { + let b = Any.self + expectNotNil(runtimeCast(b, to: Any.Protocol.self)) + blackhole(b as! Any.Protocol) // Should not trap + expectTrue(b is Any.Protocol) + expectNotNil(b as? Any.Protocol) + + // Unlike most other protocols, Any.self does conform to Any + expectNotNil(runtimeCast(b, to: Any.Type.self)) + expectNotNil(b as? Any.Type) + expectTrue(b is Any.Type) + blackhole(b as! Any.Type) +} + +// rdar://59067748 (Error Protocol should self-conform in optimized casts) +CastsTests.test("Self-conformance for Error.self") +.skip(.custom({ + !_isDebugAssertConfiguration() + }, + reason: "Cast optimizer breaks this test")) +.code { + let c = Error.self + expectNotNil(runtimeCast(c, to: Error.Protocol.self)) + expectNotNil(c as? Error.Protocol) + expectTrue(c is Error.Protocol) + blackhole(c as! Error.Protocol) + + // Unlike most other protocols, Error.self does conform to Error + expectNotNil(runtimeCast(c, to: Error.Type.self)) + expectFailure { expectNotNil(c as? Error.Type) } + expectFailure { expectTrue(c is Error.Type) } + // blackhole(c as! Error.Type) // Should not trap, but currently does +} + +// rdar://59067556 (Obj-C Protocol Metatypes should self-conform) +#if _runtime(_ObjC) +@objc protocol ObjCProtocol {} +CastsTests.test("ObjC Protocol Metatypes self-conform") { + let a = ObjCProtocol.self + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + expectNotNil(runtimeCast(a, to: ObjCProtocol.Protocol.self)) + } + expectNotNil(a as? ObjCProtocol.Protocol) + expectTrue(a is ObjCProtocol.Protocol) + blackhole(a as! ObjCProtocol.Protocol) + + // Unlike Swift protocols, ObjC protocols do conform to themselves + expectFailure { expectNotNil(runtimeCast(a, to: ObjCProtocol.Type.self)) } + expectFailure { expectNotNil(a as? ObjCProtocol.Type) } + expectFailure { expectTrue(a is ObjCProtocol.Type) } + // blackhole(a as! ObjCProtocol.Type) // Should not trap, but currently does +} +#endif + +#if _runtime(_ObjC) +protocol NewStringProtocol {} +extension String: NewStringProtocol { } +CastsTests.test("String/NSString extension compat") { + let x: Any = NSString() + expectFailure { expectNotNil(runtimeCast(x, to: NewStringProtocol.self)) } + expectFailure { expectNotNil(x as? NewStringProtocol) } +} +#endif + +protocol P1999 {} +if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { +CastsTests.test("Cast Any(Optional(class)) to Protocol type (SR-1999)") { + class Foo: P1999 { } + + let optionalFoo : Foo? = Foo() + let anyValue: Any = optionalFoo + + let foo1 = anyValue as? Foo + expectNotNil(foo1) + + let foo2 = anyValue as? P1999 + expectNotNil(foo2) + + let foo3 = runtimeCast(anyValue, to: Foo.self) + expectNotNil(foo3) + + let foo4 = runtimeCast(anyValue, to: P1999.self) + expectNotNil(foo4) +} +} + +#if _runtime(_ObjC) +CastsTests.test("Dict value casting (SR-2911)") { + var dict: [AnyHashable: String] = [:] + dict["Key"] = "Value" + expectNotNil(dict["Key"] as? NSString) + expectNotNil(runtimeCast(dict["Key"], to: NSString.self)) +} +#endif + +#if _runtime(_ObjC) +CastsTests.test("String coercions should work on Linux (SR-12020)") { + let a = "abc" as Substring as NSString + let b = "abc" as NSString + expectEqual(a, b) + + let c = "abc" as Substring + let d = c as? NSString + let e = "abc" as? NSString + expectEqual(d, e) + + let f = runtimeCast(d, to: NSString.self) + expectEqual(e, f) +} +#endif + +class ClassInt: Equatable, Hashable { + private var tracker = LifetimeTracked(77) + static func == (lhs: ClassInt, rhs: ClassInt) -> Bool {return true} + func hash(into hasher: inout Hasher) {} +} +CastsTests.test("AnyHashable(Class) -> Obj-C -> Class") +.skip(.custom({ + !_isDebugAssertConfiguration() + }, + reason: "Cast optimizer breaks this test")) +.code { + let a = ClassInt() + let b = runtimeCast(a, to: AnyHashable.self)! + let c = _bridgeAnythingToObjectiveC(b) + let d = /* SwiftValueBox(AnyHashable(ClassInt)) */ c as? ClassInt + expectNotNil(d) + let d2 = runtimeCast(c, to: ClassInt.self) + expectNotNil(d2) + let e = runtimeCast(/* SwiftValueBox(AnyHashable(ClassInt)) */ c, to: ClassInt.self) + expectNotNil(e) +} + +#if _runtime(_ObjC) +// rdar://58999120 +CastsTests.test("Error -> NSError -> Protocol transitivity (SR-12095)") { + enum NonConformingError: Error { + case ok + } + + let nonConformingError: Error = NonConformingError.ok + + // NSError conforms to CustomStringConvertible, so ... + let conformingError = nonConformingError as? NSError + expectTrue(conformingError is CustomStringConvertible) + expectNotNil(conformingError as? CustomStringConvertible) + + // Our error type does not conform directly, but should conform + // indirectly because of NSError... + // Note: Known broken in both runtime and compiler. + expectFailure { expectTrue(nonConformingError is CustomStringConvertible) } + expectFailure { expectNotNil(nonConformingError as? CustomStringConvertible) } +} +#endif + +#if _runtime(_ObjC) +CastsTests.test("Runtime crash casting Obj-C object to Obj-C protocol (rdar://16449805)") { + // FIXME: The reported crash was for `NSPoint(x:0, y:0) as? NSCoding`, + // but NSPoint seems to not be available on 32-bit platforms. + expectNotNil(NSString() as? NSCoding) +} +#endif + +CastsTests.test("Casting Swift Error-conforming types to Error existentials") { + enum Foo: Error { + case OK + case Broken + } + let a = Foo.Broken + let b = a as? Error + expectNotNil(b) + let c = b as? Foo + expectNotNil(c) + let d = Foo.self as? Error.Type + expectNotNil(d) +} + +#if _runtime(_ObjC) +CastsTests.test("Casting NSError <-> Error") { + @objc class Bar: NSError { + init() {super.init(domain: "Bar", code: 99)} + required init?(coder: NSCoder) {super.init(coder: coder)} + } + let e = Bar.self as? Error.Type + expectNotNil(e) + let f = Bar.self as? Bar.Type + expectNotNil(f) + let g = Bar() as? Error + expectNotNil(g) +} +#endif + +// Foundation's JSON handling makes heavy use of passing Any? inside of Any +// existentials. That inspired the following three checks: +CastsTests.test("[Any(Any?)] -> [Any?] should prefer unwrapping source") { + let a: Any? = nil + let b: [Any] = [a as Any] + let c = b as? [Any?] + let d = c! + let e = d[0] + expectNil(e) +} + +CastsTests.test("Any(Any?) -> Any? should prefer unwrapping source") { + let a: Any? = nil + let b: Any = a + let c = b as? Any? + let d = c! + expectNil(d) +} + +#if _runtime(_ObjC) +CastsTests.test("NSNull?.none -> Any? should set outer nil") { + let a: NSNull? = nil + let b = a as? Any? + let c = b! + expectNil(c) +} +#endif + +CastsTests.test("Int??.some(nil) => Int??? should inject naturally") { + let a: Int?? = .some(nil) + let b = a as? Int??? + let c = b! + let d = c! + let e = d! + expectNil(e) +} + +CastsTests.test("Int??.some(nil) => String??? should inject naturally") { + let a: Int?? = .some(nil) + let b = runtimeCast(a, to: String???.self) + let c = b! + let d = c! + let e = d! + expectNil(e) +} + +CastsTests.test("Int??.some(nil) => Any??? should inject naturally") { + let a: Int?? = .some(nil) + let b = a as? Any??? + let c = b! + let d = c! + let e = d! + expectNil(e) +} + +#if _runtime(_ObjC) +CastsTests.test("NSString -> String fast path") { + let a = "short" as NSString + expectNotNil(a as? String) + let b = runtimeCast(a, to: String.self) + expectNotNil(b) + + let c = "Long (adj) -- extended, large, the opposite of short" as NSString + expectNotNil(c as? String) + let d = runtimeCast(c, to: String.self) + expectNotNil(d) + + let e = NSMutableString("not read-only") + expectNotNil(e as? String) + let f = runtimeCast(e, to: String.self) + expectNotNil(f) + + let g = CFStringCreateWithCString(nil, "hello, world", CFStringBuiltInEncodings.UTF8.rawValue) + expectNotNil(g as? String) + let h = runtimeCast(g, to: String.self) + expectNotNil(h) + + let i = CFStringCreateMutable(nil, 0) + expectNotNil(i as? String) + let j = runtimeCast(i, to: String.self) + expectNotNil(j) +} +#endif + +// This fails in optimized builds because after inlining `runtimeCast`, +// the resulting SIL cast operation is left in a form that IRGen can't +// correctly handle. +//CastsTests.test("Optimized metatype -> AnyObject cast") { +// struct StructInt { } +// let a = StructInt.self +// let b = runtimeCast(a, to: AnyObject.self) +// expectNotNil(b) +//} + +CastsTests.test("Any.Protocol") { + class C {} + struct S {} + func isAnyProtocol(_ type: T.Type) -> Bool { + let result = T.self is Any.Protocol + if result { + // `as!` should succeed if `is` does + blackhole(T.self as! Any.Protocol) + } + return result + } + func isAnyType(_ type: T.Type) -> Bool { + return T.self is Any.Type + } + func isType(_ type: T.Type, to: U.Type) -> Bool { + return T.self is U.Type + } + + expectTrue(Int.self is Any.Type) + expectNotNil(Int.self as? Any.Type) + expectTrue(isAnyType(Int.self)) + expectFalse(Int.self is Any.Protocol) + expectNil(Int.self as? Any.Protocol) + expectFalse(isAnyProtocol(Int.self)) + expectFalse(isType(Int.self, to: Any.self)) + + expectTrue(C.self is Any.Type) + expectNotNil(C.self as? Any.Type) + expectTrue(isAnyType(C.self)) + expectFalse(C.self is Any.Protocol) + expectNil(C.self as? Any.Protocol) + expectFalse(isAnyProtocol(C.self)) + expectFalse(isType(C.self, to: Any.self)) + + expectTrue(S.self is Any.Type) + expectNotNil(S.self as? Any.Type) + expectTrue(isAnyType(S.self)) + expectFalse(S.self is Any.Protocol) + expectNil(S.self as? Any.Protocol) + expectFalse(isAnyProtocol(S.self)) + expectFalse(isType(S.self, to: Any.self)) + + expectTrue(Any.self is Any.Type) + expectNotNil(Any.self as? Any.Type) + expectTrue(isAnyType(Any.self)) + expectTrue(Any.self is Any.Protocol) + expectNotNil(Any.self as? Any.Protocol) + expectTrue(isAnyProtocol(Any.self)) + expectTrue(isType(Any.self, to: Any.self)) + + expectTrue(Any?.self is Any.Type) + expectNotNil(Any?.self as? Any.Type) + expectTrue(isAnyType(Any?.self)) + expectFalse(Any?.self is Any.Protocol) + expectNil(Any?.self as? Any.Protocol) + expectFalse(isAnyProtocol(Any?.self)) + expectFalse(isType(Any?.self, to: Any.self)) +} + +CastsTests.test("Async function types") { + let asyncFnType: Any.Type = (() async -> Void).self + let fnType: Any.Type = (() -> Void).self + + expectTrue(fnType is (() -> Void).Type) + expectTrue(asyncFnType is (() async -> Void).Type) + expectFalse(fnType is (() async -> Void).Type) + expectFalse(asyncFnType is (() -> Void).Type) +} + +runAllTests() diff --git a/test/Interpreter/SDK/protocol_lookup_foreign.swift b/test/Interpreter/SDK/protocol_lookup_foreign.swift index e0836c4cfa31d..f0565dc042a65 100644 --- a/test/Interpreter/SDK/protocol_lookup_foreign.swift +++ b/test/Interpreter/SDK/protocol_lookup_foreign.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift | %FileCheck %s +// RUN: %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: objc_interop @@ -10,38 +10,62 @@ // XFAIL: swift_test_mode_optimize_unchecked import Foundation +import StdlibUnittest protocol Fooable { - func foo() + func foo() -> String } -func fooify(_ x: T) { +func fooify(_ x: T) -> String { if let foo = x as? Fooable { - foo.foo() + return foo.foo() } else { - print("not fooable") + return "not fooable" } } extension NSRect: Fooable { - func foo() { print("NSRect") } + func foo() -> String { return "NSRect" } } extension CFSet: Fooable { - func foo() { print("CFSet") } + func foo() -> String { return "CFSet" } } extension NSString: Fooable { - func foo() { print("NSString") } + func foo() -> String { return "NSString" } } -fooify(NSRect()) // CHECK: NSRect -fooify(NSPoint()) // CHECK-NEXT: not fooable -// FIXME: CF types get their ObjC class dynamically looked up during dynamic -// casting. -fooify(CFSetCreate(kCFAllocatorDefault, nil, 0, nil)!) // TODO-NEXT: CFSet CHECK-NEXT: not fooable -fooify(CFArrayCreate(kCFAllocatorDefault, nil, 0, nil)!) // CHECK-NEXT: not fooable -fooify(NSString()) // CHECK-NEXT: NSString -fooify(NSMutableString()) // CHECK-NEXT: NSString -fooify(NSSet()) // CHECK-NEXT: not fooable +var ProtocolLookupForeign = TestSuite("ProtocolLookupForeign") +ProtocolLookupForeign.test("NSRect") { + expectEqual("NSRect", fooify(NSRect())) +} + +ProtocolLookupForeign.test("NSPoint") { + expectEqual("not fooable", fooify(NSPoint())) +} + +ProtocolLookupForeign.test("CFSet") { + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + expectEqual("CFSet", fooify(CFSetCreate(kCFAllocatorDefault, nil, 0, nil)!)) + } +} + +ProtocolLookupForeign.test("CFArray") { + expectEqual("not fooable", fooify(CFArrayCreate(kCFAllocatorDefault, nil, 0, nil)!)) +} + +ProtocolLookupForeign.test("NSString") { + expectEqual("NSString", fooify(NSString())) +} + +ProtocolLookupForeign.test("NSMutableString") { + expectEqual("NSString", fooify(NSMutableString())) +} + +ProtocolLookupForeign.test("NSSet") { + expectEqual("not fooable", fooify(NSSet())) +} + +runAllTests() diff --git a/test/Interpreter/tuple_casts.swift b/test/Interpreter/tuple_casts.swift index 4c0dacafa57f9..0f662932ed6e7 100644 --- a/test/Interpreter/tuple_casts.swift +++ b/test/Interpreter/tuple_casts.swift @@ -1,7 +1,13 @@ // RUN: %target-run-simple-swift -// RUN: %target-build-swift -O %s -o %t/a.out.optimized -// RUN: %target-codesign %t/a.out.optimized -// RUN: %target-run %t/a.out.optimized +// +// RUN: %target-build-swift -swift-version 5 -O %s -o %t/a.swift5.O.out +// RUN: %target-codesign %t/a.swift5.O.out +// RUN: %target-run %t/a.swift5.O.out +// +// RUN: %target-build-swift -swift-version 5 -Onone %s -o %t/a.swift5.Onone.out +// RUN: %target-codesign %t/a.swift5.Onone.out +// RUN: %target-run %t/a.swift5.Onone.out +// // REQUIRES: executable_test import StdlibUnittest @@ -31,15 +37,31 @@ tupleCastTests.test("Adding/removing labels") { String(describing: anyToIntPoint((3, 4)))) expectEqual("(x: 5, y: 6)", String(describing: anyToIntPoint((x: 5, 6)))) + expectEqual("(x: 5, y: 6)", + String(describing: anyToIntPoint((5, y: 6)))) expectEqual("(1, 2)", String(describing: anyToInt2((1, 2)))) expectEqual("(3, 4)", String(describing: anyToInt2((x: 3, y: 4)))) expectEqual("(5, 6)", String(describing: anyToInt2((x: 5, 6)))) + expectEqual("(7, 8)", String(describing: anyToInt2((7, y: 8)))) expectEqual("(first: 1, 2, third: 3)", String(describing: anyToPartlyLabeled((1, 2, 3)))) } +tupleCastTests.test("Label checks on casting") { + expectTrue((x: 1, y: 2) is (Int, Int)) + expectTrue((x: 1, y: 2) is (x: Int, Int)) + expectTrue((x: 1, y: 2) is (Int, y: Int)) + expectTrue((x: 1, y: 2) is (x: Int, y: Int)) + + expectFalse((x: 1, y: 2) is (x: Int, z: Int)) + expectFalse((x: 1, y: 2) is (a: Int, y: Int)) + expectFalse((x: 1, y: 2) is (a: Int, z: Int)) + expectFalse((x: 1, y: 2) is (Int, z: Int)) + expectFalse((x: 1, y: 2) is (a: Int, Int)) +} + tupleCastTests.test("Incorrect labels conditional cast") { expectNil(anyToIntPointOpt((x: 1, z: 2))) expectEqual("Optional((x: 1, y: 2))", diff --git a/test/stdlib/Casts.swift b/test/stdlib/Casts.swift deleted file mode 100644 index 5e81f6643b154..0000000000000 --- a/test/stdlib/Casts.swift +++ /dev/null @@ -1,236 +0,0 @@ -// Casts.swift - Tests for conversion between types. -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -// ----------------------------------------------------------------------------- -/// -/// Contains tests for conversions between types which shouldn't trap. -/// -// ----------------------------------------------------------------------------- -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency) -// REQUIRES: executable_test -// UNSUPPORTED: use_os_stdlib - -import StdlibUnittest -#if _runtime(_ObjC) -import Foundation -#endif - -private func blackhole(_ t: T) {} - -let CastsTests = TestSuite("Casts") - -// Test for SR-426: missing release for some types after failed conversion -CastsTests.test("No leak for failed tuple casts") { - let t: Any = (1, LifetimeTracked(0)) - expectFalse(t is Any.Type) -} - -protocol P {} -class ErrClass : Error { } - -CastsTests.test("No overrelease of existential boxes in failed casts") { - // Test for crash from SR-392 - // We fail casts of an existential box repeatedly - // to ensure it does not get over-released. - func bar(_ t: T) { - for _ in 0..<10 { - if case let a as P = t { - _ = a - } - } - } - - let err: Error = ErrClass() - bar(err) -} - -extension Int : P {} - -// Test for SR-7664: Inconsistent optional casting behaviour with generics -// Runtime failed to unwrap multiple levels of Optional when casting. -CastsTests.test("Multi-level optionals can be casted") { - func testSuccess(_ x: From, from: From.Type, to: To.Type) { - expectNotNil(x as? To) - } - func testFailure(_ x: From, from: From.Type, to: To.Type) { - expectNil(x as? To) - } - testSuccess(42, from: Int?.self, to: Int.self) - testSuccess(42, from: Int??.self, to: Int.self) - testSuccess(42, from: Int???.self, to: Int.self) - testSuccess(42, from: Int???.self, to: Int?.self) - testSuccess(42, from: Int???.self, to: Int??.self) - testSuccess(42, from: Int???.self, to: Int???.self) - testFailure(42, from: Int?.self, to: String.self) - testFailure(42, from: Int??.self, to: String.self) - testFailure(42, from: Int???.self, to: String.self) -} - -// Test for SR-9837: Optional.none not casting to Optional.none in generic context -CastsTests.test("Optional.none can be casted to Optional.none in generic context") { - func test(_ type: T.Type) -> T? { - return Any?.none as? T - } - - expectEqual(type(of: test(Bool.self)), Bool?.self) - expectEqual(type(of: test(Bool?.self)), Bool??.self) -} - -// Test for SR-3871: Cannot cast from ObjC existential without going through AnyObject -#if _runtime(_ObjC) -protocol P2 {} -CastsTests.test("Cast from ObjC existential to Protocol (SR-3871)") { - if #available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) { - struct S: P2 {} - - class ObjCWrapper { - @objc dynamic let any: Any = S() - init() {} - } - let a = ObjCWrapper().any - expectTrue(a is P2) - // In SR-3871, the following cast failed (everything else here succeeded) - expectNotNil(a as? P2) - expectNotNil(a as? S) - let b = a as AnyObject - expectTrue(a is P2) - expectNotNil(b as? P2) - expectNotNil(b as? S) - } -} -#endif - -protocol P3 {} -CastsTests.test("Cast from Swift existential to Protocol") { - struct S: P3 {} - class SwiftWrapper { - let any: Any = S() - init() {} - } - let a = SwiftWrapper().any - expectTrue(a is P3) - expectNotNil(a as? P3) - expectNotNil(a as? S) - let b = a as AnyObject - expectTrue(b is P3) - expectNotNil(b as? P3) - expectNotNil(b as? S) -} - - -#if _runtime(_ObjC) -extension CFBitVector : P { - static func makeImmutable(from values: Array) -> CFBitVector { - return CFBitVectorCreate(/*allocator:*/ nil, values, values.count * 8) - } -} - -extension CFMutableBitVector { - static func makeMutable(from values: Array) -> CFMutableBitVector { - return CFBitVectorCreateMutableCopy( - /*allocator:*/ nil, - /*capacity:*/ 0, - CFBitVector.makeImmutable(from: values)) - } -} - -func isP(_ t: T) -> Bool { - return t is P -} - -CastsTests.test("Dynamic casts of CF types to protocol existentials") - .skip(.custom( - { !_isDebugAssertConfiguration() }, - reason: "This test behaves unpredictably in optimized mode.")) - .code { - expectTrue(isP(10 as Int)) - - // FIXME: SR-2289: dynamic casting of CF types to protocol existentials - // should work, but there is a bug in the runtime that prevents them from - // working. - expectFailure { - expectTrue(isP(CFBitVector.makeImmutable(from: [10, 20]))) - } - expectFailure { - expectTrue(isP(CFMutableBitVector.makeMutable(from: [10, 20]))) - } -} -#endif - -CastsTests.test("Any.Protocol") { - class C {} - struct S {} - func isAnyProtocol(_ type: T.Type) -> Bool { - let result = T.self is Any.Protocol - if result { - // `as!` should succeed if `is` does - blackhole(T.self as! Any.Protocol) - } - return result - } - func isAnyType(_ type: T.Type) -> Bool { - return T.self is Any.Type - } - func isType(_ type: T.Type, to: U.Type) -> Bool { - return T.self is U.Type - } - - expectTrue(Int.self is Any.Type) - expectNotNil(Int.self as? Any.Type) - expectTrue(isAnyType(Int.self)) - expectFalse(Int.self is Any.Protocol) - expectNil(Int.self as? Any.Protocol) - expectFalse(isAnyProtocol(Int.self)) - expectFalse(isType(Int.self, to: Any.self)) - - expectTrue(C.self is Any.Type) - expectNotNil(C.self as? Any.Type) - expectTrue(isAnyType(C.self)) - expectFalse(C.self is Any.Protocol) - expectNil(C.self as? Any.Protocol) - expectFalse(isAnyProtocol(C.self)) - expectFalse(isType(C.self, to: Any.self)) - - expectTrue(S.self is Any.Type) - expectNotNil(S.self as? Any.Type) - expectTrue(isAnyType(S.self)) - expectFalse(S.self is Any.Protocol) - expectNil(S.self as? Any.Protocol) - expectFalse(isAnyProtocol(S.self)) - expectFalse(isType(S.self, to: Any.self)) - - expectTrue(Any.self is Any.Type) - expectNotNil(Any.self as? Any.Type) - expectTrue(isAnyType(Any.self)) - expectTrue(Any.self is Any.Protocol) - expectNotNil(Any.self as? Any.Protocol) - expectTrue(isAnyProtocol(Any.self)) - expectTrue(isType(Any.self, to: Any.self)) - - expectTrue(Any?.self is Any.Type) - expectNotNil(Any?.self as? Any.Type) - expectTrue(isAnyType(Any?.self)) - expectFalse(Any?.self is Any.Protocol) - expectNil(Any?.self as? Any.Protocol) - expectFalse(isAnyProtocol(Any?.self)) - expectFalse(isType(Any?.self, to: Any.self)) -} - -CastsTests.test("Async function types") { - let asyncFnType: Any.Type = (() async -> Void).self - let fnType: Any.Type = (() -> Void).self - - expectTrue(fnType is (() -> Void).Type) - expectTrue(asyncFnType is (() async -> Void).Type) - expectFalse(fnType is (() async -> Void).Type) - expectFalse(asyncFnType is (() -> Void).Type) -} - -runAllTests() diff --git a/validation-test/Casting/BoxingCasts.swift.gyb b/validation-test/Casting/BoxingCasts.swift.gyb new file mode 100644 index 0000000000000..7ee19e67a85fb --- /dev/null +++ b/validation-test/Casting/BoxingCasts.swift.gyb @@ -0,0 +1,264 @@ +// BoxingCasts.swift - Tests for boxing/unboxing casts +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +// ----------------------------------------------------------------------------- +/// +/// Contains tests for existential, optional, and other casts that box/unbox values. +/// +// ----------------------------------------------------------------------------- +// RUN: %empty-directory(%t) +// +// RUN: %gyb %s -o %t/BoxingCasts.swift +// RUN: %line-directive %t/BoxingCasts.swift -- %target-build-swift -g -module-name a -swift-version 5 -Onone %t/BoxingCasts.swift -o %t/a.swift5.Onone.out +// RUN: %target-codesign %t/a.swift5.Onone.out +// RUN: %line-directive %t/BoxingCasts.swift -- %target-run %t/a.swift5.Onone.out +// +// Note: The RUN directives above override the default test optimizations. +// This test is deliberately run non-optimized in order to verify the +// behavior of runtime methods that may not be called for optimized casts. +// +// XXX FIXME XXX TODO XXX _Also_ build this with optimizations in order to +// verify compiler behaviors. +// +// REQUIRES: executable_test + +import StdlibUnittest +#if _runtime(_ObjC) +import Foundation +#endif + +fileprivate func runtimeCast (_ x: From, to: To.Type) -> To? { + return x as? To +} + +fileprivate func optional(_ x: T) -> Optional { + return runtimeCast(x, to: Optional.self)! +} + +fileprivate protocol FilePrivateProtocol {} +internal protocol InternalProtocol {} +public protocol PublicProtocol {} +protocol UnimplementedProtocol {} + +fileprivate enum EmptyEnum: FilePrivateProtocol, InternalProtocol, PublicProtocol { } + +fileprivate enum SingleCaseEnum: FilePrivateProtocol, InternalProtocol, PublicProtocol { +case case0 + init() {self = .case0} +} + +fileprivate enum TrivialPayloadEnum: FilePrivateProtocol, InternalProtocol, PublicProtocol { +case payloadCase(Int) + init() {self = .payloadCase(42)} +} + +extension TrivialPayloadEnum: Hashable {} + +fileprivate enum MultiPayloadEnum: FilePrivateProtocol, InternalProtocol, PublicProtocol { +case case0(String) +case case1(Int) + init() {self = .case1(42)} +} + +extension MultiPayloadEnum: Hashable {} + +fileprivate class ClassInt: FilePrivateProtocol, InternalProtocol, PublicProtocol { + public var value: Int + private var tracker = LifetimeTracked(77) + init(_ v: Int = 42) {value = v} +} + +extension ClassInt: Equatable, Hashable { + static func ==(left: ClassInt, right: ClassInt) -> Bool {left.value == right.value} + func hash(into hasher: inout Hasher) { value.hash(into: &hasher) } +} + +fileprivate struct StructInt: FilePrivateProtocol, InternalProtocol, PublicProtocol { + public var value: Int + private var tracker = LifetimeTracked(77) + init(_ v: Int = 42) { value = v} +} + +extension StructInt: Hashable, Equatable { } + +#if _runtime(_ObjC) +fileprivate class OCClassInt: NSObject, FilePrivateProtocol, InternalProtocol, PublicProtocol { + public var value: Int + private var tracker = LifetimeTracked(77) + init(_ v: Int = 42) { value = v} +} +#endif + +let BoxingCasts = TestSuite("BoxingCasts") + +%{ +import random +# The test body goes into a named function and the test case just invokes that +# function by name. This makes debugging easier, since it's easier to set break +# points on named functions than on arbitrary closures. +# The function names are included in the test name +# for ease of reference. +testNumber = 0 +def testFunctionName(): + return "test{number}".format(number=testNumber) +def nextTestNumber(): + global testNumber + testNumber += 1 + +# Type used for intermediate casts. The base object gets +# cast to one or more of these before the final test. +class Box: + def __init__(self, name, typeName=None, cast=None): + self.name = name + self.typeName = typeName or name + self.cast_template = cast or "runtimeCast({expr}, to: {typeName}.self)!" + def cast_oper(self, expr): + return self.cast_template.format(expr=expr, typeName=self.typeName) + +anyHashableBox = Box(name="AnyHashable") +all_boxes = [ + Box(name="Any", typeName="Any"), + Box(name="AnyStatic", cast="({expr} as Any)"), + Box(name="AnyObject"), + Box(name="SwiftValueBox", cast="_bridgeAnythingToObjectiveC({expr})"), + Box(name="Optional", cast="optional({expr})") +] +protocol_boxes = [ + Box(name="PublicProtocol"), +# Box(name="FilePrivateProtocol"), # Blocked by SR-2620 aka rdar://28281488 + Box(name="InternalProtocol"), +] + +# Describes a base object that will be subject to a variety of casts +default_protocols = [ + "PublicProtocol", + "InternalProtocol", + # "FilePrivateProtocol" # Blocked by SR-2620 aka rdar://28281488 +] + +class Contents: + def __init__(self, name, constructor=None, extra_targets=[], hashable=True, roundtrips=True, protocols=default_protocols, objc_only=False): + self.name = name + self.constructor = constructor or "{name}()".format(name=name) + self.objc_only = objc_only + + self.targets = ["Any"] + self.targets.extend(protocols) + self.targets.extend(extra_targets) + if roundtrips: + self.targets.append(self.name) + + self.boxes = [] + self.boxes.extend(all_boxes) + self.boxes.extend([Box(name=n) for n in protocols]) + if hashable: + self.boxes.append(anyHashableBox) + +contents = [ + Contents(name="StructInt", + # extra_targets=["StructInt?"], + ), + Contents(name="StructInt?", + constructor="Optional.some(StructInt())", + extra_targets=["StructInt"], + roundtrips=False, # Compiler bug rejects roundtrip cast T? => Any => T? + ), + Contents(name="ClassInt"), + Contents(name="OCClassInt", objc_only=True), + Contents(name="SingleCaseEnum"), + Contents(name="TrivialPayloadEnum"), + Contents(name="TrivialPayloadEnum"), + Contents(name="MultiPayloadEnum"), + Contents(name="StructInt.Type", + constructor="StructInt.self", + hashable=False, + protocols=[], + ), + Contents(name="StructInt.Type?", + constructor="Optional.some(StructInt.self)", + hashable=False, + protocols=[], + extra_targets=["StructInt.Type"], + roundtrips=False, # Compiler bug rejects roundtrip cast T? => Any => T? + ), + Contents(name="PublicProtocol.Protocol", + constructor="PublicProtocol.self", + hashable=False, + protocols=[], + ), +] + +# Code below generates a separate test case for each combination of content, +# target type, and one or more box/intermediate types. +}% + +% for content in contents: +% if content.objc_only: +#if _runtime(_ObjC) +% end +% for target in content.targets: +% for box in content.boxes: +% nextTestNumber() +BoxingCasts.test("${testFunctionName()}(): Casting ${box.name}(${content.name}) to ${target}") { + // TODO: Selectively enable/disable cases that work with earlier stdlib + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + ${testFunctionName()}() + } +} +func ${testFunctionName()}() { + let a = ${content.constructor} + let b = ${box.cast_oper("a")} +% # // Skip trivial cast from T? to T +% if not (content.name == target and box.name == "Optional"): +% # // Skip trivial cast from protocol box to protocol +% if box.name != target: +% # // Skip trivial cast from T? => P +% if not (target.endswith("Protocol") and box.name == "Optional"): + let c = /* ${box.name}(${content.name})) */ b as? ${target} + expectNotNil(c) +% end +% end +% end + let d = runtimeCast(/* ${box.name}(${content.name}) */ b, to: ${target}.self) + expectNotNil(d) +} + +% for innerBox in [random.choice(content.boxes)]: +% nextTestNumber() +BoxingCasts.test("${testFunctionName()}(): Casting ${box.name}(${innerBox.name}(${content.name})) to ${target}") { + // TODO: Selectively enable/disable cases that work with earlier stdlib + if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) { + ${testFunctionName()}() + } +} +func ${testFunctionName()}() { + let a = ${content.constructor} + let b = ${innerBox.cast_oper("a")} + let c = ${box.cast_oper("b")} +% # // Skip trivial cast from T? to T +% if not (innerBox.name == target and box.name == "Optional"): +% # // Skip trivial cast from protocol box to protocol +% if box.name != target: + let d = /* ${box.name}(${innerBox.name}(${content.name})) */ c as? ${target} + expectNotNil(d) +% end +% end + let e = runtimeCast(/* ${box.name}(${innerBox.name}(${content.name})) */ c, to: ${target}.self) + expectNotNil(e) +} +% end +% end +% end +% if content.objc_only: +#endif +% end +% end + +runAllTests() From 9665091121fb29edba277bc9782a30db8b70e507 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 27 Aug 2020 11:51:00 -0700 Subject: [PATCH 073/120] [CSBindings] Don't attempt to join types that contain holes --- lib/AST/TypeJoinMeet.cpp | 3 +++ lib/Sema/CSBindings.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/AST/TypeJoinMeet.cpp b/lib/AST/TypeJoinMeet.cpp index a5609f8b669fb..468cb6810fc1a 100644 --- a/lib/AST/TypeJoinMeet.cpp +++ b/lib/AST/TypeJoinMeet.cpp @@ -87,6 +87,9 @@ struct TypeJoin : CanTypeVisitor { assert(!first->hasTypeVariable() && !second->hasTypeVariable() && "Cannot compute join of types involving type variables"); + assert(!first->hasHole() && !second->hasHole() && + "Cannot compute join of types involving type holes"); + assert(first->getWithoutSpecifierType()->isEqual(first) && "Expected simple type!"); assert(second->getWithoutSpecifierType()->isEqual(second) && diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 807a86b01a85a..a82b389991788 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -481,6 +481,7 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding( if (binding.Kind == AllowedBindingKind::Supertypes && !binding.BindingType->hasUnresolvedType() && !binding.BindingType->hasTypeVariable() && + !binding.BindingType->hasHole() && !binding.BindingType->hasUnboundGenericType() && !binding.hasDefaultedLiteralProtocol() && !binding.isDefaultableBinding() && allowJoinMeet) { From 83b50cd8e360d78dc320fd70020c5a08899994cb Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 27 Aug 2020 11:53:33 -0700 Subject: [PATCH 074/120] [AutoDiff] Add missing `withoutDerivative(at:)` fix-its. (#33660) Add `withoutDerivative(at:)` fix-its for errors regarding non-differentiable arguments and results. --- .../Differentiation/JVPCloner.cpp | 22 ++++++++++++++----- .../Differentiation/VJPCloner.cpp | 21 +++++++++++++----- ...erentiation_control_flow_diagnostics.swift | 2 +- .../differentiation_diagnostics.swift | 8 +++---- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index d7b8caa102e53..2586e5ac6ad2e 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -553,9 +553,14 @@ class JVPCloner::Implementation final if (!originalFnTy->getParameters()[paramIndex] .getSILStorageInterfaceType() .isDifferentiable(getModule())) { - context.emitNondifferentiabilityError( - ai->getArgumentsWithoutIndirectResults()[paramIndex], - invoker, diag::autodiff_nondifferentiable_argument); + auto arg = ai->getArgumentsWithoutIndirectResults()[paramIndex]; + auto startLoc = arg.getLoc().getStartSourceLoc(); + auto endLoc = arg.getLoc().getEndSourceLoc(); + context + .emitNondifferentiabilityError( + arg, invoker, diag::autodiff_nondifferentiable_argument) + .fixItInsert(startLoc, "withoutDerivative(at: ") + .fixItInsertAfter(endLoc, ")"); errorOccurred = true; return true; } @@ -573,9 +578,14 @@ class JVPCloner::Implementation final .getSILStorageInterfaceType(); } if (!remappedResultType.isDifferentiable(getModule())) { - context.emitNondifferentiabilityError( - origCallee, invoker, - diag::autodiff_nondifferentiable_result); + auto startLoc = ai->getLoc().getStartSourceLoc(); + auto endLoc = ai->getLoc().getEndSourceLoc(); + context + .emitNondifferentiabilityError( + origCallee, invoker, + diag::autodiff_nondifferentiable_result) + .fixItInsert(startLoc, "withoutDerivative(at: ") + .fixItInsertAfter(endLoc, ")"); errorOccurred = true; return true; } diff --git a/lib/SILOptimizer/Differentiation/VJPCloner.cpp b/lib/SILOptimizer/Differentiation/VJPCloner.cpp index 7279ce914d83c..f58db200debe9 100644 --- a/lib/SILOptimizer/Differentiation/VJPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/VJPCloner.cpp @@ -457,9 +457,14 @@ class VJPCloner::Implementation final if (!originalFnTy->getParameters()[paramIndex] .getSILStorageInterfaceType() .isDifferentiable(getModule())) { - context.emitNondifferentiabilityError( - ai->getArgumentsWithoutIndirectResults()[paramIndex], invoker, - diag::autodiff_nondifferentiable_argument); + auto arg = ai->getArgumentsWithoutIndirectResults()[paramIndex]; + auto startLoc = arg.getLoc().getStartSourceLoc(); + auto endLoc = arg.getLoc().getEndSourceLoc(); + context + .emitNondifferentiabilityError( + arg, invoker, diag::autodiff_nondifferentiable_argument) + .fixItInsert(startLoc, "withoutDerivative(at: ") + .fixItInsertAfter(endLoc, ")"); errorOccurred = true; return true; } @@ -477,8 +482,14 @@ class VJPCloner::Implementation final .getSILStorageInterfaceType(); } if (!remappedResultType.isDifferentiable(getModule())) { - context.emitNondifferentiabilityError( - origCallee, invoker, diag::autodiff_nondifferentiable_result); + auto startLoc = ai->getLoc().getStartSourceLoc(); + auto endLoc = ai->getLoc().getEndSourceLoc(); + context + .emitNondifferentiabilityError( + origCallee, invoker, + diag::autodiff_nondifferentiable_result) + .fixItInsert(startLoc, "withoutDerivative(at: ") + .fixItInsertAfter(endLoc, ")"); errorOccurred = true; return true; } diff --git a/test/AutoDiff/SILOptimizer/differentiation_control_flow_diagnostics.swift b/test/AutoDiff/SILOptimizer/differentiation_control_flow_diagnostics.swift index 331a48a425c2c..5248811b04aa0 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_control_flow_diagnostics.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_control_flow_diagnostics.swift @@ -171,7 +171,7 @@ func loop_array(_ array: [Float]) -> Float { var result: Float = 1 // TODO(TF-957): Improve non-differentiability errors for for-in loops // (`Collection.makeIterator` and `IteratorProtocol.next`). - // expected-note @+1 {{cannot differentiate through a non-differentiable result; do you want to use 'withoutDerivative(at:)'?}} + // expected-note @+1 {{cannot differentiate through a non-differentiable result; do you want to use 'withoutDerivative(at:)'?}} {{12-12=withoutDerivative(at: }} {{17-17=)}} for x in array { result = result * x } diff --git a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift index cbb4e0fa132f7..22d3fca6a17c3 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift @@ -301,14 +301,14 @@ struct TF_687 : Differentiable { } } // expected-error @+2 {{function is not differentiable}} -// expected-note @+1 {{cannot differentiate through a non-differentiable argument; do you want to use 'withoutDerivative(at:)'?}} +// expected-note @+1 {{cannot differentiate through a non-differentiable argument; do you want to use 'withoutDerivative(at:)'?}} {{78-78=withoutDerivative(at: }} {{79-79=)}} let _: @differentiable (Float) -> TF_687 = { x in TF_687(x, dummy: x) } // expected-error @+1 {{function is not differentiable}} @differentiable // expected-note @+1 {{when differentiating this function definition}} func roundingGivesError(x: Float) -> Float { - // expected-note @+1 {{cannot differentiate through a non-differentiable result; do you want to use 'withoutDerivative(at:)'?}} + // expected-note @+1 {{cannot differentiate through a non-differentiable result; do you want to use 'withoutDerivative(at:)'?}} {{16-16=withoutDerivative(at: }} {{22-22=)}} return Float(Int(x)) } @@ -688,7 +688,7 @@ func differentiableProjectedValueAccess(_ s: Struct) -> Float { // expected-note @+2 {{when differentiating this function definition}} @differentiable func projectedValueAccess(_ s: Struct) -> Float { - // expected-note @+1 {{cannot differentiate through a non-differentiable result; do you want to use 'withoutDerivative(at:)'?}} + // expected-note @+1 {{cannot differentiate through a non-differentiable result; do you want to use 'withoutDerivative(at:)'?}} {{3-3=withoutDerivative(at: }} {{7-7=)}} s.$y.wrappedValue } @@ -714,7 +714,7 @@ func modify(_ s: Struct, _ x: Float) -> Float { func tupleArrayLiteralInitialization(_ x: Float, _ y: Float) -> Float { // `Array<(Float, Float)>` does not conform to `Differentiable`. let array = [(x * y, x * y)] - // expected-note @+1 {{cannot differentiate through a non-differentiable argument; do you want to use 'withoutDerivative(at:)'?}} + // expected-note @+1 {{cannot differentiate through a non-differentiable argument; do you want to use 'withoutDerivative(at:)'?}} {{10-10=withoutDerivative(at: }} {{15-15=)}} return array[0].0 } From 103b61c8be901bf003c37faaae241a9af2cc2db1 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 27 Aug 2020 12:06:25 -0700 Subject: [PATCH 075/120] ABIChecker: rename argument -protocol-requirement-white-list to -protocol-requirement-allow-list --- test/api-digester/compare-clang-dump.swift | 2 +- .../swift-api-digester/swift-api-digester.cpp | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/api-digester/compare-clang-dump.swift b/test/api-digester/compare-clang-dump.swift index cdb59d1bf93a4..3cafec46d6288 100644 --- a/test/api-digester/compare-clang-dump.swift +++ b/test/api-digester/compare-clang-dump.swift @@ -3,7 +3,7 @@ // RUN: %empty-directory(%t.module-cache) // RUN: %api-digester -dump-sdk -module Foo -o %t.dump1.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %S/Inputs/Foo -avoid-location // RUN: %api-digester -dump-sdk -module Foo -o %t.dump2.json -module-cache-path %t.module-cache %clang-importer-sdk-nosource -I %S/Inputs/Foo-new-version -avoid-location -// RUN: %api-digester -diagnose-sdk -protocol-requirement-white-list %S/Inputs/Foo-prot-allowlist.txt -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result +// RUN: %api-digester -diagnose-sdk -protocol-requirement-allow-list %S/Inputs/Foo-prot-allowlist.txt -print-module --input-paths %t.dump1.json -input-paths %t.dump2.json -o %t.result // RUN: %clang -E -P -x c %S/Outputs/Foo-diff.txt -o - | sed '/^\s*$/d' > %t.expected // RUN: %clang -E -P -x c %t.result -o - | sed '/^\s*$/d' > %t.result.tmp diff --git a/tools/swift-api-digester/swift-api-digester.cpp b/tools/swift-api-digester/swift-api-digester.cpp index 2b089bf718c5f..4980d39d15e09 100644 --- a/tools/swift-api-digester/swift-api-digester.cpp +++ b/tools/swift-api-digester/swift-api-digester.cpp @@ -72,7 +72,7 @@ ModuleList("module-list-file", llvm::cl::cat(Category)); static llvm::cl::opt -ProtReqWhiteList("protocol-requirement-white-list", +ProtReqAllowList("protocol-requirement-allow-list", llvm::cl::desc("File containing a new-line separated list of protocol names"), llvm::cl::cat(Category)); @@ -1074,7 +1074,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { SDKContext &Ctx; UpdatedNodesMap &UpdateMap; - llvm::StringSet<> ProtocolReqWhitelist; + llvm::StringSet<> ProtocolReqAllowlist; SDKNodeRoot *LeftRoot; SDKNodeRoot *RightRoot; @@ -1123,10 +1123,10 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { public: PrunePass(SDKContext &Ctx): Ctx(Ctx), UpdateMap(Ctx.getNodeUpdateMap()) {} - PrunePass(SDKContext &Ctx, llvm::StringSet<> prWhitelist): + PrunePass(SDKContext &Ctx, llvm::StringSet<> prAllowlist): Ctx(Ctx), UpdateMap(Ctx.getNodeUpdateMap()), - ProtocolReqWhitelist(std::move(prWhitelist)) {} + ProtocolReqAllowlist(std::move(prAllowlist)) {} void diagnoseMissingAvailable(SDKNodeDecl *D) { // For extensions of external types, we diagnose individual member's missing @@ -1178,7 +1178,7 @@ class PrunePass : public MatchedNodeListener, public SDKTreeDiffPass { ShouldComplain = false; } if (ShouldComplain && - ProtocolReqWhitelist.count(getParentProtocolName(D))) { + ProtocolReqAllowlist.count(getParentProtocolName(D))) { // Ignore protocol requirement additions if the protocol has been added // to the allowlist. ShouldComplain = false; @@ -2311,7 +2311,7 @@ createDiagConsumer(llvm::raw_ostream &OS, bool &FailOnError) { static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, SDKNodeRoot *RightModule, StringRef OutputPath, - llvm::StringSet<> ProtocolReqWhitelist) { + llvm::StringSet<> ProtocolReqAllowlist) { assert(LeftModule); assert(RightModule); llvm::raw_ostream *OS = &llvm::errs(); @@ -2334,7 +2334,7 @@ static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, RightModule->getJsonFormatVersion())); TypeAliasDiffFinder(LeftModule, RightModule, Ctx.getTypeAliasUpdateMap()).search(); - PrunePass Prune(Ctx, std::move(ProtocolReqWhitelist)); + PrunePass Prune(Ctx, std::move(ProtocolReqAllowlist)); Prune.pass(LeftModule, RightModule); ChangeRefinementPass RefinementPass(Ctx.getNodeUpdateMap()); RefinementPass.pass(LeftModule, RightModule); @@ -2347,7 +2347,7 @@ static int diagnoseModuleChange(SDKContext &Ctx, SDKNodeRoot *LeftModule, static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath, StringRef OutputPath, CheckerOptions Opts, - llvm::StringSet<> ProtocolReqWhitelist) { + llvm::StringSet<> ProtocolReqAllowlist) { if (!fs::exists(LeftPath)) { llvm::errs() << LeftPath << " does not exist\n"; return 1; @@ -2362,7 +2362,7 @@ static int diagnoseModuleChange(StringRef LeftPath, StringRef RightPath, SwiftDeclCollector RightCollector(Ctx); RightCollector.deSerialize(RightPath); diagnoseModuleChange(Ctx, LeftCollector.getSDKRoot(), RightCollector.getSDKRoot(), - OutputPath, std::move(ProtocolReqWhitelist)); + OutputPath, std::move(ProtocolReqAllowlist)); return options::CompilerStyleDiags && Ctx.getDiags().hadAnyError() ? 1 : 0; } @@ -2837,9 +2837,9 @@ int main(int argc, char *argv[]) { case ActionType::MigratorGen: case ActionType::DiagnoseSDKs: { ComparisonInputMode Mode = checkComparisonInputMode(); - llvm::StringSet<> protocolWhitelist; - if (!options::ProtReqWhiteList.empty()) { - if (readFileLineByLine(options::ProtReqWhiteList, protocolWhitelist)) + llvm::StringSet<> protocolAllowlist; + if (!options::ProtReqAllowList.empty()) { + if (readFileLineByLine(options::ProtReqAllowList, protocolAllowlist)) return 1; } if (options::Action == ActionType::MigratorGen) { @@ -2853,21 +2853,21 @@ int main(int argc, char *argv[]) { return diagnoseModuleChange(options::SDKJsonPaths[0], options::SDKJsonPaths[1], options::OutputFile, Opts, - std::move(protocolWhitelist)); + std::move(protocolAllowlist)); } case ComparisonInputMode::BaselineJson: { SDKContext Ctx(Opts); return diagnoseModuleChange(Ctx, getBaselineFromJson(argv[0], Ctx), getSDKRoot(argv[0], Ctx, false), options::OutputFile, - std::move(protocolWhitelist)); + std::move(protocolAllowlist)); } case ComparisonInputMode::BothLoad: { SDKContext Ctx(Opts); return diagnoseModuleChange(Ctx, getSDKRoot(argv[0], Ctx, true), getSDKRoot(argv[0], Ctx, false), options::OutputFile, - std::move(protocolWhitelist)); + std::move(protocolAllowlist)); } } } From 80560dab2582172e0684080382586591a55cb342 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 27 Aug 2020 22:17:09 +0300 Subject: [PATCH 076/120] CSGen: Infer generic arguments in explicit closure result types --- lib/Sema/CSGen.cpp | 14 +++++++++----- test/Constraints/closures.swift | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 0891e23c6474c..37bc51c12508b 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2073,12 +2073,14 @@ namespace { return resolveTypeReferenceInExpression( closure->getExplicitResultTypeRepr(), - TypeResolverContext::InExpression, nullptr); + TypeResolverContext::InExpression, + // Introduce type variables for unbound generics. + OpenUnboundGenericType( + CS, CS.getConstraintLocator(closure, + ConstraintLocator::ClosureResult))); }; Type resultTy; - auto *resultLoc = - CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult); if (auto explicityTy = getExplicitResultType()) { resultTy = explicityTy; } else { @@ -2099,9 +2101,11 @@ namespace { // If this is a multi-statement closure, let's mark result // as potential hole right away. resultTy = CS.createTypeVariable( - resultLoc, + CS.getConstraintLocator(closure, + ConstraintLocator::ClosureResult), shouldTypeCheckInEnclosingExpression(closure) - ? 0 : TVO_CanBindToHole); + ? 0 + : TVO_CanBindToHole); } } diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index b6f99b7350729..0b77ce6ac87b2 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1030,3 +1030,15 @@ func sr12815() { .doesntExist2() { $0 } } } + +// Make sure we can infer generic arguments in an explicit result type. +let explicitUnboundResult1 = { () -> Array in [0] } +let explicitUnboundResult2: (Array) -> Array = { + (arr: Array) -> Array in [0] +} +// FIXME: Should we prioritize the contextual result type and infer Array +// rather than using a type variable in these cases? +// expected-error@+1 {{unable to infer closure type in the current context}} +let explicitUnboundResult3: (Array) -> Array = { + (arr: Array) -> Array in [true] +} From 61d86d5fd2652103cb09d6b2fe7dcb48c3696104 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 27 Aug 2020 22:23:19 +0300 Subject: [PATCH 077/120] [NFC] CSGen: Clean up some flow in inferClosureType --- lib/Sema/CSGen.cpp | 70 +++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 37bc51c12508b..f4dff643717aa 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2062,52 +2062,38 @@ namespace { // parameter or return type is omitted, a fresh type variable is used to // stand in for that parameter or return type, allowing it to be inferred // from context. - auto getExplicitResultType = [&]() -> Type { - if (!closure->hasExplicitResultType()) { - return Type(); - } + Type resultTy = [&] { + if (closure->hasExplicitResultType()) { + if (auto declaredTy = closure->getExplicitResultType()) { + return declaredTy; + } - if (auto declaredTy = closure->getExplicitResultType()) { - return declaredTy; + const auto resolvedTy = resolveTypeReferenceInExpression( + closure->getExplicitResultTypeRepr(), + TypeResolverContext::InExpression, + // Introduce type variables for unbound generics. + OpenUnboundGenericType( + CS, CS.getConstraintLocator( + closure, ConstraintLocator::ClosureResult))); + if (resolvedTy) + return resolvedTy; } - return resolveTypeReferenceInExpression( - closure->getExplicitResultTypeRepr(), - TypeResolverContext::InExpression, - // Introduce type variables for unbound generics. - OpenUnboundGenericType( - CS, CS.getConstraintLocator(closure, - ConstraintLocator::ClosureResult))); - }; - - Type resultTy; - if (auto explicityTy = getExplicitResultType()) { - resultTy = explicityTy; - } else { - auto getContextualResultType = [&]() -> Type { - if (auto contextualType = CS.getContextualType(closure)) { - if (auto fnType = contextualType->getAs()) - return fnType->getResult(); - } - return Type(); - }; - - if (auto contextualResultTy = getContextualResultType()) { - resultTy = contextualResultTy; - } else { - // If no return type was specified, create a fresh type - // variable for it and mark it as possible hole. - // - // If this is a multi-statement closure, let's mark result - // as potential hole right away. - resultTy = CS.createTypeVariable( - CS.getConstraintLocator(closure, - ConstraintLocator::ClosureResult), - shouldTypeCheckInEnclosingExpression(closure) - ? 0 - : TVO_CanBindToHole); + if (auto contextualType = CS.getContextualType(closure)) { + if (auto fnType = contextualType->getAs()) + return fnType->getResult(); } - } + + // If no return type was specified, create a fresh type + // variable for it and mark it as possible hole. + // + // If this is a multi-statement closure, let's mark result + // as potential hole right away. + return Type(CS.createTypeVariable( + CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult), + shouldTypeCheckInEnclosingExpression(closure) ? 0 + : TVO_CanBindToHole)); + }(); return FunctionType::get(closureParams, resultTy, extInfo); } From 84c50655472e5bf5329ddc5e3d9f0386acfbd07e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 27 Aug 2020 12:48:41 -0700 Subject: [PATCH 078/120] [SR-13461] Relax An Assert This assert doesn't consider reference storage types in its predicate. Look through them since they're not relevant to the type consistency check it's trying to pick out. --- lib/SILGen/SILGenConstructor.cpp | 8 +++----- .../compiler_crashers_2_fixed/sr13461.swift | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 validation-test/compiler_crashers_2_fixed/sr13461.swift diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 0aef4de8034b3..0f4866879fb90 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -216,11 +216,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, .forwardInto(SGF, Loc, init.get()); ++elti; } else { -#ifndef NDEBUG - assert( - field->getType()->isEqual(field->getParentInitializer()->getType()) - && "Checked by sema"); -#endif + assert(field->getType()->getReferenceStorageReferent()->isEqual( + field->getParentInitializer()->getType()) && + "Initialization of field with mismatched type!"); // Cleanup after this initialization. FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); diff --git a/validation-test/compiler_crashers_2_fixed/sr13461.swift b/validation-test/compiler_crashers_2_fixed/sr13461.swift new file mode 100644 index 0000000000000..b6bda64bdce53 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr13461.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -o /dev/null %s +// REQUIRES: asserts + +final class Klass { + static var current: Klass { + fatalError() + } +} +private struct Build { + let val: T + unowned let unownedBinding = Klass.current + unowned(unsafe) let unownedUnsafeBinding = Klass.current + weak var weakBinding = Klass.current +} +private func phase(_ val: T) -> Build { + return Build(val: val) +} From c890cdd1a379db55e51d7ec8657d55b6dc2e7ba4 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 27 Jul 2020 22:01:54 -0700 Subject: [PATCH 079/120] [AST] Add functionality for computing Clang types for SIL functions. --- include/swift/AST/ASTContext.h | 14 ++++++++++ lib/AST/ASTContext.cpp | 24 ++++++++++++---- lib/AST/ClangTypeConverter.cpp | 51 ++++++++++++++++++++++++++++++++++ lib/AST/ClangTypeConverter.h | 5 ++++ lib/IRGen/GenClangType.cpp | 10 +++---- 5 files changed, 94 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 9281ebcf55c2b..e3ecfbb158dfb 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -62,6 +62,7 @@ namespace swift { class BoundGenericType; class ClangModuleLoader; class ClangNode; + class ClangTypeConverter; class ConcreteDeclRef; class ConstructorDecl; class Decl; @@ -586,6 +587,10 @@ class ASTContext final { Type getBridgedToObjC(const DeclContext *dc, Type type, Type *bridgedValueType = nullptr) const; +private: + ClangTypeConverter &getClangTypeConverter(); + +public: /// Get the Clang type corresponding to a Swift function type. /// /// \param params The function parameters. @@ -595,6 +600,15 @@ class ASTContext final { getClangFunctionType(ArrayRef params, Type resultTy, FunctionTypeRepresentation trueRep); + /// Get the canonical Clang type corresponding to a SIL function type. + /// + /// SIL analog of \c ASTContext::getClangFunctionType . + const clang::Type * + getCanonicalClangFunctionType( + ArrayRef params, Optional result, + const SILFunctionType::ExtInfo incompleteExtInfo, + SILFunctionType::Representation trueRep); + /// Get the Swift declaration that a Clang declaration was exported from, /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 633527ccf7735..b8a0f69ccd747 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4457,16 +4457,30 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, return Type(); } -const clang::Type * -ASTContext::getClangFunctionType(ArrayRef params, - Type resultTy, - FunctionTypeRepresentation trueRep) { +ClangTypeConverter &ASTContext::getClangTypeConverter() { auto &impl = getImpl(); if (!impl.Converter) { auto *cml = getClangModuleLoader(); impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); } - return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep); + return impl.Converter.getValue(); +} + +const clang::Type * +ASTContext::getClangFunctionType(ArrayRef params, + Type resultTy, + FunctionTypeRepresentation trueRep) { + return getClangTypeConverter().getFunctionType(params, resultTy, trueRep); +} + +const clang::Type * +ASTContext::getCanonicalClangFunctionType( + ArrayRef params, + Optional result, + SILFunctionType::ExtInfo incompleteExtInfo, + SILFunctionType::Representation trueRep) { + auto *ty = getClangTypeConverter().getFunctionType(params, result, trueRep); + return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; } const Decl * diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 2a08f78fb282e..9fd13f797798d 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -158,6 +158,57 @@ const clang::Type *ClangTypeConverter::getFunctionType( llvm_unreachable("invalid representation"); } +const clang::Type *ClangTypeConverter::getFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation repr) { + + // Using the interface type is sufficient as type parameters get mapped to + // `id`, since ObjC lightweight generics use type erasure. (See also: SE-0057) + auto resultClangTy = result.hasValue() + ? convert(result.getValue().getInterfaceType()) + : ClangASTContext.VoidTy; + + if (resultClangTy.isNull()) + return nullptr; + + SmallVector extParamInfos; + SmallVector paramsClangTy; + bool someParamIsConsumed = false; + for (auto &p : params) { + auto pc = convert(p.getInterfaceType()); + if (pc.isNull()) + return nullptr; + clang::FunctionProtoType::ExtParameterInfo extParamInfo; + if (p.isConsumed()) { + someParamIsConsumed = true; + extParamInfo = extParamInfo.withIsConsumed(true); + } + extParamInfos.push_back(extParamInfo); + paramsClangTy.push_back(pc); + } + + clang::FunctionProtoType::ExtProtoInfo info(clang::CallingConv::CC_C); + if (someParamIsConsumed) + info.ExtParameterInfos = extParamInfos.begin(); + auto fn = ClangASTContext.getFunctionType(resultClangTy, paramsClangTy, info); + if (fn.isNull()) + return nullptr; + + switch (repr) { + case SILFunctionType::Representation::CFunctionPointer: + return ClangASTContext.getPointerType(fn).getTypePtr(); + case SILFunctionType::Representation::Block: + return ClangASTContext.getBlockPointerType(fn).getTypePtr(); + case SILFunctionType::Representation::Thick: + case SILFunctionType::Representation::Thin: + case SILFunctionType::Representation::Method: + case SILFunctionType::Representation::ObjCMethod: + case SILFunctionType::Representation::WitnessMethod: + case SILFunctionType::Representation::Closure: + llvm_unreachable("Expected a C-compatible representation."); + } +} + clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, StringRef memberName) { auto memberTypeDecl = cast( diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index 450b9e30cf19a..e41a824163312 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -74,6 +74,11 @@ class ClangTypeConverter : ArrayRef params, Type resultTy, AnyFunctionType::Representation repr); + /// Compute the C function type for a SIL function type. + const clang::Type *getFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation repr); + /// Check whether the given Clang declaration is an export of a Swift /// declaration introduced by this converter, and if so, return the original /// Swift declaration. diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index bc788fab6904a..8bc35dd99c134 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -132,10 +132,10 @@ namespace { /// ABI. class GenClangType : public CanTypeVisitor { IRGenModule &IGM; - ClangTypeConverter &Converter; + irgen::ClangTypeConverter &Converter; public: - GenClangType(IRGenModule &IGM, ClangTypeConverter &converter) + GenClangType(IRGenModule &IGM, irgen::ClangTypeConverter &converter) : IGM(IGM), Converter(converter) {} const clang::ASTContext &getClangASTContext() const { @@ -264,8 +264,8 @@ static clang::CanQualType getClangBuiltinTypeFromTypedef( } clang::CanQualType -ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, - CanStructType type) { +irgen::ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, + CanStructType type) { // Handle builtin types by adding entries to the cache that reverse // the mapping done by the importer. We could try to look at the // members of the struct instead, but even if that's ABI-equivalent @@ -748,7 +748,7 @@ clang::CanQualType GenClangType::visitType(CanType type) { llvm_unreachable("Unexpected type in Clang type generation."); } -clang::CanQualType ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { +clang::CanQualType irgen::ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { // Look in the cache. auto it = Cache.find(type); if (it != Cache.end()) { From 1807412e85f5e9fc274e254ad029ebb199f8a3fc Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 14:44:13 -0700 Subject: [PATCH 080/120] [Serialization] Allow different Clang types in deserialization. We could've stored a function pointer or a block pointer or a function reference. --- lib/Serialization/Deserialization.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index ed98ea9976f26..724aef5da6b76 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5413,16 +5413,12 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); - const clang::FunctionType *clangFunctionType = nullptr; + const clang::Type *clangFunctionType = nullptr; if (clangFunctionTypeID) { auto clangType = MF.getClangType(clangFunctionTypeID); if (!clangType) return clangType.takeError(); - // FIXME: allow block pointers here. - clangFunctionType = - dyn_cast_or_null(clangType.get()); - if (!clangFunctionType) - MF.fatal(); + clangFunctionType = clangType.get(); } auto extInfo = From eaac23fdd4dbb2f35c160dabf89ea54557701950 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 15:14:51 -0700 Subject: [PATCH 081/120] [NFC] Remove ASTExtInfo::assertIsFunctionType in favor of checkInvariants. Invariants should be checked only when calling build(), not when the builder itself is created. --- include/swift/AST/ExtInfo.h | 10 +--------- lib/AST/ExtInfo.cpp | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 51592181aabf2..02c78efccf68f 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -194,16 +194,8 @@ class ASTExtInfoBuilder { using Representation = FunctionTypeRepresentation; - static void assertIsFunctionType(const clang::Type *); - ASTExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) - : bits(bits), clangTypeInfo(clangTypeInfo) { - // TODO: [clang-function-type-serialization] Once we start serializing - // the Clang type, we should also assert that the pointer is non-null. - auto Rep = Representation(bits & RepresentationMask); - if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) - assertIsFunctionType(clangTypeInfo.type); - } + : bits(bits), clangTypeInfo(clangTypeInfo) {} public: // Constructor with all defaults. diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp index a75a3ff1d9f5e..5b779920ace0b 100644 --- a/lib/AST/ExtInfo.cpp +++ b/lib/AST/ExtInfo.cpp @@ -19,6 +19,20 @@ #include "clang/AST/Type.h" +static void assertIsFunctionType(const clang::Type *type) { +#ifndef NDEBUG + if (!(type->isFunctionPointerType() || type->isBlockPointerType() || + type->isFunctionReferenceType())) { + llvm::SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Expected a Clang function type wrapped in a pointer type or " + << "a block pointer type but found:\n"; + type->dump(os); + llvm_unreachable(os.str().data()); + } +#endif +} + namespace swift { // MARK: - ClangTypeInfo @@ -53,23 +67,12 @@ void ClangTypeInfo::dump(llvm::raw_ostream &os) const { // MARK: - ASTExtInfoBuilder -void ASTExtInfoBuilder::assertIsFunctionType(const clang::Type *type) { -#ifndef NDEBUG - if (!(type->isFunctionPointerType() || type->isBlockPointerType() || - type->isFunctionReferenceType())) { - SmallString<256> buf; - llvm::raw_svector_ostream os(buf); - os << "Expected a Clang function type wrapped in a pointer type or " - << "a block pointer type but found:\n"; - type->dump(os); - llvm_unreachable(os.str().data()); - } -#endif - return; -} - void ASTExtInfoBuilder::checkInvariants() const { - // TODO: Add validation checks here while making sure things don't blow up. + // TODO: [clang-function-type-serialization] Once we start serializing + // the Clang type, we should also assert that the pointer is non-null. + auto Rep = Representation(bits & RepresentationMask); + if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) + assertIsFunctionType(clangTypeInfo.type); } // MARK: - ASTExtInfo From 8da4d53d2c3a0b3976b14d0e0dc38d1144191fe8 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 16:09:32 -0700 Subject: [PATCH 082/120] [NFC] Use ClangTypeInfo's implicit null state instead of an extra Optional. --- include/swift/AST/ExtInfo.h | 19 +++++-------------- include/swift/AST/Types.h | 2 +- lib/AST/ASTContext.cpp | 12 ++++++------ lib/AST/ASTPrinter.cpp | 6 +++--- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 02c78efccf68f..6b2bc1b8bf7c5 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -246,10 +246,7 @@ class ASTExtInfoBuilder { DifferentiabilityKind::NonDifferentiable; } - /// Get the underlying ClangTypeInfo value if it is not the default value. - Optional getClangTypeInfo() const { - return clangTypeInfo.empty() ? Optional() : clangTypeInfo; - } + ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; } constexpr SILFunctionTypeRepresentation getSILRepresentation() const { unsigned rawRep = bits & RepresentationMask; @@ -396,9 +393,7 @@ class ASTExtInfo { constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } - Optional getClangTypeInfo() const { - return builder.getClangTypeInfo(); - } + ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); } constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } @@ -558,10 +553,8 @@ class SILExtInfoBuilder { DifferentiabilityKind::NonDifferentiable; } - /// Get the underlying ClangTypeInfo value if it is not the default value. - Optional getClangTypeInfo() const { - return clangTypeInfo.empty() ? Optional() : clangTypeInfo; - } + /// Get the underlying ClangTypeInfo value. + ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; } constexpr bool hasSelfParam() const { switch (getRepresentation()) { @@ -689,9 +682,7 @@ class SILExtInfo { constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } - Optional getClangTypeInfo() const { - return builder.getClangTypeInfo(); - } + ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); } constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 1dd1aed9bd772..863cce2503988 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2872,7 +2872,7 @@ class AnyFunctionType : public TypeBase { unsigned NumParams, ExtInfo Info) : TypeBase(Kind, CanTypeContext, properties), Output(Output) { Bits.AnyFunctionType.ExtInfoBits = Info.getBits(); - Bits.AnyFunctionType.HasClangTypeInfo = Info.getClangTypeInfo().hasValue(); + Bits.AnyFunctionType.HasClangTypeInfo = !Info.getClangTypeInfo().empty(); Bits.AnyFunctionType.NumParams = NumParams; assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); // The use of both assert() and static_assert() is intentional. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b8a0f69ccd747..ce4f9c980bf6f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3131,16 +3131,16 @@ FunctionType *FunctionType::get(ArrayRef params, return funcTy; } - Optional clangTypeInfo = info.getClangTypeInfo(); + auto clangTypeInfo = info.getClangTypeInfo(); size_t allocSize = totalSizeToAlloc( - params.size(), clangTypeInfo.hasValue() ? 1 : 0); + params.size(), clangTypeInfo.empty() ? 0 : 1); void *mem = ctx.Allocate(allocSize, alignof(FunctionType), arena); bool isCanonical = isFunctionTypeCanonical(params, result); - if (clangTypeInfo.hasValue()) { + if (!clangTypeInfo.empty()) { if (ctx.LangOpts.UseClangFunctionTypes) - isCanonical &= clangTypeInfo->type->isCanonicalUnqualified(); + isCanonical &= clangTypeInfo.getType()->isCanonicalUnqualified(); else isCanonical = false; } @@ -3162,8 +3162,8 @@ FunctionType::FunctionType(ArrayRef params, std::uninitialized_copy(params.begin(), params.end(), getTrailingObjects()); auto clangTypeInfo = info.getClangTypeInfo(); - if (clangTypeInfo.hasValue()) - *getTrailingObjects() = clangTypeInfo.getValue(); + if (!clangTypeInfo.empty()) + *getTrailingObjects() = clangTypeInfo; } void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 58a220d54c776..a2eea55434aa4 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3629,7 +3629,7 @@ void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { auto *cml = Ctx.getClangModuleLoader(); SmallString<64> buf; llvm::raw_svector_ostream os(buf); - info.getClangTypeInfo().getValue().printType(cml, os); + info.getClangTypeInfo().printType(cml, os); Printer << ", cType: " << QuotedString(os.str()); } @@ -4054,7 +4054,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; // [TODO: Clang-type-plumbing] Remove the second check. - if (printNameOnly || !info.getClangTypeInfo().hasValue()) + if (printNameOnly || info.getClangTypeInfo().empty()) break; printCType(Ctx, Printer, info); break; @@ -4120,7 +4120,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; // [TODO: Clang-type-plumbing] Remove the second check. - if (printNameOnly || !info.getClangTypeInfo().hasValue()) + if (printNameOnly || info.getClangTypeInfo().empty()) break; printCType(Ctx, Printer, info); break; From eead4ae8f05a91e2a087d027e341712d500f263b Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Tue, 18 Aug 2020 16:18:38 -0700 Subject: [PATCH 083/120] [NFC] Remove unused function parameter. --- include/swift/AST/ASTContext.h | 1 - lib/AST/ASTContext.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index e3ecfbb158dfb..7697baf4d1d01 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -606,7 +606,6 @@ class ASTContext final { const clang::Type * getCanonicalClangFunctionType( ArrayRef params, Optional result, - const SILFunctionType::ExtInfo incompleteExtInfo, SILFunctionType::Representation trueRep); /// Get the Swift declaration that a Clang declaration was exported from, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ce4f9c980bf6f..ca9d3ed0f34e8 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4477,7 +4477,6 @@ const clang::Type * ASTContext::getCanonicalClangFunctionType( ArrayRef params, Optional result, - SILFunctionType::ExtInfo incompleteExtInfo, SILFunctionType::Representation trueRep) { auto *ty = getClangTypeConverter().getFunctionType(params, result, trueRep); return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; From eeec16f14322e96577e21a9e0d2b554fb98dd313 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Wed, 19 Aug 2020 18:06:00 -0700 Subject: [PATCH 084/120] [NFC] Remove redundant ExtInfo parameter for getBridgedFunctionType. At all call-sites, the extInfo passed as the third argument is computed directly from the second argument, so we compute it directly in getBridgedFunctionType. --- include/swift/SIL/TypeLowering.h | 1 - lib/SIL/IR/SILFunctionType.cpp | 13 ++++--------- lib/SIL/IR/TypeLowering.cpp | 2 +- lib/SILGen/SILGenApply.cpp | 1 - lib/SILGen/SILGenBridging.cpp | 3 +-- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index aba919ee0090c..344087f0a1aaf 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -992,7 +992,6 @@ class TypeConverter { /// Given a function type, yield its bridged formal type. CanAnyFunctionType getBridgedFunctionType(AbstractionPattern fnPattern, CanAnyFunctionType fnType, - AnyFunctionType::ExtInfo extInfo, Bridgeability bridging); /// Given a referenced value and the substituted formal type of a diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 2d3e10054313d..794706f5c15b2 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -3984,7 +3984,6 @@ SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, CanAnyFunctionType TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, CanAnyFunctionType t, - AnyFunctionType::ExtInfo extInfo, Bridgeability bridging) { // Pull out the generic signature. CanGenericSignature genericSig = t.getOptGenericSignature(); @@ -3996,12 +3995,8 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, case SILFunctionTypeRepresentation::Closure: case SILFunctionTypeRepresentation::WitnessMethod: { // No bridging needed for native functions. - if (t->getExtInfo().isEqualTo(extInfo, useClangTypes(t))) - return t; - return CanAnyFunctionType::get(genericSig, t.getParams(), t.getResult(), - extInfo); + return t; } - case SILFunctionTypeRepresentation::CFunctionPointer: case SILFunctionTypeRepresentation::Block: case SILFunctionTypeRepresentation::ObjCMethod: { @@ -4016,7 +4011,7 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, suppressOptional); return CanAnyFunctionType::get(genericSig, llvm::makeArrayRef(params), - result, extInfo); + result, t->getExtInfo()); } } llvm_unreachable("bad calling convention"); @@ -4098,7 +4093,6 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, auto bridging = Bridgeability::Full; unsigned numParameterLists = constant.getParameterListCount(); - auto extInfo = fnType->getExtInfo(); // Form an abstraction pattern for bridging purposes. AbstractionPattern bridgingFnPattern = @@ -4108,12 +4102,13 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, // Fast path: no uncurrying required. if (numParameterLists == 1) { auto bridgedFnType = - getBridgedFunctionType(bridgingFnPattern, fnType, extInfo, bridging); + getBridgedFunctionType(bridgingFnPattern, fnType, bridging); bridgingFnPattern.rewriteType(bridgingFnPattern.getGenericSignature(), bridgedFnType); return { bridgingFnPattern, bridgedFnType }; } + auto extInfo = fnType->getExtInfo(); SILFunctionTypeRepresentation rep = extInfo.getSILRepresentation(); assert(rep != SILFunctionType::Representation::Block && "objc blocks cannot be curried"); diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index e1d4417e42dd6..45a725074cb8d 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -1964,7 +1964,7 @@ TypeConverter::computeLoweredRValueType(TypeExpansionContext forExpansion, // Bridge the parameters and result of the function type. auto bridgedFnType = - TC.getBridgedFunctionType(origType, substFnType, extInfo, bridging); + TC.getBridgedFunctionType(origType, substFnType, bridging); substFnType = bridgedFnType; // Also rewrite the type of the abstraction pattern. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index bffecc2c2c187..22d3136ca776c 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -76,7 +76,6 @@ getIndirectApplyAbstractionPattern(SILGenFunction &SGF, // bridged to a foreign type. auto bridgedType = SGF.SGM.Types.getBridgedFunctionType(pattern, fnType, - fnType->getExtInfo(), Bridgeability::Full); pattern.rewriteType(CanGenericSignature(), bridgedType); return pattern; diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 7eedc3b1975fa..0c1bb7d417a89 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -350,8 +350,7 @@ getParameterTypes(AnyFunctionType::CanParamArrayRef params) { static CanAnyFunctionType getBridgedBlockType(SILGenModule &SGM, CanAnyFunctionType blockType) { return SGM.Types.getBridgedFunctionType(AbstractionPattern(blockType), - blockType, blockType->getExtInfo(), - Bridgeability::Full); + blockType, Bridgeability::Full); } static void buildFuncToBlockInvokeBody(SILGenFunction &SGF, From fdbcd1236fd6d94239eb37b07cb729c4eae466aa Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 20 Aug 2020 17:11:48 -0700 Subject: [PATCH 085/120] [NFC] Bridge based on SILFunctionLanguage instead of Representation. --- lib/SIL/IR/SILFunctionType.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 794706f5c15b2..29a40a911d107 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -3988,18 +3988,13 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, // Pull out the generic signature. CanGenericSignature genericSig = t.getOptGenericSignature(); - switch (auto rep = t->getExtInfo().getSILRepresentation()) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::WitnessMethod: { + auto rep = t->getExtInfo().getSILRepresentation(); + switch (getSILFunctionLanguage(rep)) { + case SILFunctionLanguage::Swift: { // No bridging needed for native functions. return t; } - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Block: - case SILFunctionTypeRepresentation::ObjCMethod: { + case SILFunctionLanguage::C: { SmallVector params; getBridgedParams(rep, pattern, t->getParams(), params, bridging); From f86aad9b294819a84aa1493330e07b0675ac90e6 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 20 Aug 2020 18:00:27 -0700 Subject: [PATCH 086/120] [NFC] Extract computation of SILExtInfoBuilder bits into new method. --- include/swift/AST/ExtInfo.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 6b2bc1b8bf7c5..0850870f6ce45 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -505,6 +505,15 @@ class SILExtInfoBuilder { SILExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) : bits(bits), clangTypeInfo(clangTypeInfo) {} + static constexpr unsigned makeBits(Representation rep, bool isPseudogeneric, + bool isNoEscape, bool isAsync, + DifferentiabilityKind diffKind) { + return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | + (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | + (((unsigned)diffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask); + } + public: // Constructor with all defaults. SILExtInfoBuilder() : bits(0), clangTypeInfo(ClangTypeInfo(nullptr)) {} @@ -513,12 +522,9 @@ class SILExtInfoBuilder { SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, bool isAsync, DifferentiabilityKind diffKind, const clang::Type *type) - : SILExtInfoBuilder( - ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | - (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | - (((unsigned)diffKind << DifferentiabilityMaskOffset) & - DifferentiabilityMask), - ClangTypeInfo(type)) {} + : SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape, isAsync, + diffKind), + ClangTypeInfo(type)) {} void checkInvariants() const; From c4ad840ad3dbc40c885af117105a3a92326df149 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Thu, 20 Aug 2020 17:40:41 -0700 Subject: [PATCH 087/120] [NFC] Reuse SILExtInfoBuilder's main constructor in default constructor. --- include/swift/AST/ExtInfo.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 0850870f6ce45..55ea27bc47dc8 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -516,7 +516,7 @@ class SILExtInfoBuilder { public: // Constructor with all defaults. - SILExtInfoBuilder() : bits(0), clangTypeInfo(ClangTypeInfo(nullptr)) {} + SILExtInfoBuilder() : SILExtInfoBuilder(0, ClangTypeInfo(nullptr)) {} // Constructor for polymorphic type. SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, @@ -526,6 +526,12 @@ class SILExtInfoBuilder { diffKind), ClangTypeInfo(type)) {} + SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric) + : SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric, + info.isNoEscape(), info.isAsync(), + info.getDifferentiabilityKind()), + info.getClangTypeInfo()) {} + void checkInvariants() const; /// Check if \c this is well-formed and create an ExtInfo. From 863bca87e8644151d492f5c526fe62d4fee8ef9f Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 27 Aug 2020 12:57:30 -0700 Subject: [PATCH 088/120] [Dependency Scanner] Prefix Clang dependency scanner search path arguments with `-Xcc` Experimentally, this seems to be required for these paths to actually be picked up by the underlying scanner. --- lib/ClangImporter/ClangModuleDependencyScanner.cpp | 6 ++---- test/ScanDependencies/batch_module_scan.swift | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index 290dd69506ed2..82c502b962ff0 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -260,12 +260,10 @@ void ClangImporter::recordModuleDependencies( // be found via search paths. Passing these headers as explicit inputs can // be quite challenging. for (auto &path: Impl.SwiftContext.SearchPathOpts.ImportSearchPaths) { - swiftArgs.push_back("-I"); - swiftArgs.push_back(path); + addClangArg("-I" + path); } for (auto &path: Impl.SwiftContext.SearchPathOpts.FrameworkSearchPaths) { - swiftArgs.push_back(path.IsSystem ? "-Fsystem": "-F"); - swiftArgs.push_back(path.Path); + addClangArg((path.IsSystem ? "-Fsystem": "-F") + path.Path); } // Swift frontend option for input file path (Foo.modulemap). diff --git a/test/ScanDependencies/batch_module_scan.swift b/test/ScanDependencies/batch_module_scan.swift index 4f0c173f7a99f..59189771b340f 100644 --- a/test/ScanDependencies/batch_module_scan.swift +++ b/test/ScanDependencies/batch_module_scan.swift @@ -28,7 +28,7 @@ // CHECK-PCM-NEXT: }, // CHECK-PCM-NEXT: { // CHECK-PCM-NEXT: "modulePath": "F.pcm", -// CHECK-PCM: "-I" +// CHECK-PCM: "-I // CHECK-SWIFT: { // CHECK-SWIFT-NEXT: "mainModuleName": "F", From 4847ec9a6bed06259750b7595871e87678acd554 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 27 Aug 2020 13:18:45 -0700 Subject: [PATCH 089/120] [AST/TypeChecker] Add more asserts to make sure types don't get into AST --- lib/AST/ASTContext.cpp | 2 ++ lib/AST/Expr.cpp | 4 ++-- lib/AST/SILLayout.cpp | 2 ++ lib/AST/Type.cpp | 2 +- lib/AST/TypeCheckRequests.cpp | 1 + lib/Sema/CSApply.cpp | 2 ++ lib/Sema/CSDiagnostics.cpp | 2 ++ lib/Sema/CSGen.cpp | 1 + lib/Sema/TypeCheckCodeCompletion.cpp | 2 ++ 9 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 8a744f7d03d94..b33d17cbd0636 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2811,6 +2811,7 @@ ReferenceStorageType *ReferenceStorageType::get(Type T, ReferenceOwnership ownership, const ASTContext &C) { assert(!T->hasTypeVariable()); // not meaningful in type-checker + assert(!T->hasHole()); switch (optionalityOf(ownership)) { case ReferenceOwnershipOptionality::Disallowed: assert(!T->getOptionalObjectType() && "optional type is disallowed"); @@ -3197,6 +3198,7 @@ GenericFunctionType *GenericFunctionType::get(GenericSignature sig, ExtInfo info) { assert(sig && "no generic signature for generic function type?!"); assert(!result->hasTypeVariable()); + assert(!result->hasHole()); llvm::FoldingSetNodeID id; GenericFunctionType::Profile(id, sig, params, result, info); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 95a290a0d303c..40e632b5a09ca 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -126,7 +126,7 @@ namespace { } // end anonymous namespace void Expr::setType(Type T) { - assert(!T || !T->hasTypeVariable()); + assert(!T || !T->hasTypeVariable() || !T->hasHole()); Ty = T; } @@ -2001,7 +2001,7 @@ bool ClosureExpr::capturesSelfEnablingImplictSelf() const { } void ClosureExpr::setExplicitResultType(Type ty) { - assert(ty && !ty->hasTypeVariable()); + assert(ty && !ty->hasTypeVariable() && !ty->hasHole()); ExplicitResultTypeAndBodyState.getPointer() ->setType(MetatypeType::get(ty)); } diff --git a/lib/AST/SILLayout.cpp b/lib/AST/SILLayout.cpp index da70ccd300917..ffbd35175d226 100644 --- a/lib/AST/SILLayout.cpp +++ b/lib/AST/SILLayout.cpp @@ -52,6 +52,8 @@ static void verifyFields(CanGenericSignature Sig, ArrayRef Fields) { && "SILLayout field cannot have an archetype type"); assert(!ty->hasTypeVariable() && "SILLayout cannot contain constraint system type variables"); + assert(!ty->hasHole() && + "SILLayout cannot contain constraint system type holes"); if (!ty->hasTypeParameter()) continue; field.getLoweredType().findIf([Sig](Type t) -> bool { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 6cc2cc05a01fd..d91dc2b1a8fd8 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -76,7 +76,7 @@ Type QuerySubstitutionMap::operator()(SubstitutableType *type) const { } void TypeLoc::setType(Type Ty) { - assert(!Ty || !Ty->hasTypeVariable()); + assert(!Ty || !Ty->hasTypeVariable() || !Ty->hasHole()); this->Ty = Ty; } diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 82419b737dfb5..b6bf4130e16f3 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1013,6 +1013,7 @@ void InterfaceTypeRequest::cacheResult(Type type) const { auto *decl = std::get<0>(getStorage()); if (type) { assert(!type->hasTypeVariable() && "Type variable in interface type"); + assert(!type->hasHole() && "Type hole in interface type"); assert(!type->is() && "Interface type must be materializable"); assert(!type->hasArchetype() && "Archetype in interface type"); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index b4ec32888c67c..b86a7919376ec 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7673,6 +7673,8 @@ namespace { componentType = solution.simplifyType(cs.getType(kp, i)); assert(!componentType->hasTypeVariable() && "Should not write type variable into key-path component"); + assert(!componentType->hasHole() && + "Should not write type hole into key-path component"); kp->getMutableComponents()[i].setComponentType(componentType); } } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 25b54e13e7538..97ecb96e50aa9 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1235,8 +1235,10 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { assert(!baseType->hasTypeVariable() && "Base type must not be a type variable"); + assert(!baseType->isHole() && "Base type must not be a type hole"); assert(!unwrappedType->hasTypeVariable() && "Unwrapped type must not be a type variable"); + assert(!unwrappedType->isHole() && "Unwrapped type must not be a type hole"); if (!baseType->getOptionalObjectType()) return false; diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index d54105b2c721a..38f0861ed1c91 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3684,6 +3684,7 @@ namespace { if (CG.getConstraintSystem().shouldReusePrecheckedType()) { if (expr->getType()) { assert(!expr->getType()->hasTypeVariable()); + assert(!expr->getType()->hasHole()); CG.getConstraintSystem().cacheType(expr); return { false, expr }; } diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 77a8222774652..6ba92aeb42dd4 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -416,6 +416,8 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, assert(exprType && !exprType->hasTypeVariable() && "free type variable with FreeTypeVariableBinding::GenericParameters?"); + assert(exprType && !exprType->hasHole() && + "type hole with FreeTypeVariableBinding::GenericParameters?"); if (exprType->hasError()) { recoverOriginalType(); From 0bef4a661bdf7c64d4145fb76a852b238381b4ff Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 27 Aug 2020 11:08:52 -0700 Subject: [PATCH 090/120] Give global once symbols stabler manglings. This allows symbol ordering and other analyses to be more robust with regards to these symbols. --- docs/ABI/Mangling.rst | 5 +++ include/swift/AST/ASTMangler.h | 3 +- include/swift/Demangling/DemangleNodes.def | 3 ++ lib/AST/ASTMangler.cpp | 36 +++++++++++-------- lib/Demangling/Demangler.cpp | 26 +++++++++++++- lib/Demangling/NodePrinter.cpp | 25 +++++++++++++ lib/Demangling/OldRemangler.cpp | 9 +++++ lib/Demangling/Remangler.cpp | 17 +++++++++ lib/SILGen/SILGenGlobalVariable.cpp | 16 ++------- test/IRGen/big_types_corner_cases_tiny.swift | 2 +- test/IRGen/globals.swift | 3 +- test/IRGen/lazy_globals.swift | 10 +++--- test/SILGen/default_arguments.swift | 2 +- test/SILGen/fragile_globals.swift | 6 ++-- test/SILGen/global_resilience.swift | 8 ++--- test/SILGen/lazy_globals.swift | 24 ++++++------- test/SILGen/lazy_globals_multiple_vars.swift | 12 +++---- test/SILGen/observers.swift | 2 +- test/SILOptimizer/access_marker_verify.swift | 12 +++---- .../access_marker_verify_objc.swift | 4 +-- test/SILOptimizer/inline_addressor.swift | 4 +-- .../SILOptimizer/large_string_array.swift.gyb | 2 +- 22 files changed, 155 insertions(+), 76 deletions(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index f35339c9ebb70..9e60d04a4f5e3 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -176,6 +176,11 @@ Globals global ::= global 'MJ' // noncanonical specialized generic type metadata instantiation cache associated with global global ::= global 'MN' // noncanonical specialized generic type metadata for global + #if SWIFT_RUNTIME_VERSION >= 5.4 + global ::= context (decl-name '_')+ 'WZ' // global variable one-time initialization function + global ::= context (decl-name '_')+ 'Wz' // global variable one-time initialization token + #endif + A direct symbol resolves directly to the address of an object. An indirect symbol resolves to the address of a pointer to the object. They are distinct manglings to make a certain class of bugs diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 50c401e7ee2bc..b244e766ffc52 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -146,7 +146,8 @@ class ASTMangler : public Mangler { std::string mangleGlobalVariableFull(const VarDecl *decl); - std::string mangleGlobalInit(const VarDecl *decl, int counter, + std::string mangleGlobalInit(const PatternBindingDecl *decl, + unsigned entry, bool isInitFunc); std::string mangleReabstractionThunkHelper(CanSILFunctionType ThunkType, diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 468a0711f90f4..1c3c612f9e98a 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -294,6 +294,9 @@ NODE(CanonicalSpecializedGenericTypeMetadataAccessFunction) NODE(MetadataInstantiationCache) NODE(NoncanonicalSpecializedGenericTypeMetadata) NODE(NoncanonicalSpecializedGenericTypeMetadataCache) +NODE(GlobalVariableOnceFunction) +NODE(GlobalVariableOnceToken) +NODE(GlobalVariableOnceDeclList) #undef CONTEXT_NODE #undef NODE diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index be1c4af4e0ea1..ee1269258f4f2 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -336,21 +336,29 @@ std::string ASTMangler::mangleKeyPathHashHelper(ArrayRef indices, return finalize(); } -std::string ASTMangler::mangleGlobalInit(const VarDecl *decl, int counter, +std::string ASTMangler::mangleGlobalInit(const PatternBindingDecl *pd, + unsigned pbdEntry, bool isInitFunc) { - auto topLevelContext = decl->getDeclContext()->getModuleScopeContext(); - auto fileUnit = cast(topLevelContext); - Identifier discriminator = fileUnit->getDiscriminatorForPrivateValue(decl); - assert(!discriminator.empty()); - assert(!isNonAscii(discriminator.str()) && - "discriminator contains non-ASCII characters"); - assert(!clang::isDigit(discriminator.str().front()) && - "not a valid identifier"); - - Buffer << "globalinit_"; - appendIdentifier(discriminator.str()); - Buffer << (isInitFunc ? "_func" : "_token"); - Buffer << counter; + beginMangling(); + + Pattern *pattern = pd->getPattern(pbdEntry); + bool first = true; + pattern->forEachVariable([&](VarDecl *D) { + if (first) { + appendContextOf(D); + first = false; + } + appendDeclName(D); + appendListSeparator(); + }); + assert(!first && "no variables in pattern binding?!"); + + if (isInitFunc) { + appendOperator("WZ"); + } else { + appendOperator("Wz"); + } + return finalize(); } diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 3628481d8a47e..f49ba90261714 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -2669,7 +2669,7 @@ NodePointer Demangler::demangleSpecAttributes(Node::Kind SpecKind) { } NodePointer Demangler::demangleWitness() { - switch (nextChar()) { + switch (char c = nextChar()) { case 'C': return createWithChild(Node::Kind::EnumCase, popNode(isEntity)); @@ -2811,6 +2811,30 @@ NodePointer Demangler::demangleWitness() { return nullptr; } } + case 'Z': + case 'z': { + auto declList = createNode(Node::Kind::GlobalVariableOnceDeclList); + std::vector vars; + while (auto sig = popNode(Node::Kind::FirstElementMarker)) { + auto identifier = popNode(isDeclName); + if (!identifier) + return nullptr; + vars.push_back(identifier); + } + for (auto i = vars.rbegin(); i != vars.rend(); ++i) { + declList->addChild(*i, *this); + } + + auto context = popContext(); + if (!context) + return nullptr; + Node::Kind kind = c == 'Z' + ? Node::Kind::GlobalVariableOnceFunction + : Node::Kind::GlobalVariableOnceToken; + return createWithChildren(kind, + context, + declList); + } default: return nullptr; } diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index e7ea0e52e29d6..3a7fc824699b2 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -552,6 +552,9 @@ class NodePrinter { case Node::Kind::CanonicalSpecializedGenericTypeMetadataAccessFunction: case Node::Kind::NoncanonicalSpecializedGenericTypeMetadata: case Node::Kind::NoncanonicalSpecializedGenericTypeMetadataCache: + case Node::Kind::GlobalVariableOnceDeclList: + case Node::Kind::GlobalVariableOnceFunction: + case Node::Kind::GlobalVariableOnceToken: return false; } printer_unreachable("bad node kind"); @@ -2466,6 +2469,28 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "cache variable for noncanonical specialized generic type metadata for "; print(Node->getChild(0)); return nullptr; + case Node::Kind::GlobalVariableOnceToken: + case Node::Kind::GlobalVariableOnceFunction: + Printer << (kind == Node::Kind::GlobalVariableOnceToken + ? "one-time initialization token for " + : "one-time initialization function for "); + printContext(Node->getChild(0)); + print(Node->getChild(1)); + return nullptr; + case Node::Kind::GlobalVariableOnceDeclList: + if (Node->getNumChildren() == 1) { + print(Node->getChild(0)); + } else { + Printer << '('; + for (unsigned i = 0, e = Node->getNumChildren(); i < e; ++i) { + if (i != 0) { + Printer << ", "; + } + print(Node->getChild(i)); + } + Printer << ')'; + } + return nullptr; } printer_unreachable("bad node kind!"); } diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index ba7ca2eb51960..7df26cbfad047 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -2139,6 +2139,15 @@ void Remangler::mangleAccessorFunctionReference(Node *node) { void Remangler::mangleMetadataInstantiationCache(Node *node) { unreachable("unsupported"); } +void Remangler::mangleGlobalVariableOnceToken(Node *node) { + unreachable("unsupported"); +} +void Remangler::mangleGlobalVariableOnceFunction(Node *node) { + unreachable("unsupported"); +} +void Remangler::mangleGlobalVariableOnceDeclList(Node *node) { + unreachable("unsupported"); +} void Remangler::mangleCanonicalSpecializedGenericMetaclass(Node *node) { Buffer << "MM"; diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 60dfeafaee925..8abad3f38cf0f 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -2566,6 +2566,23 @@ void Remangler::mangleNoncanonicalSpecializedGenericTypeMetadataCache(Node *node Buffer << "MJ"; } +void Remangler::mangleGlobalVariableOnceToken(Node *node) { + mangleChildNodes(node); + Buffer << "Wz"; +} + +void Remangler::mangleGlobalVariableOnceFunction(Node *node) { + mangleChildNodes(node); + Buffer << "WZ"; +} + +void Remangler::mangleGlobalVariableOnceDeclList(Node *node) { + for (unsigned i = 0, e = node->getNumChildren(); i < e; ++i) { + mangle(node->getChild(i)); + Buffer << '_'; + } +} + } // anonymous namespace /// The top-level interface to the remangler. diff --git a/lib/SILGen/SILGenGlobalVariable.cpp b/lib/SILGen/SILGenGlobalVariable.cpp index 551b438f461f0..a43ce63f99508 100644 --- a/lib/SILGen/SILGenGlobalVariable.cpp +++ b/lib/SILGen/SILGenGlobalVariable.cpp @@ -178,20 +178,8 @@ void SILGenModule::emitGlobalInitialization(PatternBindingDecl *pd, ->areAllParamsConcrete()); } - // Emit the lazy initialization token for the initialization expression. - auto counter = anonymousSymbolCounter++; - - // Pick one variable of the pattern. Usually it's only one variable, but it - // can also be something like: var (a, b) = ... - Pattern *pattern = pd->getPattern(pbdEntry); - VarDecl *varDecl = nullptr; - pattern->forEachVariable([&](VarDecl *D) { - varDecl = D; - }); - assert(varDecl); - Mangle::ASTMangler TokenMangler; - std::string onceTokenBuffer = TokenMangler.mangleGlobalInit(varDecl, counter, + std::string onceTokenBuffer = TokenMangler.mangleGlobalInit(pd, pbdEntry, false); auto onceTy = BuiltinIntegerType::getWordType(M.getASTContext()); @@ -207,7 +195,7 @@ void SILGenModule::emitGlobalInitialization(PatternBindingDecl *pd, // Emit the initialization code into a function. Mangle::ASTMangler FuncMangler; - std::string onceFuncBuffer = FuncMangler.mangleGlobalInit(varDecl, counter, + std::string onceFuncBuffer = FuncMangler.mangleGlobalInit(pd, pbdEntry, true); SILFunction *onceFunc = emitLazyGlobalInitializer(onceFuncBuffer, pd, diff --git a/test/IRGen/big_types_corner_cases_tiny.swift b/test/IRGen/big_types_corner_cases_tiny.swift index 8a9a88b28f9b1..a4fb5a9a6929a 100644 --- a/test/IRGen/big_types_corner_cases_tiny.swift +++ b/test/IRGen/big_types_corner_cases_tiny.swift @@ -4,7 +4,7 @@ // DO NOT ADD ANY MORE CODE TO THIS FILE! -// CHECK-LABEL: define internal void @globalinit +// CHECK-LABEL: define internal void @{{.*}}WZ // CHECK: [[ALLOC:%.*]] = alloca %T27big_types_corner_cases_tiny30LoadableStructWithBiggerStringV // CHECK: call swiftcc void {{.*}}(%T27big_types_corner_cases_tiny30LoadableStructWithBiggerStringV* noalias nocapture sret [[ALLOC]] let model = ClassWithLoadableStructWithBiggerString().f() diff --git a/test/IRGen/globals.swift b/test/IRGen/globals.swift index 509922e9483ed..b423befb38b6d 100644 --- a/test/IRGen/globals.swift +++ b/test/IRGen/globals.swift @@ -53,6 +53,5 @@ extension A { // CHECK: define{{( dllexport)?}}{{( protected)?}} i32 @main(i32 %0, i8** %1) {{.*}} { // CHECK: store i64 {{.*}}, i64* getelementptr inbounds ([[INT]], [[INT]]* @"$s7globals2g0Sivp", i32 0, i32 0), align 8 -// FIXME: give these initializers a real mangled name -// CHECK: define internal void @globalinit_{{.*}}func0() {{.*}} { +// CHECK: define internal void @"{{.*}}WZ"() {{.*}} { // CHECK: store i64 5, i64* getelementptr inbounds (%TSi, %TSi* @"$s7globals1AV3fooSivpZ", i32 0, i32 0), align 8 diff --git a/test/IRGen/lazy_globals.swift b/test/IRGen/lazy_globals.swift index ce0f387d9e485..8b1d761c70e40 100644 --- a/test/IRGen/lazy_globals.swift +++ b/test/IRGen/lazy_globals.swift @@ -2,12 +2,12 @@ // REQUIRES: CPU=x86_64 -// CHECK: @globalinit_[[T:.*]]_token0 = internal global i64 0, align 8 +// CHECK: @"[[T:.*]]Wz" = internal global i64 0, align 8 // CHECK: @"$s12lazy_globals1xSivp" = hidden global %TSi zeroinitializer, align 8 // CHECK: @"$s12lazy_globals1ySivp" = hidden global %TSi zeroinitializer, align 8 // CHECK: @"$s12lazy_globals1zSivp" = hidden global %TSi zeroinitializer, align 8 -// CHECK: define internal void @globalinit_[[T]]_func0() {{.*}} { +// CHECK: define internal void @"[[T]]WZ"() {{.*}} { // CHECK: entry: // CHECK: store i64 1, i64* getelementptr inbounds (%TSi, %TSi* @"$s12lazy_globals1xSivp", i32 0, i32 0), align 8 // CHECK: store i64 2, i64* getelementptr inbounds (%TSi, %TSi* @"$s12lazy_globals1ySivp", i32 0, i32 0), align 8 @@ -17,17 +17,17 @@ // CHECK: define hidden swiftcc i8* @"$s12lazy_globals1xSivau"() {{.*}} { // CHECK: entry: -// CHECK: call void @swift_once(i64* @globalinit_[[T]]_token0, i8* bitcast (void ()* @globalinit_[[T]]_func0 to i8*), i8* undef) +// CHECK: call void @swift_once(i64* @"[[T]]Wz", i8* bitcast (void ()* @"[[T]]WZ" to i8*), i8* undef) // CHECK: } // CHECK: define hidden swiftcc i8* @"$s12lazy_globals1ySivau"() {{.*}} { // CHECK: entry: -// CHECK: call void @swift_once(i64* @globalinit_[[T]]_token0, i8* bitcast (void ()* @globalinit_[[T]]_func0 to i8*), i8* undef) +// CHECK: call void @swift_once(i64* @"[[T]]Wz", i8* bitcast (void ()* @"[[T]]WZ" to i8*), i8* undef) // CHECK: } // CHECK: define hidden swiftcc i8* @"$s12lazy_globals1zSivau"() {{.*}} { // CHECK: entry: -// CHECK: call void @swift_once(i64* @globalinit_[[T]]_token0, i8* bitcast (void ()* @globalinit_[[T]]_func0 to i8*), i8* undef) +// CHECK: call void @swift_once(i64* @"[[T]]Wz", i8* bitcast (void ()* @"[[T]]WZ" to i8*), i8* undef) // CHECK: } var (x, y, z) = (1, 2, 3) diff --git a/test/SILGen/default_arguments.swift b/test/SILGen/default_arguments.swift index 94450cf10b7a9..0895a9a0f018a 100644 --- a/test/SILGen/default_arguments.swift +++ b/test/SILGen/default_arguments.swift @@ -156,7 +156,7 @@ class Foo { return x } - // CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_33_E52D764B1F2009F2390B2B8DF62DAEB8_func0 + // CHECK-LABEL: sil private [global_init_once_fn] [ossa] @{{.*}}WZ // CHECK: string_literal utf8 "Foo" static let x = Foo(int:0) diff --git a/test/SILGen/fragile_globals.swift b/test/SILGen/fragile_globals.swift index 0a4ace7ffa3a1..c6c6e412eece0 100644 --- a/test/SILGen/fragile_globals.swift +++ b/test/SILGen/fragile_globals.swift @@ -11,7 +11,7 @@ var mygg = 29 // Check if we have one token: from mygg. // Initializers from other modules are never fragile. -// CHECK: sil_global private{{.*}} @globalinit_[[T3:.*]]_token0 +// CHECK: sil_global private{{.*}} @[[T3:.*]]Wz //@inlinable public func sum() -> Int { @@ -21,8 +21,8 @@ public func sum() -> Int { // Check if all the addressors are inlined. // CHECK-LABEL: sil {{.*}}@$s15fragile_globals3sumSiyF -// CHECK-DAG: global_addr @globalinit_[[T1:.*]]_token0 -// CHECK-DAG: function_ref @globalinit_[[T1]]_func0 +// CHECK-DAG: global_addr @[[T1:.*]]Wz +// CHECK-DAG: function_ref @[[T1]]WZ // CHECK-DAG: global_addr @$s15fragile_globals4myggSivp // CHECK-DAG: function_ref @$s7ModuleA2ggSivau // CHECK-DAG: function_ref @$s7ModuleB2ggSivau diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index ee01a547e3b4e..2f5ea45c549e8 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -43,21 +43,21 @@ public var myEmptyGlobal = MyEmptyStruct() // Mutable addressor for fixed-layout global -// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_{{.*}}_func1 +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @{{.*}}WZ // CHECK: alloc_global @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVv // CHECK: return // CHECK-LABEL: sil [global_init] [ossa] @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVvau -// CHECK: function_ref @globalinit_{{.*}}_func1 +// CHECK: function_ref @{{.*}}WZ // CHECK: global_addr @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVv // CHECK: return -// CHECK-OPT-LABEL: sil private [global_init_once_fn] @globalinit_{{.*}}_func1 +// CHECK-OPT-LABEL: sil private [global_init_once_fn] @{{.*}}WZ // CHECK-OPT: alloc_global @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVv // CHECK-OPT: return // CHECK-OPT-LABEL: sil [global_init] @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVvau -// CHECK-OPT: function_ref @globalinit_{{.*}}_func1 +// CHECK-OPT: function_ref @{{.*}}WZ // CHECK-OPT: global_addr @$s17global_resilience19myFixedLayoutGlobalAA13MyEmptyStructVvp // CHECK-OPT: return diff --git a/test/SILGen/lazy_globals.swift b/test/SILGen/lazy_globals.swift index c772aa7a2bd27..c5604dc5a159a 100644 --- a/test/SILGen/lazy_globals.swift +++ b/test/SILGen/lazy_globals.swift @@ -1,14 +1,14 @@ // RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s -// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T:.*]]_func0 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @[[T:.*]]WZ : $@convention(c) () -> () { // CHECK: alloc_global @$s12lazy_globals1xSiv // CHECK: [[XADDR:%.*]] = global_addr @$s12lazy_globals1xSivp : $*Int // CHECK: store {{%.*}} to [trivial] [[XADDR]] : $*Int // CHECK: sil hidden [global_init] [ossa] @$s12lazy_globals1xSivau : $@convention(thin) () -> Builtin.RawPointer { -// CHECK: [[TOKEN_ADDR:%.*]] = global_addr @globalinit_[[T]]_token0 : $*Builtin.Word +// CHECK: [[TOKEN_ADDR:%.*]] = global_addr @[[T]]Wz : $*Builtin.Word // CHECK: [[TOKEN_PTR:%.*]] = address_to_pointer [[TOKEN_ADDR]] : $*Builtin.Word to $Builtin.RawPointer -// CHECK: [[INIT_FUNC:%.*]] = function_ref @globalinit_[[T]]_func0 : $@convention(c) () -> () +// CHECK: [[INIT_FUNC:%.*]] = function_ref @[[T]]WZ : $@convention(c) () -> () // CHECK: builtin "once"([[TOKEN_PTR]] : $Builtin.RawPointer, [[INIT_FUNC]] : $@convention(c) () -> ()) : $() // CHECK: [[GLOBAL_ADDR:%.*]] = global_addr @$s12lazy_globals1xSivp : $*Int // CHECK: [[GLOBAL_PTR:%.*]] = address_to_pointer [[GLOBAL_ADDR]] : $*Int to $Builtin.RawPointer @@ -16,7 +16,7 @@ // CHECK: } var x: Int = 0 -// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T:.*]]_func1 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @[[T:.*]]WZ : $@convention(c) () -> () { // CHECK: alloc_global @$s12lazy_globals3FooV3fooSivpZ // CHECK: [[XADDR:%.*]] = global_addr @$s12lazy_globals3FooV3fooSivpZ : $*Int // CHECK: store {{.*}} to [trivial] [[XADDR]] : $*Int @@ -24,9 +24,9 @@ var x: Int = 0 struct Foo { // CHECK: sil hidden [global_init] [ossa] @$s12lazy_globals3FooV3fooSivau : $@convention(thin) () -> Builtin.RawPointer { -// CHECK: [[TOKEN_ADDR:%.*]] = global_addr @globalinit_[[T]]_token1 : $*Builtin.Word +// CHECK: [[TOKEN_ADDR:%.*]] = global_addr @[[T]]Wz : $*Builtin.Word // CHECK: [[TOKEN_PTR:%.*]] = address_to_pointer [[TOKEN_ADDR]] : $*Builtin.Word to $Builtin.RawPointer -// CHECK: [[INIT_FUNC:%.*]] = function_ref @globalinit_[[T]]_func1 : $@convention(c) () -> () +// CHECK: [[INIT_FUNC:%.*]] = function_ref @[[T]]WZ : $@convention(c) () -> () // CHECK: builtin "once"([[TOKEN_PTR]] : $Builtin.RawPointer, [[INIT_FUNC]] : $@convention(c) () -> ()) : $() // CHECK: [[GLOBAL_ADDR:%.*]] = global_addr @$s12lazy_globals3FooV3fooSivpZ : $*Int // CHECK: [[GLOBAL_PTR:%.*]] = address_to_pointer [[GLOBAL_ADDR]] : $*Int to $Builtin.RawPointer @@ -40,7 +40,7 @@ struct Foo { static var initialized: Int = 57 } -// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T:.*]]_func3 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @[[T:.*3bar.*]]WZ : $@convention(c) () -> () { // CHECK: alloc_global @$s12lazy_globals3BarO3barSivpZ // CHECK: [[XADDR:%.*]] = global_addr @$s12lazy_globals3BarO3barSivpZ : $*Int // CHECK: store {{.*}} to [trivial] [[XADDR]] : $*Int @@ -48,9 +48,9 @@ struct Foo { enum Bar { // CHECK: sil hidden [global_init] [ossa] @$s12lazy_globals3BarO3barSivau : $@convention(thin) () -> Builtin.RawPointer { -// CHECK: [[TOKEN_ADDR:%.*]] = global_addr @globalinit_[[T]]_token3 : $*Builtin.Word +// CHECK: [[TOKEN_ADDR:%.*]] = global_addr @[[T]]Wz : $*Builtin.Word // CHECK: [[TOKEN_PTR:%.*]] = address_to_pointer [[TOKEN_ADDR]] : $*Builtin.Word to $Builtin.RawPointer -// CHECK: [[INIT_FUNC:%.*]] = function_ref @globalinit_[[T]]_func3 : $@convention(c) () -> () +// CHECK: [[INIT_FUNC:%.*]] = function_ref @[[T]]WZ : $@convention(c) () -> () // CHECK: builtin "once"([[TOKEN_PTR]] : $Builtin.RawPointer, [[INIT_FUNC]] : $@convention(c) () -> ()) : $() // CHECK: [[GLOBAL_ADDR:%.*]] = global_addr @$s12lazy_globals3BarO3barSivpZ : $*Int // CHECK: [[GLOBAL_PTR:%.*]] = address_to_pointer [[GLOBAL_ADDR]] : $*Int to $Builtin.RawPointer @@ -63,13 +63,13 @@ enum Bar { func f() -> (Int, Int) { return (1, 2) } -// CHECK: sil private [global_init_once_fn] [ossa] @globalinit_[[T]]_func4 : $@convention(c) () -> () { +// CHECK: sil private [global_init_once_fn] [ossa] @[[T:.*2a1.*2b1.*]]WZ : $@convention(c) () -> () { // CHECK: function_ref @$s12lazy_globals1fSi_SityF : $@convention(thin) () -> (Int, Int) // CHECK: sil hidden [global_init] [ossa] @$s12lazy_globals2a1Sivau : $@convention(thin) () -> Builtin.RawPointer -// CHECK: function_ref @globalinit_[[T]]_func4 : $@convention(c) () -> () +// CHECK: function_ref @[[T]]WZ : $@convention(c) () -> () // CHECK: global_addr @$s12lazy_globals2a1Sivp : $*Int // CHECK: sil hidden [global_init] [ossa] @$s12lazy_globals2b1Sivau : $@convention(thin) () -> Builtin.RawPointer { -// CHECK: function_ref @globalinit_[[T]]_func4 : $@convention(c) () -> () +// CHECK: function_ref @[[T]]WZ : $@convention(c) () -> () // CHECK: global_addr @$s12lazy_globals2b1Sivp : $*Int var (a1, b1) = f() diff --git a/test/SILGen/lazy_globals_multiple_vars.swift b/test/SILGen/lazy_globals_multiple_vars.swift index bad5e0b41e1cc..059cfa8267cbf 100644 --- a/test/SILGen/lazy_globals_multiple_vars.swift +++ b/test/SILGen/lazy_globals_multiple_vars.swift @@ -1,34 +1,34 @@ // RUN: %target-swift-emit-silgen -parse-as-library %s | %FileCheck %s -// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_A_B:@globalinit_.*]] : +// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_A_B:@.*1a.*1b.*WZ]] : // CHECK: alloc_global @$s26lazy_globals_multiple_vars1aSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1aSiv // CHECK: alloc_global @$s26lazy_globals_multiple_vars1bSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1bSiv // CHECK: sil hidden [global_init] [ossa] @$s26lazy_globals_multiple_vars1aSivau -// CHECK: global_addr [[TOKEN_A_B:@globalinit_.*]] : +// CHECK: global_addr [[TOKEN_A_B:@.*1a.*1b.*Wz]] : // CHECK: function_ref [[INIT_A_B]] // CHECK: sil hidden [global_init] [ossa] @$s26lazy_globals_multiple_vars1bSivau // CHECK: global_addr [[TOKEN_A_B]] // CHECK: function_ref [[INIT_A_B]] var (a, b) = (1, 2) -// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_C:@globalinit_.*]] : +// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_C:@.*1c.*WZ]] : // CHECK-NOT: global_addr @$s26lazy_globals_multiple_vars1dSiv // CHECK: alloc_global @$s26lazy_globals_multiple_vars1cSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1cSiv // CHECK-NOT: global_addr @$s26lazy_globals_multiple_vars1dSiv // CHECK: sil hidden [global_init] [ossa] @$s26lazy_globals_multiple_vars1cSivau -// CHECK: global_addr [[TOKEN_C:@globalinit_.*]] : +// CHECK: global_addr [[TOKEN_C:@.*1c.*Wz]] : // CHECK: function_ref [[INIT_C]] -// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_D:@globalinit_.*]] : +// CHECK: sil private [global_init_once_fn] [ossa] [[INIT_D:@.*1d.*WZ]] : // CHECK-NOT: global_addr @$s26lazy_globals_multiple_vars1cSiv // CHECK: alloc_global @$s26lazy_globals_multiple_vars1dSiv // CHECK: global_addr @$s26lazy_globals_multiple_vars1dSiv // CHECK-NOT: global_addr @$s26lazy_globals_multiple_vars1cSiv // CHECK: sil hidden [global_init] [ossa] @$s26lazy_globals_multiple_vars1dSivau // CHECK-NOT: global_addr [[TOKEN_C]] -// CHECK: global_addr [[TOKEN_D:@globalinit_.*]] : +// CHECK: global_addr [[TOKEN_D:@.*1d.*Wz]] : // CHECK-NOT: global_addr [[TOKEN_C]] // CHECK: function_ref [[INIT_D]] var c = 1, d = 2 diff --git a/test/SILGen/observers.swift b/test/SILGen/observers.swift index 22ac63b2a603e..6b6c6d4503fa1 100644 --- a/test/SILGen/observers.swift +++ b/test/SILGen/observers.swift @@ -171,7 +171,7 @@ public struct DidSetWillSetTests { var global_observing_property : Int = zero { // The variable is initialized with "zero". - // CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_{{.*}}_func1 : $@convention(c) () -> () { + // CHECK-LABEL: sil private [global_init_once_fn] [ossa] @{{.*}}WZ : $@convention(c) () -> () { // CHECK: bb0: // CHECK-NEXT: alloc_global @$s9observers25global_observing_propertySiv // CHECK-NEXT: %1 = global_addr @$s9observers25global_observing_propertySivp : $*Int diff --git a/test/SILOptimizer/access_marker_verify.swift b/test/SILOptimizer/access_marker_verify.swift index af14c54be2fcf..6062f4116579f 100644 --- a/test/SILOptimizer/access_marker_verify.swift +++ b/test/SILOptimizer/access_marker_verify.swift @@ -627,17 +627,17 @@ func testShims() -> UInt32 { // --- global variable initialization. var globalString1 = "⓪" // start non-empty -// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_33_{{.*}}_func0 : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @{{.*}}WZ : $@convention(c) () -> () { // CHECK: alloc_global @$s20access_marker_verify13globalString1SSvp // CHECK: [[GA:%.*]] = global_addr @$s20access_marker_verify13globalString1SSvp : $*String // CHECK: apply // CHECK: [[ACCESS:%.*]] = begin_access [modify] [unsafe] [[GA]] : $*String // CHECK: store %{{.*}} to [init] [[ACCESS]] : $*String // CHECK: end_access -// CHECK-LABEL: } // end sil function 'globalinit_33_180BF7B9126DB0C8C6C26F15ACD01908_func0' +// CHECK-LABEL: } // end sil function '{{.*}}WZ' var globalString2 = globalString1 -// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit_33_180BF7B9126DB0C8C6C26F15ACD01908_func1 : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @{{.*}}WZ : $@convention(c) () -> () { // CHECK: alloc_global @$s20access_marker_verify13globalString2SSvp // CHECK: [[GA:%.*]] = global_addr @$s20access_marker_verify13globalString2SSvp : $*String // CHECK: apply @@ -648,7 +648,7 @@ var globalString2 = globalString1 // CHECK: end_access [[INIT]] : $*String // CHECK: end_access [[ACCESS]] : $*String // CHECK-NOT: end_access -// CHECK-LABEL: } // end sil function 'globalinit_33_180BF7B9126DB0C8C6C26F15ACD01908_func1' +// CHECK-LABEL: } // end sil function '{{.*}}WZ' // --- getter. @@ -1037,13 +1037,13 @@ func testPointerInit(x: Int, y: UnsafeMutablePointer) { class testInitExistentialGlobal { static var testProperty: P = StructP() } -// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @globalinit{{.*}} : $@convention(c) () -> () { +// CHECK-LABEL: sil private [global_init_once_fn] [ossa] @{{.*}}WZ : $@convention(c) () -> () { // CHECK: alloc_global @$s20access_marker_verify25testInitExistentialGlobalC0D8PropertyAA1P_pvpZ // CHECK: [[GADR:%.*]] = global_addr @$s20access_marker_verify25testInitExistentialGlobalC0D8PropertyAA1P_pvpZ : $*P // CHECK: %{{.*}} = apply %{{.*}}({{.*}}) : $@convention(method) (@thin StructP.Type) -> StructP // CHECK: [[EADR:%.*]] = init_existential_addr [[GADR]] : $*P, $StructP // CHECK: store %{{.*}} to [trivial] [[EADR]] : $*StructP -// CHECK-LABEL: } // end sil function 'globalinit +// CHECK-LABEL: } // end sil function '{{.*}}WZ public enum SomeError: Swift.Error { case error diff --git a/test/SILOptimizer/access_marker_verify_objc.swift b/test/SILOptimizer/access_marker_verify_objc.swift index b9fc5fbaa7c0e..c3e1ed48efe35 100644 --- a/test/SILOptimizer/access_marker_verify_objc.swift +++ b/test/SILOptimizer/access_marker_verify_objc.swift @@ -12,14 +12,14 @@ import Foundation // --- initializer `let` of CFString. // The verifier should ignore this. -// CHECK_LABEL: sil private @globalinit{{.*}} : $@convention(c) () -> () { +// CHECK_LABEL: sil private @{{.*}}WZ : $@convention(c) () -> () { // CHECK: bb0: // CHECK: alloc_global @$s25access_marker_verify_objc12testCFStringC8cfStringSo0F3RefavpZ // CHECK: [[GA:%.*]] = global_addr @$s25access_marker_verify_objc12testCFStringC8cfStringSo0F3RefavpZ : $*CFString // CHECK-NOT: begin_access // CHECK: store %{{.*}} to [init] [[GA]] : $*CFString // CHECK: return %{{.*}} : $() -// CHECK-LABEL: } // end sil function 'globalinit{{.*}}' +// CHECK-LABEL: } // end sil function '{{.*}}WZ' class testCFString { public static let cfString: CFString = "" as CFString } diff --git a/test/SILOptimizer/inline_addressor.swift b/test/SILOptimizer/inline_addressor.swift index c78e0a25c5f41..369799a725370 100644 --- a/test/SILOptimizer/inline_addressor.swift +++ b/test/SILOptimizer/inline_addressor.swift @@ -11,10 +11,10 @@ var totalsum = nonTrivialInit(true) //CHECK-LABEL: sil {{.*}}testit //CHECK: {{^bb0}} -//CHECK: globalinit_ +//CHECK: WZ //CHECK-NOT: {{^bb0}} //CHECK: {{^bb1}} -//CHECK-NOT: globalinit +//CHECK-NOT: WZ //CHECK-NOT: totalsum //CHECK-NOT: inputval //CHECK: {{^}$}} diff --git a/validation-test/SILOptimizer/large_string_array.swift.gyb b/validation-test/SILOptimizer/large_string_array.swift.gyb index 6285e4b97c75b..6b6093f78552b 100644 --- a/validation-test/SILOptimizer/large_string_array.swift.gyb +++ b/validation-test/SILOptimizer/large_string_array.swift.gyb @@ -15,7 +15,7 @@ // Check if the optimizer can optimize the whole array into a statically // initialized global -// CHECK: sil_global private @globalinit_{{.*}} : $_ContiguousArrayStorage = { +// CHECK: sil_global private @{{.*9str_array.*}}WZTv_ : $_ContiguousArrayStorage = { // CHECK: %initval = object $_ContiguousArrayStorage public let str_array: [String] = [ From 4b921c3487d3804c14718c374242619ff44c65fd Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Sun, 23 Aug 2020 16:10:32 -0300 Subject: [PATCH 091/120] [Sema] Improve diagnostics for key path root type inferred as option accessing wrapped member --- include/swift/AST/DiagnosticsSema.def | 9 +++++++++ lib/Sema/CSDiagnostics.cpp | 16 +++++++++++++--- localization/diagnostics/en.yaml | 9 +++++++++ test/expr/unary/keypath/keypath.swift | 9 ++++++--- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index beaf7b6ea3dd5..90bc5604344ae 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1082,6 +1082,9 @@ NOTE(unwrap_with_guard,none, ERROR(optional_base_not_unwrapped,none, "value of optional type %0 must be unwrapped to refer to member %1 of " "wrapped base type %2", (Type, DeclNameRef, Type)) +ERROR(invalid_optional_infered_keypath_root, none, + "key path root inferred as optional type %0 must be unwrapped to refer to member %1 " + "of unwrapped type %2", (Type, DeclNameRef, Type)) NOTE(optional_base_chain,none, "chain the optional using '?' to access member %0 only for non-'nil' " "base values", (DeclNameRef)) @@ -1089,6 +1092,12 @@ NOTE(optional_base_remove_optional_for_keypath_root, none, "use unwrapped type %0 as key path root", (Type)) NOTE(optional_keypath_application_base, none, "use '?' to access key path subscript only for non-'nil' base values", ()) +NOTE(optional_key_path_root_base_chain, none, + "chain the optional using '?.' to access unwrapped type member %0", + (DeclNameRef)) +NOTE(optional_key_path_root_base_unwrap, none, + "unwrap the optional using '!.' to access unwrapped type member %0", + (DeclNameRef)) ERROR(missing_unwrap_optional_try,none, "value of optional type %0 not unwrapped; did you mean to use 'try!' " diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 4bef05bd9c4eb..ed980fef21cb6 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1071,9 +1071,6 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() { return false; auto sourceRange = getSourceRange(); - - emitDiagnostic(diag::optional_base_not_unwrapped, - baseType, Member, unwrappedBaseType); auto componentPathElt = locator->getLastElementAs(); @@ -1082,12 +1079,25 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() { // let's emit a tailored note suggesting to use its unwrapped type. auto *keyPathExpr = castToExpr(getAnchor()); if (auto rootType = keyPathExpr->getRootType()) { + emitDiagnostic(diag::optional_base_not_unwrapped, baseType, Member, + unwrappedBaseType); + emitDiagnostic(diag::optional_base_remove_optional_for_keypath_root, unwrappedBaseType) .fixItReplace(rootType->getSourceRange(), unwrappedBaseType.getString()); + } else { + emitDiagnostic(diag::invalid_optional_infered_keypath_root, baseType, + Member, unwrappedBaseType); + emitDiagnostic(diag::optional_key_path_root_base_chain, Member) + .fixItInsert(sourceRange.End, "?."); + emitDiagnostic(diag::optional_key_path_root_base_unwrap, Member) + .fixItInsert(sourceRange.End, "!."); } } else { + emitDiagnostic(diag::optional_base_not_unwrapped, baseType, Member, + unwrappedBaseType); + // FIXME: It would be nice to immediately offer "base?.member ?? defaultValue" // for non-optional results where that would be appropriate. For the moment // always offering "?" means that if the user chooses chaining, we'll end up diff --git a/localization/diagnostics/en.yaml b/localization/diagnostics/en.yaml index b346b96b115cc..0adbcc174f476 100644 --- a/localization/diagnostics/en.yaml +++ b/localization/diagnostics/en.yaml @@ -3136,6 +3136,9 @@ - id: optional_base_not_unwrapped msg: "value of optional type %0 must be unwrapped to refer to member %1 of wrapped base type %2" +- id: invalid_optional_infered_keypath_root + msg: "key path root inferred as optional type %0 must be unwrapped to refer to member %1 of unwrapped type %2" + - id: optional_base_chain msg: "chain the optional using '?' to access member %0 only for non-'nil' base values" @@ -3145,6 +3148,12 @@ - id: optional_keypath_application_base msg: "use '?' to access key path subscript only for non-'nil' base values" +- id: optional_key_path_root_base_chain + msg: "chain the optional using '?.' to access unwrapped type member %0" + +- id: optional_key_path_root_base_unwrap + msg: "unwrap the optional using '!.' to access unwrapped type member %0" + - id: missing_unwrap_optional_try msg: "value of optional type %0 not unwrapped; did you mean to use 'try!' or chain with '?'?" diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 9b0333b0c0de4..326e148b5b6a7 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -1006,9 +1006,12 @@ func testMemberAccessOnOptionalKeyPathComponent() { // expected-error@-1 {{value of optional type '(Int, Int)?' must be unwrapped to refer to member '0' of wrapped base type '(Int, Int)'}} // expected-note@-2 {{use unwrapped type '(Int, Int)' as key path root}}{{4-15=(Int, Int)}} - // TODO(diagnostics) Improve diagnostics refering to key path root not able to be infered as an optional type. - SR5688_KP(\.count) - // expected-error@-1 {{value of optional type 'String?' must be unwrapped to refer to member 'count' of wrapped base type 'String'}} + SR5688_KP(\.count) // expected-error {{key path root inferred as optional type 'String?' must be unwrapped to refer to member 'count' of unwrapped type 'String'}} + // expected-note@-1 {{chain the optional using '?.' to access unwrapped type member 'count'}} {{15-15=?.}} + // expected-note@-2 {{unwrap the optional using '!.' to access unwrapped type member 'count'}} {{15-15=!.}} + let _ : KeyPath = \.count // expected-error {{key path root inferred as optional type 'String?' must be unwrapped to refer to member 'count' of unwrapped type 'String'}} + // expected-note@-1 {{chain the optional using '?.' to access unwrapped type member 'count'}} {{37-37=?.}} + // expected-note@-2 {{unwrap the optional using '!.' to access unwrapped type member 'count'}} {{37-37=!.}} } func testSyntaxErrors() { From 4c7ccf5abe79563907bbfc0cd80899f0a3287476 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 25 Aug 2020 15:12:50 -0700 Subject: [PATCH 092/120] [NFC] Clean Up FrontendTool Try to impose a simple structure that splits performing actions from the pre and post-pipeline conditions. Wherever actions would take more than a simple return, split them into functions. Refine functions that perform effects to return status codes when they fail. Finally, delineate functions that need semantic analysis from those that do not. Overall this should be NFC. --- include/swift/Frontend/Frontend.h | 2 +- lib/Frontend/Frontend.cpp | 3 +- lib/FrontendTool/FrontendTool.cpp | 356 ++++++++++++++------------ lib/FrontendTool/ImportedModules.cpp | 4 +- lib/FrontendTool/ImportedModules.h | 3 +- lib/FrontendTool/ScanDependencies.cpp | 5 +- lib/FrontendTool/ScanDependencies.h | 3 +- 7 files changed, 204 insertions(+), 172 deletions(-) diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index f5f75f13e7694..39ab041dbcc1b 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -621,7 +621,7 @@ class CompilerInstance { /// /// This is similar to a parse-only invocation, but module imports will also /// be processed. - void performParseAndResolveImportsOnly(); + bool performParseAndResolveImportsOnly(); /// Performs mandatory, diagnostic, and optimization passes over the SIL. /// \param silModule The SIL module that was generated during SILGen. diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 0d6263b2e544c..688acbf0b29dd 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -848,7 +848,7 @@ void CompilerInstance::setMainModule(ModuleDecl *newMod) { Context->addLoadedModule(newMod); } -void CompilerInstance::performParseAndResolveImportsOnly() { +bool CompilerInstance::performParseAndResolveImportsOnly() { FrontendStatsTracer tracer(getStatsReporter(), "parse-and-resolve-imports"); // Resolve imports for all the source files. @@ -867,6 +867,7 @@ void CompilerInstance::performParseAndResolveImportsOnly() { mainModule->setHasResolvedImports(); bindExtensions(*mainModule); + return Context->hadError(); } void CompilerInstance::performSema() { diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 4221b53394e5f..743205b9ccb44 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1229,7 +1229,7 @@ static void verifyGenericSignaturesIfNeeded(const CompilerInvocation &Invocation GenericSignatureBuilder::verifyGenericSignaturesInModule(module); } -static void dumpAndPrintScopeMap(const CompilerInstance &Instance, +static bool dumpAndPrintScopeMap(const CompilerInstance &Instance, SourceFile *SF) { // Not const because may require reexpansion ASTScope &scope = SF->getScope(); @@ -1239,13 +1239,14 @@ static void dumpAndPrintScopeMap(const CompilerInstance &Instance, llvm::errs() << "***Complete scope map***\n"; scope.buildFullyExpandedTree(); scope.print(llvm::errs()); - return; + return Instance.getASTContext().hadError(); } // Probe each of the locations, and dump what we find. for (auto lineColumn : opts.DumpScopeMapLocations) { scope.buildFullyExpandedTree(); scope.dumpOneScopeMapLocation(lineColumn); } + return Instance.getASTContext().hadError(); } static SourceFile * @@ -1260,7 +1261,7 @@ getPrimaryOrMainSourceFile(const CompilerInstance &Instance) { /// Dumps the AST of all available primary source files. If corresponding output /// files were specified, use them; otherwise, dump the AST to stdout. -static void dumpAST(CompilerInstance &Instance) { +static bool dumpAST(CompilerInstance &Instance) { auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { @@ -1275,55 +1276,7 @@ static void dumpAST(CompilerInstance &Instance) { auto *SF = getPrimaryOrMainSourceFile(Instance); SF->dump(llvm::outs(), /*parseIfNeeded*/ true); } -} - -/// We may have been told to dump the AST (either after parsing or -/// type-checking, which is already differentiated in -/// CompilerInstance::performSema()), so dump or print the main source file and -/// return. - -static Optional dumpASTIfNeeded(CompilerInstance &Instance) { - const auto &opts = Instance.getInvocation().getFrontendOptions(); - const FrontendOptions::ActionType Action = opts.RequestedAction; - ASTContext &Context = Instance.getASTContext(); - switch (Action) { - default: - return None; - - case FrontendOptions::ActionType::PrintAST: - getPrimaryOrMainSourceFile(Instance) - ->print(llvm::outs(), PrintOptions::printEverything()); - break; - - case FrontendOptions::ActionType::DumpScopeMaps: - dumpAndPrintScopeMap(Instance, getPrimaryOrMainSourceFile(Instance)); - break; - - case FrontendOptions::ActionType::DumpTypeRefinementContexts: - getPrimaryOrMainSourceFile(Instance) - ->getTypeRefinementContext() - ->dump(llvm::errs(), Context.SourceMgr); - break; - - case FrontendOptions::ActionType::DumpInterfaceHash: - getPrimaryOrMainSourceFile(Instance)->dumpInterfaceHash(llvm::errs()); - break; - - case FrontendOptions::ActionType::EmitSyntax: - emitSyntax(getPrimaryOrMainSourceFile(Instance), - opts.InputsAndOutputs.getSingleOutputFilename()); - break; - - case FrontendOptions::ActionType::DumpParse: - case FrontendOptions::ActionType::DumpAST: - dumpAST(Instance); - break; - - case FrontendOptions::ActionType::EmitImportedModules: - emitImportedModules(Context, Instance.getMainModule(), opts); - break; - } - return Context.hadError(); + return Instance.getASTContext().hadError(); } static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( @@ -1747,40 +1700,187 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { emitCompiledSourceForAllPrimaryInputsIfNeeded(Instance); } -/// Performs the compile requested by the user. -/// \param Instance Will be reset after performIRGeneration when the verifier -/// mode is NoVerify and there were no errors. -/// \returns true on error -static bool performCompile(CompilerInvocation &Invok, - CompilerInstance &Instance, - ArrayRef Args, - int &ReturnValue, - FrontendObserver *observer) { +static bool printSwiftVersion(const CompilerInvocation &Invocation) { + llvm::outs() << version::getSwiftFullVersion( + version::Version::getCurrentLanguageVersion()) + << '\n'; + llvm::outs() << "Target: " << Invocation.getLangOptions().Target.str() + << '\n'; + return false; +} + +static bool +withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer, + llvm::function_ref cont) { const auto &Invocation = Instance.getInvocation(); const auto &opts = Invocation.getFrontendOptions(); - const FrontendOptions::ActionType Action = opts.RequestedAction; + assert(!FrontendOptions::shouldActionOnlyParse(opts.RequestedAction) && + "Action may only parse, but has requested semantic analysis!"); + + Instance.performSema(); + if (observer) + observer->performedSemanticAnalysis(Instance); + + switch (opts.CrashMode) { + case FrontendOptions::DebugCrashMode::AssertAfterParse: + debugFailWithAssertion(); + return true; + case FrontendOptions::DebugCrashMode::CrashAfterParse: + debugFailWithCrash(); + return true; + case FrontendOptions::DebugCrashMode::None: + break; + } + + (void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance); + + if (Instance.getASTContext().hadError()) + return true; + + return cont(Instance); +} + +static bool performScanDependencies(CompilerInstance &Instance) { + auto batchScanInput = + Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; + if (batchScanInput.empty()) { + return scanDependencies(Instance); + } else { + return batchScanModuleDependencies(Instance, batchScanInput); + } +} + +static bool performParseOnly(ModuleDecl &MainModule) { + // A -parse invocation only cares about the side effects of parsing, so + // force the parsing of all the source files. + for (auto *file : MainModule.getFiles()) { + if (auto *SF = dyn_cast(file)) + (void)SF->getTopLevelDecls(); + } + return MainModule.getASTContext().hadError(); +} + +static bool performAction(CompilerInstance &Instance, + int &ReturnValue, + FrontendObserver *observer) { + const auto &opts = Instance.getInvocation().getFrontendOptions(); + auto &Context = Instance.getASTContext(); + switch (Instance.getInvocation().getFrontendOptions().RequestedAction) { + // MARK: Trivial Actions + case FrontendOptions::ActionType::NoneAction: + return Context.hadError(); + case FrontendOptions::ActionType::PrintVersion: + return printSwiftVersion(Instance.getInvocation()); + case FrontendOptions::ActionType::REPL: + llvm::report_fatal_error("Compiler-internal integrated REPL has been " + "removed; use the LLDB-enhanced REPL instead."); + // MARK: Actions for Clang and Clang Modules // We've been asked to precompile a bridging header or module; we want to // avoid touching any other inputs and just parse, emit and exit. - if (Action == FrontendOptions::ActionType::EmitPCH) + case FrontendOptions::ActionType::EmitPCH: return precompileBridgingHeader(Instance); - if (Action == FrontendOptions::ActionType::EmitPCM) + case FrontendOptions::ActionType::EmitPCM: return precompileClangModule(Instance); - if (Action == FrontendOptions::ActionType::DumpPCM) + case FrontendOptions::ActionType::DumpPCM: return dumpPrecompiledClangModule(Instance); - if (Action == FrontendOptions::ActionType::PrintVersion) { - llvm::outs() << version::getSwiftFullVersion( - version::Version::getCurrentLanguageVersion()) << '\n'; - llvm::outs() << "Target: " - << Invocation.getLangOptions().Target.str() << '\n'; - return false; - } - if (Action == FrontendOptions::ActionType::CompileModuleFromInterface || - Action == FrontendOptions::ActionType::TypecheckModuleFromInterface) + + // MARK: Module Interface Actions + case FrontendOptions::ActionType::CompileModuleFromInterface: + case FrontendOptions::ActionType::TypecheckModuleFromInterface: return buildModuleFromInterface(Instance); - if (Invocation.getInputKind() == InputFileKind::LLVM) - return compileLLVMIR(Instance); + // MARK: Actions that Dump + case FrontendOptions::ActionType::DumpParse: { + return dumpAST(Instance); + } + case FrontendOptions::ActionType::DumpAST: + return withSemanticAnalysis( + Instance, observer, + [](CompilerInstance &Instance) { return dumpAST(Instance); }); + case FrontendOptions::ActionType::PrintAST: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance)->print( + llvm::outs(), PrintOptions::printEverything()); + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::DumpScopeMaps: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + return dumpAndPrintScopeMap(Instance, + getPrimaryOrMainSourceFile(Instance)); + }); + case FrontendOptions::ActionType::DumpTypeRefinementContexts: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance) + ->getTypeRefinementContext() + ->dump(llvm::errs(), Instance.getASTContext().SourceMgr); + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::DumpInterfaceHash: + getPrimaryOrMainSourceFile(Instance)->dumpInterfaceHash(llvm::errs()); + return Context.hadError(); + case FrontendOptions::ActionType::EmitSyntax: + return emitSyntax(getPrimaryOrMainSourceFile(Instance), + opts.InputsAndOutputs.getSingleOutputFilename()); + case FrontendOptions::ActionType::EmitImportedModules: + return emitImportedModules(Instance.getMainModule(), opts); + + // MARK: Dependency Scanning Actions + case FrontendOptions::ActionType::ScanDependencies: + return performScanDependencies(Instance); + case FrontendOptions::ActionType::ScanClangDependencies: + return scanClangDependencies(Instance); + + // MARK: General Compilation Actions + case FrontendOptions::ActionType::Parse: + return performParseOnly(*Instance.getMainModule()); + case FrontendOptions::ActionType::ResolveImports: + return Instance.performParseAndResolveImportsOnly(); + case FrontendOptions::ActionType::Typecheck: + return withSemanticAnalysis(Instance, observer, + [](CompilerInstance &Instance) { + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::EmitSILGen: + case FrontendOptions::ActionType::EmitSIBGen: + case FrontendOptions::ActionType::EmitSIL: + case FrontendOptions::ActionType::EmitSIB: + case FrontendOptions::ActionType::EmitModuleOnly: + case FrontendOptions::ActionType::MergeModules: + case FrontendOptions::ActionType::Immediate: + case FrontendOptions::ActionType::EmitAssembly: + case FrontendOptions::ActionType::EmitIR: + case FrontendOptions::ActionType::EmitBC: + case FrontendOptions::ActionType::EmitObject: + case FrontendOptions::ActionType::DumpTypeInfo: + return withSemanticAnalysis( + Instance, observer, [&](CompilerInstance &Instance) { + assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) && + "All actions not requiring SILGen must have been handled!"); + if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) + return compileLLVMIR(Instance); + + return performCompileStepsPostSema(Instance, ReturnValue, observer); + }); + } + + assert(false && "Unhandled case in performCompile!"); + return Context.hadError(); +} + +/// Performs the compile requested by the user. +/// \param Instance Will be reset after performIRGeneration when the verifier +/// mode is NoVerify and there were no errors. +/// \returns true on error +static bool performCompile(CompilerInstance &Instance, + int &ReturnValue, + FrontendObserver *observer) { + const auto &Invocation = Instance.getInvocation(); + const auto &opts = Invocation.getFrontendOptions(); + const FrontendOptions::ActionType Action = opts.RequestedAction; // If we aren't in a parse-only context and expect an implicit stdlib import, // load in the standard library. If we either fail to find it or encounter an @@ -1793,95 +1893,27 @@ static bool performCompile(CompilerInvocation &Invok, return true; } - bool didFinishPipeline = false; - SWIFT_DEFER { - assert(didFinishPipeline && "Returned without calling finishPipeline"); - }; - - auto finishPipeline = [&](bool hadError) -> bool { - // We might have freed the ASTContext already, but in that case we would - // have already performed these actions. - if (Instance.hasASTContext()) { - performEndOfPipelineActions(Instance); - hadError |= Instance.getASTContext().hadError(); - } - didFinishPipeline = true; - return hadError; - }; - - auto &Context = Instance.getASTContext(); - if (FrontendOptions::shouldActionOnlyParse(Action)) { - // Parsing gets triggered lazily, but let's make sure we have the right - // input kind. - auto kind = Invocation.getInputKind(); - assert((kind == InputFileKind::Swift || - kind == InputFileKind::SwiftLibrary || - kind == InputFileKind::SwiftModuleInterface) && - "Only supports parsing .swift files"); - (void)kind; - } else if (Action == FrontendOptions::ActionType::ResolveImports) { - Instance.performParseAndResolveImportsOnly(); - return finishPipeline(Context.hadError()); - } else { - Instance.performSema(); - } - - if (Action == FrontendOptions::ActionType::Parse) { - // A -parse invocation only cares about the side effects of parsing, so - // force the parsing of all the source files. - for (auto *file : Instance.getMainModule()->getFiles()) { - if (auto *SF = dyn_cast(file)) - (void)SF->getTopLevelDecls(); + assert([&]() -> bool { + if (FrontendOptions::shouldActionOnlyParse(Action)) { + // Parsing gets triggered lazily, but let's make sure we have the right + // input kind. + auto kind = Invocation.getInputKind(); + return kind == InputFileKind::Swift || + kind == InputFileKind::SwiftLibrary || + kind == InputFileKind::SwiftModuleInterface; } - return finishPipeline(Context.hadError()); - } - - if (Action == FrontendOptions::ActionType::ScanDependencies) { - auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; - if (batchScanInput.empty()) - return finishPipeline(scanDependencies(Instance)); - else - return finishPipeline(batchScanModuleDependencies(Invok, - Instance, - batchScanInput)); - } - - if (Action == FrontendOptions::ActionType::ScanClangDependencies) - return finishPipeline(scanClangDependencies(Instance)); - - if (observer) - observer->performedSemanticAnalysis(Instance); - - { - FrontendOptions::DebugCrashMode CrashMode = opts.CrashMode; - if (CrashMode == FrontendOptions::DebugCrashMode::AssertAfterParse) - debugFailWithAssertion(); - else if (CrashMode == FrontendOptions::DebugCrashMode::CrashAfterParse) - debugFailWithCrash(); - } + return true; + }() && "Only supports parsing .swift files"); - (void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance); + bool hadError = performAction(Instance, ReturnValue, observer); - if (Action == FrontendOptions::ActionType::REPL) { - llvm::report_fatal_error("Compiler-internal integrated REPL has been " - "removed; use the LLDB-enhanced REPL instead."); + // We might have freed the ASTContext already, but in that case we would + // have already performed these actions. + if (Instance.hasASTContext()) { + performEndOfPipelineActions(Instance); + hadError |= Instance.getASTContext().hadError(); } - - if (auto r = dumpASTIfNeeded(Instance)) - return finishPipeline(*r); - - if (Context.hadError()) - return finishPipeline(/*hadError*/ true); - - // We've just been told to perform a typecheck, so we can return now. - if (Action == FrontendOptions::ActionType::Typecheck) - return finishPipeline(/*hadError*/ false); - - assert(FrontendOptions::doesActionGenerateSIL(Action) && - "All actions not requiring SILGen must have been handled!"); - - return finishPipeline( - performCompileStepsPostSema(Instance, ReturnValue, observer)); + return hadError; } static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs, @@ -2655,7 +2687,7 @@ int swift::performFrontend(ArrayRef Args, } int ReturnValue = 0; - bool HadError = performCompile(Invocation, *Instance, Args, ReturnValue, observer); + bool HadError = performCompile(*Instance, ReturnValue, observer); if (verifierEnabled) { DiagnosticEngine &diags = Instance->getDiags(); diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 90a33895ee52a..d750417737264 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -42,9 +42,9 @@ static void findAllClangImports(const clang::Module *module, } } -bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, +bool swift::emitImportedModules(ModuleDecl *mainModule, const FrontendOptions &opts) { - + auto &Context = mainModule->getASTContext(); std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); std::error_code EC; llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); diff --git a/lib/FrontendTool/ImportedModules.h b/lib/FrontendTool/ImportedModules.h index 42b4e76babaf4..510fa4ccdedde 100644 --- a/lib/FrontendTool/ImportedModules.h +++ b/lib/FrontendTool/ImportedModules.h @@ -20,8 +20,7 @@ class FrontendOptions; class ModuleDecl; /// Emit the names of the modules imported by \c mainModule. -bool emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, - const FrontendOptions &opts); +bool emitImportedModules(ModuleDecl *mainModule, const FrontendOptions &opts); } // end namespace swift #endif diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index fb0cd4b47a0bc..34e99395e1b17 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -690,9 +690,10 @@ bool swift::scanClangDependencies(CompilerInstance &instance) { .InputsAndOutputs.getSingleOutputFilename()); } -bool swift::batchScanModuleDependencies(CompilerInvocation &invok, - CompilerInstance &instance, +bool swift::batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile) { + const CompilerInvocation &invok = instance.getInvocation(); + (void)instance.getMainModule(); llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); diff --git a/lib/FrontendTool/ScanDependencies.h b/lib/FrontendTool/ScanDependencies.h index 4604496ba66dc..fab7d26bbb190 100644 --- a/lib/FrontendTool/ScanDependencies.h +++ b/lib/FrontendTool/ScanDependencies.h @@ -21,8 +21,7 @@ class CompilerInvocation; class CompilerInstance; /// Batch scan the dependencies for modules specified in \c batchInputFile. -bool batchScanModuleDependencies(CompilerInvocation &invok, - CompilerInstance &instance, +bool batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile); /// Scans the dependencies of the main module of \c instance. From fe7444ffa3e3ac61da5194102c57e6c451156c88 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 27 Aug 2020 12:10:13 -0700 Subject: [PATCH 093/120] Add doesActionRequireSwiftStandardLibrary --- include/swift/Frontend/FrontendOptions.h | 6 ++- lib/Frontend/FrontendOptions.cpp | 59 ++++++++++++++++++++---- lib/FrontendTool/FrontendTool.cpp | 2 +- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 724cf7e6efa31..0d3a0e68de7f1 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -294,9 +294,13 @@ class FrontendOptions { /// '.../lib/swift', otherwise '.../lib/swift_static'. bool UseSharedResourceFolder = true; - /// \return true if action only parses without doing other compilation steps. + /// \return true if the given action only parses without doing other compilation steps. static bool shouldActionOnlyParse(ActionType); + /// \return true if the given action requires the standard library to be + /// loaded before it is run. + static bool doesActionRequireSwiftStandardLibrary(ActionType); + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 83718a733666e..013a83157bf9d 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -73,20 +73,63 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { bool FrontendOptions::shouldActionOnlyParse(ActionType action) { switch (action) { - case FrontendOptions::ActionType::Parse: - case FrontendOptions::ActionType::DumpParse: - case FrontendOptions::ActionType::EmitSyntax: - case FrontendOptions::ActionType::DumpInterfaceHash: - case FrontendOptions::ActionType::EmitImportedModules: - case FrontendOptions::ActionType::ScanDependencies: - case FrontendOptions::ActionType::ScanClangDependencies: - case FrontendOptions::ActionType::PrintVersion: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::PrintVersion: return true; default: return false; } } +bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { + switch (action) { + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::PrintVersion: + case ActionType::EmitPCH: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::CompileModuleFromInterface: + case ActionType::TypecheckModuleFromInterface: + return false; + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpAST: + case ActionType::PrintAST: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::EmitSILGen: + case ActionType::EmitSIL: + case ActionType::EmitModuleOnly: + case ActionType::MergeModules: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitAssembly: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitObject: + case ActionType::DumpTypeInfo: + assert(!FrontendOptions::shouldActionOnlyParse(action) && + "Parse-only actions should not load modules!"); + return true; + } + llvm_unreachable("Unknown ActionType"); +} + void FrontendOptions::forAllOutputPaths( const InputFile &input, llvm::function_ref fn) const { if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly && diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 743205b9ccb44..0d28ccf3d2ace 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1888,7 +1888,7 @@ static bool performCompile(CompilerInstance &Instance, // trigger a bunch of other errors due to the stdlib being missing, or at // worst crash downstream as many call sites don't currently handle a missing // stdlib. - if (!FrontendOptions::shouldActionOnlyParse(Action)) { + if (FrontendOptions::doesActionRequireSwiftStandardLibrary(Action)) { if (Instance.loadStdlibIfNeeded()) return true; } From 4c56c95674a12342d4321ab78003a9fe983581c7 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 27 Aug 2020 16:13:47 -0700 Subject: [PATCH 094/120] Define doesActionRequireInputs --- include/swift/Frontend/FrontendOptions.h | 3 ++ .../ArgsToFrontendOptionsConverter.cpp | 3 +- lib/Frontend/FrontendOptions.cpp | 41 +++++++++++++++++++ lib/FrontendTool/FrontendTool.cpp | 34 ++++++++++----- 4 files changed, 68 insertions(+), 13 deletions(-) diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 0d3a0e68de7f1..c961794d35582 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -301,6 +301,9 @@ class FrontendOptions { /// loaded before it is run. static bool doesActionRequireSwiftStandardLibrary(ActionType); + /// \return true if the given action requires input files to be provided. + static bool doesActionRequireInputs(ActionType action); + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 4e12495bb35df..c876a44f87383 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -406,8 +406,7 @@ bool ArgsToFrontendOptionsConverter::setUpInputKindAndImmediateArgs() { if (Opts.InputsAndOutputs.verifyInputs( Diags, treatAsSIL, Opts.RequestedAction == FrontendOptions::ActionType::REPL, - (Opts.RequestedAction == FrontendOptions::ActionType::NoneAction || - Opts.RequestedAction == FrontendOptions::ActionType::PrintVersion))){ + !FrontendOptions::doesActionRequireInputs(Opts.RequestedAction))) { return true; } if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate) { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 013a83157bf9d..16fe1877a6d25 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -130,6 +130,47 @@ bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { llvm_unreachable("Unknown ActionType"); } +bool FrontendOptions::doesActionRequireInputs(ActionType action) { + switch (action) { + case ActionType::NoneAction: + case ActionType::PrintVersion: + return false; + case ActionType::REPL: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::EmitPCH: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::CompileModuleFromInterface: + case ActionType::TypecheckModuleFromInterface: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpAST: + case ActionType::PrintAST: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::EmitSILGen: + case ActionType::EmitSIL: + case ActionType::EmitModuleOnly: + case ActionType::MergeModules: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::Immediate: + case ActionType::EmitAssembly: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitObject: + case ActionType::DumpTypeInfo: + return true; + } + llvm_unreachable("Unknown ActionType"); +} + void FrontendOptions::forAllOutputPaths( const InputFile &input, llvm::function_ref fn) const { if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly && diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 0d28ccf3d2ace..f4b42016d7d46 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1653,6 +1653,7 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { // it's -emit-imported-modules, which can load modules. auto action = opts.RequestedAction; if (FrontendOptions::shouldActionOnlyParse(action) && + !ctx.getLoadedModules().empty() && action != FrontendOptions::ActionType::EmitImportedModules) { assert(ctx.getNumLoadedModules() == 1 && "Loaded a module during parse-only"); @@ -1689,9 +1690,15 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { } } - // Emit dependencies and index data. + // FIXME: This predicate matches the status quo, but there's no reason + // indexing cannot run for actions that do not require stdlib e.g. to better + // facilitate tests. + if (FrontendOptions::doesActionRequireSwiftStandardLibrary(action)) { + emitIndexData(Instance); + } + + // Emit dependencies. emitReferenceDependenciesForAllPrimaryInputsIfNeeded(Instance); - emitIndexData(Instance); emitMakeDependenciesIfNeeded(Instance.getDiags(), Instance.getDependencyTracker(), opts); @@ -1791,13 +1798,16 @@ static bool performAction(CompilerInstance &Instance, return buildModuleFromInterface(Instance); // MARK: Actions that Dump - case FrontendOptions::ActionType::DumpParse: { + case FrontendOptions::ActionType::DumpParse: + return dumpAST(Instance); + case FrontendOptions::ActionType::DumpAST: { + // FIXME: -dump-ast expects to be able to write output even if type checking + // fails which does not cleanly fit the model \c withSemanticAnalysis is + // trying to impose. Once there is a request for the "semantic AST", this + // point is moot. + Instance.performSema(); return dumpAST(Instance); } - case FrontendOptions::ActionType::DumpAST: - return withSemanticAnalysis( - Instance, observer, - [](CompilerInstance &Instance) { return dumpAST(Instance); }); case FrontendOptions::ActionType::PrintAST: return withSemanticAnalysis( Instance, observer, [](CompilerInstance &Instance) { @@ -1860,9 +1870,6 @@ static bool performAction(CompilerInstance &Instance, Instance, observer, [&](CompilerInstance &Instance) { assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) && "All actions not requiring SILGen must have been handled!"); - if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) - return compileLLVMIR(Instance); - return performCompileStepsPostSema(Instance, ReturnValue, observer); }); } @@ -1882,6 +1889,10 @@ static bool performCompile(CompilerInstance &Instance, const auto &opts = Invocation.getFrontendOptions(); const FrontendOptions::ActionType Action = opts.RequestedAction; + // To compile LLVM IR, just pass it off unmodified. + if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) + return compileLLVMIR(Instance); + // If we aren't in a parse-only context and expect an implicit stdlib import, // load in the standard library. If we either fail to find it or encounter an // error while loading it, bail early. Continuing the compilation will at best @@ -1909,7 +1920,8 @@ static bool performCompile(CompilerInstance &Instance, // We might have freed the ASTContext already, but in that case we would // have already performed these actions. - if (Instance.hasASTContext()) { + if (Instance.hasASTContext() && + FrontendOptions::doesActionRequireInputs(Action)) { performEndOfPipelineActions(Instance); hadError |= Instance.getASTContext().hadError(); } From e013ebd388cba978760cfd2c2cc604025de34a1b Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 27 Aug 2020 12:55:33 -0700 Subject: [PATCH 095/120] [CodeCompletion] Ensure all ExtensionDecl's extended nominal are computed Fixes an assertion failure in ASTScope lookup rdar://problem/67102794 --- lib/IDE/ExprContextAnalysis.cpp | 20 ++++++++++++++++++-- test/IDE/complete_type.swift | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 787606e321891..6a4fff5f8c011 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -46,9 +46,11 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { while (isa(DC)) DC = DC->getParent(); - // Make sure the extension has been bound, in case it is in an inactive #if - // or something weird like that. + // Make sure the extension has been bound. { + // Even if the extension is invalid (e.g. nested in a function or another + // type), we want to know the "intended nominal" of the extension so that + // we can know the type of 'Self'. SmallVector extensions; for (auto typeCtx = DC->getInnermostTypeContext(); typeCtx != nullptr; typeCtx = typeCtx->getParent()->getInnermostTypeContext()) { @@ -59,6 +61,20 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { extensions.back()->computeExtendedNominal(); extensions.pop_back(); } + + // If the completion happens in the inheritance clause of the extension, + // 'DC' is the parent of the extension. We need to iterate the top level + // decls to find it. In theory, we don't need the extended nominal in the + // inheritance clause, but ASTScope lookup requires that. We don't care + // unless 'DC' is not 'SourceFile' because non-toplevel extensions are + // 'canNeverBeBound()' anyway. + if (auto *SF = dyn_cast(DC)) { + auto &SM = DC->getASTContext().SourceMgr; + for (auto *decl : SF->getTopLevelDecls()) + if (auto *ext = dyn_cast(decl)) + if (SM.rangeContainsTokenLoc(ext->getSourceRange(), Loc)) + ext->computeExtendedNominal(); + } } // Type-check this context. diff --git a/test/IDE/complete_type.swift b/test/IDE/complete_type.swift index 04b9158fda578..4178ff2840b04 100644 --- a/test/IDE/complete_type.swift +++ b/test/IDE/complete_type.swift @@ -765,3 +765,17 @@ func testUnbound2(x: OuterStruct.Inner.#^UNBOUND_DOT_3^#) {} // UNBOUND_DOT_3: Begin completions // UNBOUND_DOT_3-DAG: Keyword/None: Type[#OuterStruct.Inner.Type#]; name=Type // UNBOUND_DOT_3: End completions + +// rdar://problem/67102794 +struct HasProtoAlias { + typealias ProtoAlias = FooProtocol +} +extension FooStruct: HasProtoAlias.#^EXTENSION_INHERITANCE_1?check=EXTENSION_INHERITANCE^# {} + +struct ContainExtension { + extension FooStruct: HasProtoAlias.#^EXTENSION_INHERITANCE_2?check=EXTENSION_INHERITANCE^# {} +} +// EXTENSION_INHERITANCE: Begin completions, 2 items +// EXTENSION_INHERITANCE-DAG: Decl[TypeAlias]/CurrNominal: ProtoAlias[#FooProtocol#]; +// EXTENSION_INHERITANCE-DAG: Keyword/None: Type[#HasProtoAlias.Type#]; +// EXTENSION_INHERITANCE: End completions From 16876fbf6617c40df326df7d3fe00a15ff77448b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Aug 2020 13:56:23 -0700 Subject: [PATCH 096/120] [Clang importer] Drop unused parameter from getParamOptionality(). This operation is not actually dependent on the version. --- lib/ClangImporter/ClangAdapter.cpp | 3 +-- lib/ClangImporter/ClangAdapter.h | 6 +----- lib/ClangImporter/ImportName.cpp | 5 +---- lib/ClangImporter/ImportType.cpp | 6 ++---- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index eddac5d46b3bf..a2ff1426bf477 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -714,8 +714,7 @@ bool importer::isUnavailableInSwift( return false; } -OptionalTypeKind importer::getParamOptionality(version::Version swiftVersion, - const clang::ParmVarDecl *param, +OptionalTypeKind importer::getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull) { auto &clangCtx = param->getASTContext(); diff --git a/lib/ClangImporter/ClangAdapter.h b/lib/ClangImporter/ClangAdapter.h index 0b066364bd622..9a15bbd523efa 100644 --- a/lib/ClangImporter/ClangAdapter.h +++ b/lib/ClangImporter/ClangAdapter.h @@ -161,15 +161,11 @@ bool isUnavailableInSwift(const clang::Decl *decl, const PlatformAvailability &, /// Determine the optionality of the given Clang parameter. /// -/// \param swiftLanguageVersion What version of Swift we're using, which affects -/// how optionality is inferred. -/// /// \param param The Clang parameter. /// /// \param knownNonNull Whether a function- or method-level "nonnull" attribute /// applies to this parameter. -OptionalTypeKind getParamOptionality(version::Version swiftLanguageVersion, - const clang::ParmVarDecl *param, +OptionalTypeKind getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull); } } diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 46817ce93de2a..088b62ea3d2a6 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -807,8 +807,6 @@ static bool omitNeedlessWordsInFunctionName( Optional errorParamIndex, bool returnsSelf, bool isInstanceMethod, NameImporter &nameImporter) { clang::ASTContext &clangCtx = nameImporter.getClangContext(); - const version::Version &swiftLanguageVersion = - nameImporter.getLangOpts().EffectiveLanguageVersion; // Collect the parameter type names. StringRef firstParamName; @@ -837,8 +835,7 @@ static bool omitNeedlessWordsInFunctionName( bool hasDefaultArg = ClangImporter::Implementation::inferDefaultArgument( param->getType(), - getParamOptionality(swiftLanguageVersion, param, - !nonNullArgs.empty() && nonNullArgs[i]), + getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[i]), nameImporter.getIdentifier(baseName), argumentName, i == 0, isLastParameter, nameImporter) != DefaultArgumentKind::None; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index a13fb40d28210..69034c5c3778e 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1747,8 +1747,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( // Check nullability of the parameter. OptionalTypeKind OptionalityOfParam = - getParamOptionality(SwiftContext.LangOpts.EffectiveLanguageVersion, - param, !nonNullArgs.empty() && nonNullArgs[index]); + getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[index]); ImportTypeKind importKind = ImportTypeKind::Parameter; if (param->hasAttr()) @@ -2127,8 +2126,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( // Check nullability of the parameter. OptionalTypeKind optionalityOfParam - = getParamOptionality(SwiftContext.LangOpts.EffectiveLanguageVersion, - param, + = getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[paramIndex]); bool allowNSUIntegerAsIntInParam = isFromSystemModule; From 6e8ee7cc49f0443ed929640ab82192858b4f4f67 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Fri, 28 Aug 2020 04:10:37 +0200 Subject: [PATCH 097/120] [docs] Fix small mistake in DynamicCasting.md (#33641) Foundation has no `Number` type. --- docs/DynamicCasting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DynamicCasting.md b/docs/DynamicCasting.md index add2524d59e33..1ef306549a148 100644 --- a/docs/DynamicCasting.md +++ b/docs/DynamicCasting.md @@ -191,7 +191,7 @@ Implementation Note: `AnyObject` is represented in memory as a pointer to a refc ### Objective-C Interactions Note the invariant above cannot be an equality because Objective-C bridging allows libraries to introduce new relationships that can alter the behavior of seemingly-unrelated casts. -One example of this is Foundation's `Number` (or `NSNumber`) type which conditionally bridges to several Swift numeric types. +One example of this is Foundation's `NSNumber` type which conditionally bridges to several Swift numeric types. As a result, when Foundation is in scope, `Int(7) is Double == false` but `(Int(7) as! AnyObject) is Double == true`. In general, the ability to add new bridging behaviors from a single type to several distinct types implies that Swift casting cannot be transitive. From 5afbddb42bbfc15dda8d6c286dd69033aa4a1a59 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 27 Aug 2020 19:21:53 -0700 Subject: [PATCH 098/120] [Dependency Scanner] Ensure that Clang dependency scanner instances inherit the creating invocation's extra clang args. This ensures that when the dependency scanner is invoked with additional clang (`-Xcc`) options, the Clang scanner is correctly configured using these options. --- .../ClangModuleDependencyScanner.cpp | 2 + test/ScanDependencies/Inputs/CHeaders/G.h | 5 ++ test/ScanDependencies/Inputs/CHeaders/X.h | 1 + .../Inputs/CHeaders/module.modulemap | 5 ++ .../batch_module_scan_versioned.swift | 49 +++++++++++++++++++ .../can_import_placeholder.swift | 2 +- .../diagnose_dependency_cycle.swift | 2 +- test/ScanDependencies/module_deps.swift | 3 +- .../module_deps_clang_only.swift | 2 +- .../module_deps_cross_import_overlay.swift | 2 +- .../module_deps_external.swift | 2 +- .../module_deps_private_interface.swift | 2 +- test/ScanDependencies/module_framework.swift | 2 +- 13 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 test/ScanDependencies/Inputs/CHeaders/X.h create mode 100644 test/ScanDependencies/batch_module_scan_versioned.swift diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index 290dd69506ed2..85e517b6ce435 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -308,6 +308,7 @@ Optional ClangImporter::getModuleDependencies( // Reform the Clang importer options. // FIXME: Just save a reference or copy so we can get this back. ClangImporterOptions importerOpts; + importerOpts.ExtraArgs = getExtraClangArgs(); // Determine the command-line arguments for dependency scanning. auto &ctx = Impl.SwiftContext; @@ -351,6 +352,7 @@ bool ClangImporter::addBridgingHeaderDependencies( // Reform the Clang importer options. // FIXME: Just save a reference or copy so we can get this back. ClangImporterOptions importerOpts; + importerOpts.ExtraArgs = getExtraClangArgs(); // Retrieve the bridging header. std::string bridgingHeader = *targetModule.getBridgingHeader(); diff --git a/test/ScanDependencies/Inputs/CHeaders/G.h b/test/ScanDependencies/Inputs/CHeaders/G.h index ef09c49a98e0f..4da23ffcbb4f6 100644 --- a/test/ScanDependencies/Inputs/CHeaders/G.h +++ b/test/ScanDependencies/Inputs/CHeaders/G.h @@ -1 +1,6 @@ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 110000 +#include "X.h" +#endif + void funcG(void); + diff --git a/test/ScanDependencies/Inputs/CHeaders/X.h b/test/ScanDependencies/Inputs/CHeaders/X.h new file mode 100644 index 0000000000000..11c123884ef12 --- /dev/null +++ b/test/ScanDependencies/Inputs/CHeaders/X.h @@ -0,0 +1 @@ +void funcX(void); diff --git a/test/ScanDependencies/Inputs/CHeaders/module.modulemap b/test/ScanDependencies/Inputs/CHeaders/module.modulemap index 481266591c912..95fc7fef133c2 100644 --- a/test/ScanDependencies/Inputs/CHeaders/module.modulemap +++ b/test/ScanDependencies/Inputs/CHeaders/module.modulemap @@ -37,3 +37,8 @@ module I { header "I.h" export * } + +module X { + header "X.h" + export * +} diff --git a/test/ScanDependencies/batch_module_scan_versioned.swift b/test/ScanDependencies/batch_module_scan_versioned.swift new file mode 100644 index 0000000000000..02725a2ff0160 --- /dev/null +++ b/test/ScanDependencies/batch_module_scan_versioned.swift @@ -0,0 +1,49 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/inputs) +// RUN: %empty-directory(%t/outputs) +// RUN: mkdir -p %t/clang-module-cache + +// RUN: echo "[{" > %/t/inputs/input.json +// RUN: echo "\"clangModuleName\": \"G\"," >> %/t/inputs/input.json +// RUN: echo "\"arguments\": \"-Xcc -target -Xcc x86_64-apple-macosx10.9\"," >> %/t/inputs/input.json +// RUN: echo "\"output\": \"%/t/outputs/G_109.pcm.json\"" >> %/t/inputs/input.json +// RUN: echo "}," >> %/t/inputs/input.json +// RUN: echo "{" >> %/t/inputs/input.json +// RUN: echo "\"clangModuleName\": \"G\"," >> %/t/inputs/input.json +// RUN: echo "\"arguments\": \"-Xcc -target -Xcc x86_64-apple-macosx11.0\"," >> %/t/inputs/input.json +// RUN: echo "\"output\": \"%/t/outputs/G_110.pcm.json\"" >> %/t/inputs/input.json +// RUN: echo "}]" >> %/t/inputs/input.json + +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -batch-scan-input-file %/t/inputs/input.json + +// Check the contents of the JSON output +// RUN: %FileCheck %s -check-prefix=CHECK-PCM109 < %t/outputs/G_109.pcm.json +// RUN: %FileCheck %s -check-prefix=CHECK-PCM110 < %t/outputs/G_110.pcm.json + +// CHECK-PCM109: { +// CHECK-PCM109-NEXT: "mainModuleName": "G", +// CHECK-PCM109-NEXT: "modules": [ +// CHECK-PCM109-NEXT: { +// CHECK-PCM109-NEXT: "clang": "G" +// CHECK-PCM109-NEXT: }, +// CHECK-PCM109-NEXT: { +// CHECK-PCM109-NEXT: "modulePath": "G.pcm", +// CHECK-PCM109: "directDependencies": [ +// CHECK-PCM109-NEXT: { +// CHECK-PCM109-NEXT: "clang": "X" +// CHECK-PCM109-NEXT: } +// CHECK-PCM109-NEXT: ], +// CHECK-PCM109: "-I + +// CHECK-PCM110: { +// CHECK-PCM110-NEXT: "mainModuleName": "G", +// CHECK-PCM110-NEXT: "modules": [ +// CHECK-PCM110-NEXT: { +// CHECK-PCM110-NEXT: "clang": "G" +// CHECK-PCM110-NEXT: }, +// CHECK-PCM110-NEXT: { +// CHECK-PCM110-NEXT: "modulePath": "G.pcm", +// CHECK-PCM110: "directDependencies": [ +// CHECK-PCM110-NEXT: ], +// CHECK-PCM110-NOT: "clang": "X" +// CHECK-PCM110: "-I diff --git a/test/ScanDependencies/can_import_placeholder.swift b/test/ScanDependencies/can_import_placeholder.swift index 8b64644fc9a9b..8c14c6b2a125e 100644 --- a/test/ScanDependencies/can_import_placeholder.swift +++ b/test/ScanDependencies/can_import_placeholder.swift @@ -20,7 +20,7 @@ // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json -// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json diff --git a/test/ScanDependencies/diagnose_dependency_cycle.swift b/test/ScanDependencies/diagnose_dependency_cycle.swift index 1157d364a7954..609fd331646eb 100644 --- a/test/ScanDependencies/diagnose_dependency_cycle.swift +++ b/test/ScanDependencies/diagnose_dependency_cycle.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: mkdir -p %t/clang-module-cache -// RUN: not %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules &> %t/out.txt +// RUN: not %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 &> %t/out.txt // RUN: %FileCheck %s < %t/out.txt diff --git a/test/ScanDependencies/module_deps.swift b/test/ScanDependencies/module_deps.swift index 05b6f15c322fb..dc48ba5e0998d 100644 --- a/test/ScanDependencies/module_deps.swift +++ b/test/ScanDependencies/module_deps.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: mkdir -p %t/clang-module-cache -// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json @@ -105,7 +105,6 @@ import SubE // CHECK-NEXT: "-only-use-extra-clang-opts" // CHECK-NEXT: "-Xcc" // CHECK-NEXT: "clang" -// CHECK: "-fno-implicit-modules" /// --------Swift module E // CHECK: "swift": "E" diff --git a/test/ScanDependencies/module_deps_clang_only.swift b/test/ScanDependencies/module_deps_clang_only.swift index e406bacd74889..34ac0c6eeefd1 100644 --- a/test/ScanDependencies/module_deps_clang_only.swift +++ b/test/ScanDependencies/module_deps_clang_only.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: mkdir -p %t/clang-module-cache -// RUN: %target-swift-frontend -scan-clang-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules -module-name C +// RUN: %target-swift-frontend -scan-clang-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -module-name C // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json diff --git a/test/ScanDependencies/module_deps_cross_import_overlay.swift b/test/ScanDependencies/module_deps_cross_import_overlay.swift index 7b1bfd9b63363..94031c4ce38bc 100644 --- a/test/ScanDependencies/module_deps_cross_import_overlay.swift +++ b/test/ScanDependencies/module_deps_cross_import_overlay.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: mkdir -p %t/clang-module-cache -// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json diff --git a/test/ScanDependencies/module_deps_external.swift b/test/ScanDependencies/module_deps_external.swift index c7eb47a7f4e87..f3b2147a487da 100644 --- a/test/ScanDependencies/module_deps_external.swift +++ b/test/ScanDependencies/module_deps_external.swift @@ -20,7 +20,7 @@ // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json -// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -placeholder-dependency-module-map-file %t/inputs/map.json -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json diff --git a/test/ScanDependencies/module_deps_private_interface.swift b/test/ScanDependencies/module_deps_private_interface.swift index bbf0a2dfbd053..2098775216ea8 100644 --- a/test/ScanDependencies/module_deps_private_interface.swift +++ b/test/ScanDependencies/module_deps_private_interface.swift @@ -6,7 +6,7 @@ // RUN: cp %t/Foo.swiftinterface %t/Foo.private.swiftinterface -// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %t -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %t // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json diff --git a/test/ScanDependencies/module_framework.swift b/test/ScanDependencies/module_framework.swift index 937667d98d025..9756277074277 100644 --- a/test/ScanDependencies/module_framework.swift +++ b/test/ScanDependencies/module_framework.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -swift-version 4 -disable-implicit-swift-modules -Xcc -Xclang -Xcc -fno-implicit-modules +// RUN: %target-swift-frontend -scan-dependencies %s -o %t/deps.json -emit-dependencies -emit-dependencies-path %t/deps.d -swift-version 4 -Xcc -Xclang // Check the contents of the JSON output // RUN: %FileCheck %s < %t/deps.json From b1db77adff4ee91bc24b24652d04c67487da68b4 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 27 Aug 2020 20:42:23 -0700 Subject: [PATCH 099/120] Fix ConnectionGraph verification for calls to no return functions. (#33655) Functions that do not have a return, and instead end with 'unreachable' due to NoReturnFolding will not have a ReturnNode in the connection graph. A caller calling a no return function then may not have a CGNode corresponding to the call's result. Fix the ConnectionGraph's verifier so we don't assert in such cases. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 7 ++++++ test/SILOptimizer/escape_analysis.sil | 24 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 594cb81485a8c..c651cb5bf2943 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -1623,6 +1623,13 @@ void EscapeAnalysis::ConnectionGraph::verify() const { if (auto ai = dyn_cast(&i)) { if (EA->canOptimizeArrayUninitializedCall(ai).isValid()) continue; + // Ignore checking CGNode mapping for result of apply to a no return + // function that will have a null ReturnNode + if (auto *callee = ai->getReferencedFunctionOrNull()) { + if (EA->getFunctionInfo(callee)->isValid()) + if (!EA->getConnectionGraph(callee)->getReturnNodeOrNull()) + continue; + } } for (auto result : i.getResults()) { if (EA->getPointerBase(result)) diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index bc8b42ba2426d..21944bbe21603 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -1983,3 +1983,27 @@ bb4: %z = tuple () return %z : $() } + +// Test CGNode mapping for result of an apply to no return function +// CHECK-LABEL:CG of $testcaller +// CHECK: Arg [ref] %0 Esc: A, Succ: (%0.1) +// CHECK: Con [int] %0.1 Esc: A, Succ: (%0.2) +// CHECK: Con [ref] %0.2 Esc: A, Succ: +// CHECK-LABEL:End +sil hidden [noinline] @$testcaller : $@convention(thin) (@owned Z) -> () { +bb0(%0 : $Z): + %2 = function_ref @$noreturncallee : $@convention(thin) (@guaranteed Z) -> @owned Z + %3 = apply %2(%0) : $@convention(thin) (@guaranteed Z) -> @owned Z + dealloc_ref %3 : $Z + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL:CG of $noreturncallee +// CHECK: Arg [ref] %0 Esc: A, Succ: +// CHECK-LABEL:End +sil hidden [noinline] @$noreturncallee : $@convention(thin) (@guaranteed Z) -> @owned Z { +bb0(%0 : $Z): + unreachable +} + From 1e5d30f5ca78204c8239cf4088fb4e1bebf141f7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Aug 2020 16:19:52 -0700 Subject: [PATCH 100/120] [Concurrency] Import Objective-C methods with completion handlers as async When a given Objective-C method has a completion handler parameter with an appropriate signature, import that Objective-C method as async. For example, consider the following CloudKit API: - (void)fetchShareParticipantWithUserRecordID:(CKRecordID *)userRecordID completionHandler:(void (^)(CKShareParticipant * _Nullable shareParticipant, NSError * _Nullable error))completionHandler; With the experimental concurrency model, this would import as: func fetchShareParticipant(withUserRecordID userRecordID: CKRecord.ID) async throws -> CKShare.Participant? The compiler will be responsible for turning the caller's continuation into a block to pass along to the completion handler. When the error parameter of the completion handler is non-null, the async call will result in that error being thrown. Otherwise, the other arguments passed to that completion handler will be returned as the result of the async call. async versions of methods are imported alongside their completion-handler versions, to maintain source compatibility with existing code that provides a completion handler. Note that this only covers the Clang importer portion of this task. --- include/swift/AST/Decl.h | 14 +- include/swift/AST/ForeignAsyncConvention.h | 81 ++++++++ include/swift/AST/ForeignErrorConvention.h | 23 ++- lib/AST/ASTContext.cpp | 23 +++ lib/AST/Decl.cpp | 5 +- lib/ClangImporter/ClangImporter.cpp | 7 + lib/ClangImporter/ImportDecl.cpp | 59 +++++- lib/ClangImporter/ImportName.cpp | 194 ++++++++++++++++++ lib/ClangImporter/ImportName.h | 61 +++++- lib/ClangImporter/ImportType.cpp | 55 +++++ lib/ClangImporter/SwiftLookupTable.cpp | 1 + test/ClangImporter/objc_async.swift | 22 ++ test/IDE/print_clang_objc_async.swift | 19 ++ .../usr/include/ObjCConcurrency.h | 14 ++ .../clang-importer-sdk/usr/include/module.map | 5 + utils/swift-api-dump.py | 4 + 16 files changed, 564 insertions(+), 23 deletions(-) create mode 100644 include/swift/AST/ForeignAsyncConvention.h create mode 100644 test/ClangImporter/objc_async.swift create mode 100644 test/IDE/print_clang_objc_async.swift create mode 100644 test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c21cac7948679..5842456999735 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -63,6 +63,7 @@ namespace swift { class Type; class Expr; class DeclRefExpr; + class ForeignAsyncConvention; class ForeignErrorConvention; class LiteralExpr; class BraceStmt; @@ -6148,7 +6149,15 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// being dropped altogether. `None` is returned for a normal function /// or method. Optional getForeignFunctionAsMethodSelfParameterIndex() const; - + + /// Set information about the foreign async convention used by this + /// declaration. + void setForeignAsyncConvention(const ForeignAsyncConvention &convention); + + /// Get information about the foreign async convention used by this + /// declaration, given that it is @objc and 'async'. + Optional getForeignAsyncConvention() const; + static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_AbstractFunctionDecl && D->getKind() <= DeclKind::Last_AbstractFunctionDecl; @@ -6277,7 +6286,8 @@ class FuncDecl : public AbstractFunctionDecl { DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, bool Throws, + DeclName Name, SourceLoc NameLoc, + bool Async, bool Throws, ParameterList *BodyParams, Type FnRetType, DeclContext *Parent, ClangNode ClangN); diff --git a/include/swift/AST/ForeignAsyncConvention.h b/include/swift/AST/ForeignAsyncConvention.h new file mode 100644 index 0000000000000..f3657766e02af --- /dev/null +++ b/include/swift/AST/ForeignAsyncConvention.h @@ -0,0 +1,81 @@ +//===--- ForeignAsyncConvention.h - Async conventions -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the ForeignAsyncConvention structure, which +// describes the rules for how to detect that a foreign API is asynchronous. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_FOREIGN_ASYNC_CONVENTION_H +#define SWIFT_FOREIGN_ASYNC_CONVENTION_H + +#include "swift/AST/Type.h" + +namespace swift { + +/// A small structure describing the async convention of a foreign declaration. +class ForeignAsyncConvention { + /// The index of the completion handler parameters. + unsigned CompletionHandlerParamIndex; + + /// When non-zero, indicates which parameter to the completion handler is the + /// Error? parameter (minus one) that makes this async function also throwing. + unsigned CompletionHandlerErrorParamIndex; +public: + ForeignAsyncConvention() + : CompletionHandlerParamIndex(0), CompletionHandlerErrorParamIndex(0) { } + + ForeignAsyncConvention(unsigned completionHandlerParamIndex, + Optional completionHandlerErrorParamIndex) + : CompletionHandlerParamIndex(completionHandlerParamIndex), + CompletionHandlerErrorParamIndex( + completionHandlerErrorParamIndex + ? *completionHandlerErrorParamIndex + 1 + : 0) {} + + /// Retrieve the index of the completion handler parameter, which will be + /// erased from the Swift signature of the imported async function. + unsigned completionHandlerParamIndex() const { + return CompletionHandlerParamIndex; + } + + /// Retrieve the index of the \c Error? parameter in the completion handler's + /// parameter list. When argument passed to this parameter is non-null, the + /// provided error will be thrown by the async function. + Optional completionHandlerErrorParamIndex() const { + if (CompletionHandlerErrorParamIndex == 0) + return None; + + return CompletionHandlerErrorParamIndex - 1; + } + + /// Whether the async function is throwing due to the completion handler + /// having an \c Error? parameter. + /// + /// Equivalent to \c static_cast(completionHandlerErrorParamIndex()). + bool isThrowing() const { + return CompletionHandlerErrorParamIndex != 0; + } + + bool operator==(ForeignAsyncConvention other) const { + return CompletionHandlerParamIndex == other.CompletionHandlerParamIndex + && CompletionHandlerErrorParamIndex == + other.CompletionHandlerErrorParamIndex; + } + bool operator!=(ForeignAsyncConvention other) const { + return !(*this == other); + } +}; + +} + +#endif diff --git a/include/swift/AST/ForeignErrorConvention.h b/include/swift/AST/ForeignErrorConvention.h index 48d65d4612856..d09a7a80c0d86 100644 --- a/include/swift/AST/ForeignErrorConvention.h +++ b/include/swift/AST/ForeignErrorConvention.h @@ -81,6 +81,10 @@ class ForeignErrorConvention { } Info() = default; + + Kind getKind() const { + return static_cast(TheKind); + } }; private: @@ -178,11 +182,24 @@ class ForeignErrorConvention { /// Returns the physical result type of the function, for functions /// that completely erase this information. CanType getResultType() const { - assert(getKind() == ZeroResult || - getKind() == NonZeroResult); + assert(resultTypeErasedToVoid(getKind())); return ResultType; } - + + /// Whether this kind of error import erases the result type to 'Void'. + static bool resultTypeErasedToVoid(Kind kind) { + switch (kind) { + case ZeroResult: + case NonZeroResult: + return true; + + case ZeroPreservedResult: + case NilResult: + case NonNilError: + return false; + } + } + bool operator==(ForeignErrorConvention other) const { return info.TheKind == other.info.TheKind && info.ErrorIsOwned == other.info.ErrorIsOwned diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 633527ccf7735..e4f59973c6a07 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -18,6 +18,7 @@ #include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" @@ -281,6 +282,10 @@ struct ASTContext::Implementation { llvm::DenseMap ForeignErrorConventions; + /// Map from declarations to foreign async conventions. + llvm::DenseMap ForeignAsyncConventions; + /// Cache of previously looked-up precedence queries. AssociativityCacheType AssociativityCache; @@ -2238,6 +2243,24 @@ AbstractFunctionDecl::getForeignErrorConvention() const { return it->second; } +void AbstractFunctionDecl::setForeignAsyncConvention( + const ForeignAsyncConvention &conv) { + assert(hasAsync() && "setting error convention on non-throwing decl"); + auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions; + assert(!conventionsMap.count(this) && "error convention already set"); + conventionsMap.insert({this, conv}); +} + +Optional +AbstractFunctionDecl::getForeignAsyncConvention() const { + if (!hasAsync()) + return None; + auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions; + auto it = conventionsMap.find(this); + if (it == conventionsMap.end()) return None; + return it->second; +} + Optional swift::getKnownFoundationEntity(StringRef name){ return llvm::StringSwitch>(name) #define FOUNDATION_ENTITY(Name) .Case(#Name, KnownFoundationEntity::Name) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4fdb7199e4ba3..f1f17cbf3c042 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7255,13 +7255,14 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, - bool Throws, ParameterList *BodyParams, + bool Async, bool Throws, + ParameterList *BodyParams, Type FnRetType, DeclContext *Parent, ClangNode ClangN) { assert(ClangN && FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - /*Async=*/false, SourceLoc(), Throws, SourceLoc(), + Async, SourceLoc(), Throws, SourceLoc(), /*GenericParams=*/nullptr, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 74a7e4f17fe8e..e1f1ad590b202 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3681,6 +3681,7 @@ void ClangImporter::Implementation::lookupValue( clangDecl->getMostRecentDecl(); CurrentVersion.forEachOtherImportNameVersion( + SwiftContext.LangOpts.EnableExperimentalConcurrency, [&](ImportNameVersion nameVersion) { if (anyMatching) return; @@ -3690,6 +3691,12 @@ void ClangImporter::Implementation::lookupValue( if (!newName.getDeclName().matchesRef(name)) return; + // If we asked for an async import and didn't find one, skip this. + // This filters out duplicates. + if (nameVersion.supportsConcurrency() && + !newName.getAsyncInfo()) + return; + const clang::DeclContext *clangDC = newName.getEffectiveContext().getAsDeclContext(); if (!clangDC || !clangDC->isFileContext()) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 21488368a4dbe..52f26c7d0eaaf 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -160,6 +160,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, DeclName name, SourceLoc nameLoc, ParameterList *bodyParams, Type resultTy, + bool async, bool throws, DeclContext *dc, ClangNode clangNode) { @@ -176,7 +177,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, bodyParams, resultTy, dc, clangNode); } else { - return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, throws, + return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, async, throws, bodyParams, resultTy, dc, clangNode); } } @@ -2254,7 +2255,7 @@ namespace { /// Whether the names we're importing are from the language version the user /// requested, or if these are decls from another version bool isActiveSwiftVersion() const { - return getVersion() == getActiveSwiftVersion(); + return getVersion().withConcurrency(false) == getActiveSwiftVersion().withConcurrency(false); } void recordMemberInContext(const DeclContext *dc, ValueDecl *member) { @@ -2300,7 +2301,7 @@ namespace { return canonicalName; } - // Special handling when we import using the older Swift name. + // Special handling when we import using the alternate Swift name. // // Import using the alternate Swift name. If that fails, or if it's // identical to the active Swift name, we won't introduce an alternate @@ -2309,6 +2310,19 @@ namespace { if (!alternateName) return ImportedName(); + // Importing for concurrency is special in that the same declaration + // is imported both with a completion handler parameter and as 'async', + // creating two separate declarations. + if (getVersion().supportsConcurrency()) { + // If the resulting name isn't special for concurrency, it's not + // different. + if (!alternateName.getAsyncInfo()) + return ImportedName(); + + // Otherwise, it's a legitimately different import. + return alternateName; + } + if (alternateName.getDeclName() == canonicalName.getDeclName() && alternateName.getEffectiveContext().equalsWithoutResolving( canonicalName.getEffectiveContext())) { @@ -2470,6 +2484,13 @@ namespace { return; } + // If this the active and current Swift versions differ based on + // concurrency, it's not actually a variant. + if (getVersion().supportsConcurrency() != + getActiveSwiftVersion().supportsConcurrency()) { + return; + } + // TODO: some versions should be deprecated instead of unavailable ASTContext &ctx = decl->getASTContext(); @@ -3837,9 +3858,10 @@ namespace { // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); - result = createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*throws*/ false, dc, decl); + result = createFuncOrAccessor( + Impl.SwiftContext, loc, accessorInfo, name, + nameLoc, bodyParams, resultTy, + /*async*/ false, /*throws*/ false, dc, decl); if (!dc->isModuleScopeContext()) { if (selfIsInOut) @@ -4410,6 +4432,16 @@ namespace { } } + // Determine whether the function is throwing and/or async. + bool throws = importedName.getErrorInfo().hasValue(); + bool async = false; + auto asyncConvention = importedName.getAsyncInfo(); + if (asyncConvention) { + async = true; + if (asyncConvention->isThrowing()) + throws = true; + } + auto resultTy = importedType.getType(); auto isIUO = importedType.isImplicitlyUnwrapped(); @@ -4439,8 +4471,7 @@ namespace { importedName.getDeclName(), /*nameLoc*/SourceLoc(), bodyParams, resultTy, - importedName.getErrorInfo().hasValue(), - dc, decl); + async, throws, dc, decl); result->setAccess(getOverridableAccessLevel(dc)); @@ -4472,6 +4503,11 @@ namespace { result->setForeignErrorConvention(*errorConvention); } + // Record the async convention. + if (asyncConvention) { + result->setForeignAsyncConvention(*asyncConvention); + } + // Handle attributes. if (decl->hasAttr() && isa(result) && @@ -4503,6 +4539,7 @@ namespace { Impl.addAlternateDecl(result, cast(imported)); } } + return result; } @@ -6443,6 +6480,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( return known->second; // Create the actual constructor. + assert(!importedName.getAsyncInfo()); auto result = Impl.createDeclWithClangNode( objcMethod, AccessLevel::Public, importedName.getDeclName(), /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), @@ -7145,6 +7183,11 @@ void SwiftDeclConverter::importMirroredProtocolMembers( if (isa(afd)) return; + // Asynch methods are also always imported without async, so don't + // record them here. + if (afd->hasAsync()) + return; + auto objcMethod = dyn_cast_or_null(member->getClangDecl()); if (!objcMethod) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 088b62ea3d2a6..791c695ff1b91 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1136,6 +1136,174 @@ Optional NameImporter::considerErrorImport( return None; } +/// Whether the given parameter name identifies a completion handler. +static bool isCompletionHandlerParamName(StringRef paramName) { + return paramName == "completionHandler" || paramName == "completion" || + paramName == "withCompletionHandler"; +} + +/// Whether the give base name implies that the first parameter is a completion +/// handler. +/// +/// \returns a trimmed base name when it does, \c None others +static Optional isCompletionHandlerInBaseName(StringRef basename) { + if (basename.endswith("WithCompletionHandler")) { + return basename.drop_back(strlen("WithCompletionHandler")); + } + + if (basename.endswith("WithCompletion")) { + return basename.drop_back(strlen("WithCompletion")); + } + + return None; +} + + +// Determine whether the given type is a nullable NSError type. +static bool isNullableNSErrorType( + clang::ASTContext &clangCtx, clang::QualType type) { + auto objcPtrType = type->getAs(); + if (!objcPtrType) + return false; + + auto iface = objcPtrType->getInterfaceDecl(); + if (!iface || iface->getName() != "NSError") + return false; + + // If nullability is specified, check it. + if (auto nullability = type->getNullability(clangCtx)) { + switch (translateNullability(*nullability)) { + case OTK_None: + return false; + + case OTK_ImplicitlyUnwrappedOptional: + case OTK_Optional: + return true; + } + } + + // Otherwise, assume it's nullable. + return true; +} + +Optional +NameImporter::considerAsyncImport( + const clang::ObjCMethodDecl *clangDecl, + StringRef &baseName, + SmallVectorImpl ¶mNames, + ArrayRef params, + bool isInitializer, bool hasCustomName, + Optional errorInfo) { + // If there are no unclaimed parameters, there's no . + unsigned errorParamAdjust = errorInfo ? 1 : 0; + if (params.size() - errorParamAdjust == 0) + return None; + + // If the # of parameter names doesn't line up with the # of parameters, + // bail out. There are extra C parameters on the method or a custom name + // was incorrect. + if (params.size() != paramNames.size() + errorParamAdjust) + return None; + + // The last parameter will be the completion handler for an async function. + unsigned completionHandlerParamIndex = params.size() - 1; + unsigned completionHandlerParamNameIndex = paramNames.size() - 1; + + // Determine whether the naming indicates that this is a completion + // handler. + Optional newBaseName; + if (isCompletionHandlerParamName(paramNames[completionHandlerParamNameIndex])) { + // The parameter itself has an appropriate name. + } else if (!hasCustomName && completionHandlerParamIndex == 0 && + (newBaseName = isCompletionHandlerInBaseName(baseName))) { + // The base name implies that the first parameter is a completion handler. + } else { + return None; + } + + // Used for returns once we've determined that the method cannot be + // imported as async, even though it has what looks like a completion handler + // parameter. + auto notAsync = [&](const char *reason) -> Optional { +#ifdef ASYNC_IMPORT_DEBUG + llvm::errs() << "*** failed async import: " << reason << "\n"; + clangDecl->dump(llvm::errs()); +#endif + + return None; + }; + + // Initializers cannot be 'async'. + // FIXME: We might eventually allow this. + if (isInitializer) + return notAsync("initializers cannot be async"); + + // Check whether we method has a suitable return type. + if (clangDecl->getReturnType()->isVoidType()) { + // 'void' is the common case; the method produces no synchronous result. + } else if (errorInfo && + ForeignErrorConvention::resultTypeErasedToVoid( + errorInfo->getKind())) { + // The method has been imported as throwing in a manner that erased the + // result type to Void. + } else { + return notAsync("method does not return void"); + } + + // The completion handler parameter must have block type. + auto completionHandlerParam = params[completionHandlerParamIndex]; + if (!isBlockParameter(completionHandlerParam)) + return notAsync("parameter is not a block"); + + // Dig out the function type of the completion handler's block type. + // If there is no prototype, (e.g., the completion handler is of type + // void (^)()), we cannot importer it. + auto completionHandlerFunctionType = + completionHandlerParam->getType()->castAs() + ->getPointeeType()->getAs(); + if (!completionHandlerFunctionType) + return notAsync("block parameter does not have a prototype"); + + // The completion handler parameter must itself return 'void'. + if (!completionHandlerFunctionType->getReturnType()->isVoidType()) + return notAsync("completion handler parameter does not return 'void'"); + + // Scan the parameters of the block type to look for a parameter of a + // nullable NSError type, which would indicate that the async method could + // throw. + Optional completionHandlerErrorParamIndex; + auto completionHandlerParamTypes = + completionHandlerFunctionType->getParamTypes(); + auto &clangCtx = clangDecl->getASTContext(); + for (unsigned paramIdx : indices(completionHandlerParamTypes)) { + auto paramType = completionHandlerParamTypes[paramIdx]; + + // We are only interested in nullable NSError parameters. + if (!isNullableNSErrorType(clangCtx, paramType)) + continue; + + // If this is the first nullable error parameter, note that. + if (!completionHandlerErrorParamIndex) { + completionHandlerErrorParamIndex = paramIdx; + continue; + } + + // More than one nullable NSError parameter. Don't import as throwing. + completionHandlerErrorParamIndex = None; + break; + } + + // Drop the completion handler parameter name. + paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex); + + // Update the base name, if needed. + if (newBaseName && !hasCustomName) + baseName = *newBaseName; + + return ForeignAsyncConvention( + completionHandlerParamIndex, completionHandlerErrorParamIndex); +} + bool NameImporter::hasErrorMethodNameCollision( const clang::ObjCMethodDecl *method, unsigned paramIndex, StringRef suffixToStrip) { @@ -1359,6 +1527,21 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.hasErrorInfo = true; result.info.errorInfo = *errorInfo; } + + if (version.supportsConcurrency()) { + if (auto asyncInfo = considerAsyncImport( + method, parsedName.BaseName, parsedName.ArgumentLabels, + params, isInitializer, /*hasCustomName=*/true, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + + // Update the name to reflect the new parameter labels. + result.declName = formDeclName( + swiftCtx, parsedName.BaseName, parsedName.ArgumentLabels, + /*isFunction=*/true, isInitializer); + } + } } return result; @@ -1604,6 +1787,16 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.errorInfo = *errorInfo; } + if (version.supportsConcurrency()) { + if (auto asyncInfo = considerAsyncImport( + objcMethod, baseName, argumentNames, params, isInitializer, + /*hasCustomName=*/false, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + } + } + isFunction = true; // Is this one of the accessors for subscripts? @@ -1892,6 +2085,7 @@ bool NameImporter::forEachDistinctImportName( seenNames.push_back(key); activeVersion.forEachOtherImportNameVersion( + swiftCtx.LangOpts.EnableExperimentalConcurrency, [&](ImportNameVersion nameVersion) { // Check to see if the name is different. ImportedName newName = importName(decl, nameVersion); diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index ddb4b0aa4105c..09d90703563c9 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -23,6 +23,7 @@ #include "swift/Basic/Version.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "clang/Sema/Sema.h" @@ -42,15 +43,18 @@ enum { NumImportedAccessorKindBits = 3 }; /// The name version class ImportNameVersion : public RelationalOperationsBase { - unsigned rawValue; + unsigned rawValue : 31; + unsigned concurrency : 1; + friend llvm::DenseMapInfo; enum AsConstExpr_t { AsConstExpr }; - constexpr ImportNameVersion() : rawValue(0) {} + constexpr ImportNameVersion() : rawValue(0), concurrency(false) {} constexpr ImportNameVersion(unsigned version, AsConstExpr_t) - : rawValue(version) {} - explicit ImportNameVersion(unsigned version) : rawValue(version) { + : rawValue(version), concurrency(false) {} + explicit ImportNameVersion(unsigned version, bool concurrency = false) + : rawValue(version), concurrency(concurrency) { assert(version >= 2 && "only Swift 2 and later are supported"); } public: @@ -67,7 +71,7 @@ class ImportNameVersion : public RelationalOperationsBase { return ImportNameVersion::swift4_2(); } unsigned major = version[0]; - return ImportNameVersion(major >= 5 ? major + 1 : major); + return ImportNameVersion(major >= 5 ? major + 1 : major, false); } unsigned majorVersionNumber() const { @@ -89,11 +93,21 @@ class ImportNameVersion : public RelationalOperationsBase { return llvm::VersionTuple(majorVersionNumber(), minorVersionNumber()); } + /// Whether to consider importing functions as 'async'. + bool supportsConcurrency() const { return concurrency; } + + ImportNameVersion withConcurrency(bool concurrency) const { + ImportNameVersion result = *this; + result.concurrency = concurrency; + return result; + } + bool operator==(ImportNameVersion other) const { - return rawValue == other.rawValue; + return rawValue == other.rawValue && concurrency == other.concurrency; } bool operator<(ImportNameVersion other) const { - return rawValue < other.rawValue; + return rawValue < other.rawValue || + (rawValue == other.rawValue && concurrency < other.concurrency); } /// Calls \p action for each name version other than this one, first going @@ -102,10 +116,19 @@ class ImportNameVersion : public RelationalOperationsBase { /// /// This is the most useful order for importing compatibility stubs. void forEachOtherImportNameVersion( + bool withConcurrency, llvm::function_ref action) const { assert(*this >= ImportNameVersion::swift2()); ImportNameVersion nameVersion = *this; + assert(!nameVersion.supportsConcurrency()); + + // If we've been asked to also consider concurrency, do so for the + // primary version (only). + if (withConcurrency) { + action(nameVersion.withConcurrency(true)); + } + while (nameVersion > ImportNameVersion::swift2()) { --nameVersion.rawValue; action(nameVersion); @@ -175,6 +198,10 @@ class ImportedName { /// throwing Swift methods, describes how the mapping is performed. ForeignErrorConvention::Info errorInfo; + /// For names that map Objective-C completion handlers into async + /// Swift methods, describes how the mapping is performed. + ForeignAsyncConvention asyncInfo; + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. unsigned selfIndex; @@ -201,11 +228,13 @@ class ImportedName { unsigned hasErrorInfo : 1; + unsigned hasAsyncInfo : 1; + Info() : errorInfo(), selfIndex(), initKind(CtorInitializerKind::Designated), accessorKind(ImportedAccessorKind::None), hasCustomName(false), droppedVariadic(false), importAsMember(false), hasSelfIndex(false), - hasErrorInfo(false) {} + hasErrorInfo(false), hasAsyncInfo(false) {} } info; public: @@ -239,6 +268,14 @@ class ImportedName { return None; } + /// For names that map Objective-C methods with completion handlers into + /// async Swift methods, describes how the mapping is performed. + Optional getAsyncInfo() const { + if (info.hasAsyncInfo) + return info.asyncInfo; + return None; + } + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. Optional getSelfIndex() const { @@ -416,6 +453,14 @@ class NameImporter { ArrayRef params, bool isInitializer, bool hasCustomName); + Optional + considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, + StringRef &baseName, + SmallVectorImpl ¶mNames, + ArrayRef params, + bool isInitializer, bool hasCustomName, + Optional errorInfo); + EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *, const clang::DeclContext *, ImportNameVersion version); diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 69034c5c3778e..e9e0599b5f8fc 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1988,6 +1988,42 @@ static Type mapGenericArgs(const DeclContext *fromDC, return type.subst(subs); } +/// Decompose the type of a completion handler parameter in a function +/// imported as 'async' and produce the result type of the 'async' function. +static Type decomposeCompletionHandlerType( + Type paramTy, ForeignAsyncConvention info) { + auto fnType = paramTy->lookThroughAllOptionalTypes()->getAs(); + if (!fnType) + return Type(); + + SmallVector resultTypeElts; + auto params = fnType->getParams(); + for (unsigned paramIdx : indices(params)) { + const auto ¶m = params[paramIdx]; + if (param.isInOut() || param.isVariadic()) + return Type(); + + // If there is an error parameter to the completion handler, it is + // not part of the result type of the asynchronous function. + if (info.completionHandlerErrorParamIndex() && + paramIdx == *info.completionHandlerErrorParamIndex()) + continue; + + resultTypeElts.push_back(param.getPlainType()); + } + + switch (resultTypeElts.size()) { + case 0: + return paramTy->getASTContext().getVoidDecl()->getDeclaredInterfaceType(); + + case 1: + return resultTypeElts.front().getType(); + + default: + return TupleType::get(resultTypeElts, paramTy->getASTContext()); + } +} + ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( const DeclContext *dc, const clang::ObjCMethodDecl *clangDecl, ArrayRef params, bool isVariadic, @@ -2024,6 +2060,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( CanType origSwiftResultTy; Optional errorInfo = importedName.getErrorInfo(); + auto asyncInfo = importedName.getAsyncInfo(); OptionalTypeKind OptionalityOfReturn; if (clangDecl->hasAttr()) { OptionalityOfReturn = OTK_None; @@ -2122,6 +2159,9 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( continue; } + bool paramIsCompletionHandler = + asyncInfo && paramIndex == asyncInfo->completionHandlerParamIndex(); + // Import the parameter type into Swift. // Check nullability of the parameter. @@ -2191,6 +2231,21 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( continue; } + // If this is a completion handler, figure out it's effect on the result + // type but don't build it into the parameter type. + if (paramIsCompletionHandler) { + if (Type replacedSwiftResultTy = + decomposeCompletionHandlerType(swiftParamTy, *asyncInfo)) { + swiftResultTy = replacedSwiftResultTy; + + // FIXME: We will need an equivalent to "error parameter is replaced" + // for asynchronous functions. Perhaps add "async: ()"? + continue; + } + + llvm_unreachable("async info computed incorrectly?"); + } + // Map __attribute__((noescape)) to @noescape. bool addNoEscapeAttr = false; if (param->hasAttr()) { diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 3ac0d0fe45d0c..6653b2d0ee38d 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -1857,6 +1857,7 @@ SwiftNameLookupExtension::hashExtension(llvm::hash_code code) const { SWIFT_LOOKUP_TABLE_VERSION_MAJOR, SWIFT_LOOKUP_TABLE_VERSION_MINOR, inferImportAsMember, + swiftCtx.LangOpts.EnableExperimentalConcurrency, version::getSwiftFullVersion()); } diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift new file mode 100644 index 0000000000000..8ec33ae643905 --- /dev/null +++ b/test/ClangImporter/objc_async.swift @@ -0,0 +1,22 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify + +// REQUIRES: objc_interop +import Foundation +import ObjCConcurrency + +func testSlowServer(slowServer: SlowServer) async { + let _: Int = await slowServer.doSomethingSlow("mail") + let _: Bool = await slowServer.checkAvailability() + let _: String = await slowServer.findAnswer() ?? "nope" + let _: String = await slowServer.findAnswerFailingly() ?? "nope" + // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} + // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} +} + +func testSlowServerOldSchool(slowServer: SlowServer) { + var i1: Int = 0 + slowServer.doSomethingSlow("mail") { i in + i1 = i + } + print(i1) +} diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift new file mode 100644 index 0000000000000..6e4539de8825c --- /dev/null +++ b/test/IDE/print_clang_objc_async.swift @@ -0,0 +1,19 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false -enable-experimental-concurrency > %t/ObjCConcurrency.printed.txt +// RUN: %FileCheck -input-file %t/ObjCConcurrency.printed.txt %s + +// REQUIRES: objc_interop + +// CHECK-LABEL: class SlowServer : NSObject { +// CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) +// CHECK-DAG: func doSomethingSlow(_ operation: String) async -> Int +// CHECK-DAG: func doSomethingDangerous(_ operation: String, completionHandler handler: ((String?, Error?) -> Void)? = nil) +// CHECK-DAG: func doSomethingDangerous(_ operation: String) async throws -> String? +// CHECK-DAG: func checkAvailability(completionHandler: @escaping (Bool) -> Void) +// CHECK-DAG: func checkAvailability() async -> Bool +// CHECK-DAG: func findAnswer(completionHandler handler: @escaping (String?, Error?) -> Void) +// CHECK-DAG: func findAnswer() async throws -> String? +// CHECK-DAG: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws +// CHECK-DAG: func findAnswerFailingly() async throws -> String? +// CHECK: {{^[}]$}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h new file mode 100644 index 0000000000000..9903bd0a91a64 --- /dev/null +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -0,0 +1,14 @@ +@import Foundation; +@import ctypes; + +#pragma clang assume_nonnull begin + +@interface SlowServer : NSObject +-(void)doSomethingSlow:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; +-(void)doSomethingDangerous:(NSString *)operation completionHandler:(void (^ _Nullable)(NSString *_Nullable, NSError * _Nullable))handler; +-(void)checkAvailabilityWithCompletionHandler:(void (^)(BOOL isAvailable))completionHandler; +-(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); +-(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); +@end + +#pragma clang assume_nonnull end diff --git a/test/Inputs/clang-importer-sdk/usr/include/module.map b/test/Inputs/clang-importer-sdk/usr/include/module.map index 61b062ab524eb..366101cbbb723 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/module.map +++ b/test/Inputs/clang-importer-sdk/usr/include/module.map @@ -136,3 +136,8 @@ module WinBOOL { header "winbool.h" export * } + +module ObjCConcurrency { + header "ObjCConcurrency.h" + export * +} \ No newline at end of file diff --git a/utils/swift-api-dump.py b/utils/swift-api-dump.py index 9022e9c89d5b2..d44b33ae19839 100755 --- a/utils/swift-api-dump.py +++ b/utils/swift-api-dump.py @@ -102,6 +102,8 @@ def create_parser(): parser.add_argument('--enable-infer-import-as-member', action='store_true', help='Infer when a global could be imported as a ' + 'member.') + parser.add_argument('--enable-experimental-concurrency', action='store_true', + help='Enable experimental concurrency model.') parser.add_argument('-swift-version', metavar='N', help='the Swift version to use') parser.add_argument('-show-overlay', action='store_true', @@ -328,6 +330,8 @@ def main(): extra_args = ['-skip-imports'] if args.enable_infer_import_as_member: extra_args = extra_args + ['-enable-infer-import-as-member'] + if args.enable_experimental_concurrency: + extra_args = extra_args + ['-enable-experimental-concurrency'] if args.swift_version: extra_args = extra_args + ['-swift-version', '%s' % args.swift_version] From 1132cda811369be9fa6d8d43d0ddbf0d00fd6fac Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 27 Aug 2020 23:00:48 -0700 Subject: [PATCH 101/120] [ownership] Move SemanticARCOpts into a separate folder in preparation for splitting into multiple small pseudo-passes. SemanticARCOpts keeps on growing with various optimizations attached to a single "optimization" manager. Move it to its own folder in prepation for splitting it into multiple different optimizations and utility files. --- lib/SILOptimizer/CMakeLists.txt | 1 + lib/SILOptimizer/SemanticARC/CMakeLists.txt | 2 + .../SemanticARCOpts.cpp | 43 ++++++++----------- lib/SILOptimizer/Transforms/CMakeLists.txt | 1 - 4 files changed, 22 insertions(+), 25 deletions(-) create mode 100644 lib/SILOptimizer/SemanticARC/CMakeLists.txt rename lib/SILOptimizer/{Transforms => SemanticARC}/SemanticARCOpts.cpp (99%) diff --git a/lib/SILOptimizer/CMakeLists.txt b/lib/SILOptimizer/CMakeLists.txt index 4bf45e0ebd8db..ec1440d2b06a1 100644 --- a/lib/SILOptimizer/CMakeLists.txt +++ b/lib/SILOptimizer/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(LoopTransforms) add_subdirectory(Mandatory) add_subdirectory(PassManager) add_subdirectory(SILCombiner) +add_subdirectory(SemanticARC) add_subdirectory(Transforms) add_subdirectory(UtilityPasses) add_subdirectory(Utils) diff --git a/lib/SILOptimizer/SemanticARC/CMakeLists.txt b/lib/SILOptimizer/SemanticARC/CMakeLists.txt new file mode 100644 index 0000000000000..7ae8f36a2bbde --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(swiftSILOptimizer PRIVATE + SemanticARCOpts.cpp) diff --git a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp similarity index 99% rename from lib/SILOptimizer/Transforms/SemanticARCOpts.cpp rename to lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 743dac16eaa6b..180f3882a7807 100644 --- a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -1034,8 +1034,8 @@ struct SemanticARCOptVisitor } // end anonymous namespace static llvm::cl::opt -VerifyAfterTransform("sil-semantic-arc-opts-verify-after-transform", - llvm::cl::init(false), llvm::cl::Hidden); + VerifyAfterTransform("sil-semantic-arc-opts-verify-after-transform", + llvm::cl::init(false), llvm::cl::Hidden); static bool canEliminatePhi( SemanticARCOptVisitor::FrozenMultiMapRange optimizableIntroducerRange, @@ -1436,7 +1436,8 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { // are within the borrow scope. // // TODO: This needs a better name. -bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst *cvi) { +bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization( + CopyValueInst *cvi) { // For now, do not run this optimization. This is just to be careful. if (onlyGuaranteedOpts) return false; @@ -1617,7 +1618,8 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst /// If cvi only has destroy value users, then cvi is a dead live range. Lets /// eliminate all such dead live ranges. -bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) { +bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue( + CopyValueInst *cvi) { // This is a cheap optimization generally. // See if we are lucky and have a simple case. @@ -1857,8 +1859,7 @@ namespace { /// written to again. In both cases, we can convert load [copy] -> load_borrow /// safely. class StorageGuaranteesLoadVisitor - : public AccessUseDefChainVisitor -{ + : public AccessUseDefChainVisitor { // The outer SemanticARCOptVisitor. SemanticARCOptVisitor &ARCOpt; @@ -1867,7 +1868,7 @@ class StorageGuaranteesLoadVisitor // The current address being visited. SILValue currentAddress; - + Optional isWritten; public: @@ -1880,11 +1881,9 @@ class StorageGuaranteesLoadVisitor currentAddress = nullptr; isWritten = written; } - - void next(SILValue address) { - currentAddress = address; - } - + + void next(SILValue address) { currentAddress = address; } + void visitNestedAccess(BeginAccessInst *access) { // First see if we have read/modify. If we do not, just look through the // nested access. @@ -1901,9 +1900,7 @@ class StorageGuaranteesLoadVisitor // scope. If so, we may be able to use a load_borrow here! SmallVector endScopeUses; transform(access->getEndAccesses(), std::back_inserter(endScopeUses), - [](EndAccessInst *eai) { - return &eai->getAllOperands()[0]; - }); + [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; }); SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); if (!checker.validateLifetime(access, endScopeUses, @@ -1930,7 +1927,7 @@ class StorageGuaranteesLoadVisitor return answer(true); } - + void visitArgumentAccess(SILFunctionArgument *arg) { // If this load_copy is from an indirect in_guaranteed argument, then we // know for sure that it will never be written to. @@ -2007,15 +2004,15 @@ class StorageGuaranteesLoadVisitor // able to also to promote load [copy] from such args to load_borrow. return answer(true); } - + void visitGlobalAccess(SILValue global) { return answer(!AccessedStorage(global, AccessedStorage::Global) - .isLetAccess(&ARCOpt.F)); + .isLetAccess(&ARCOpt.F)); } - + void visitClassAccess(RefElementAddrInst *field) { currentAddress = nullptr; - + // We know a let property won't be written to if the base object is // guaranteed for the duration of the access. // For non-let properties conservatively assume they may be written to. @@ -2071,15 +2068,13 @@ class StorageGuaranteesLoadVisitor baseObject, endScopeInsts, liveRange.getAllConsumingUses()); return answer(foundError); } - + // TODO: Handle other access kinds? void visitBase(SILValue base, AccessedStorage::Kind kind) { return answer(true); } - void visitNonAccess(SILValue addr) { - return answer(true); - } + void visitNonAccess(SILValue addr) { return answer(true); } void visitCast(SingleValueInstruction *cast, Operand *parentAddr) { return next(parentAddr->get()); diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index c51043a1910c3..ac3718b6cf20f 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -31,7 +31,6 @@ target_sources(swiftSILOptimizer PRIVATE RedundantLoadElimination.cpp RedundantOverflowCheckRemoval.cpp ReleaseDevirtualizer.cpp - SemanticARCOpts.cpp SILCodeMotion.cpp SILLowerAggregateInstrs.cpp SILMem2Reg.cpp From 109b7b8a9b854d7482ecc14aecdda673a7ea5a4e Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Fri, 28 Aug 2020 00:09:56 -0700 Subject: [PATCH 102/120] Disable LLDB tests in nightly package preset (67923799) --- utils/build-presets.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 3a0e27830069e..0892aee12e7c8 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1313,6 +1313,8 @@ dash-dash no-assertions +# SKIP LLDB TESTS (67923799) +skip-test-lldb [preset: buildbot_osx_package,no_assertions,lto] mixin-preset=buildbot_osx_package,no_assertions From c98d89543cf8732e449a47fdb87b864b7b867584 Mon Sep 17 00:00:00 2001 From: tbkka Date: Fri, 28 Aug 2020 08:44:02 -0700 Subject: [PATCH 103/120] Expanded discussion of protocol types (#33621) * AnyObject behaves as if every class type implicitly conformed to it as a protocol. * Protocols "inherit" from other protocols, and this has implications for casting to existential metatypes I also moved the Any self-conformance invariant to the self-conformance subsection and added a couple of explanatory sentences. --- docs/DynamicCasting.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/DynamicCasting.md b/docs/DynamicCasting.md index 1ef306549a148..72b369bbb3c7c 100644 --- a/docs/DynamicCasting.md +++ b/docs/DynamicCasting.md @@ -407,9 +407,10 @@ S.self.svar // 2 ``` Invariants -* If `T` conforms to `P` and `t` is an instance of `T`, then `t is P`, and `T.self is P.Type` +* If `T` conforms to `P` and `t` is an instance of `T`, then `t is P` and `T.self is P.Type` +* If `P` is a sub-protocol of `P1` and `T` is any type, then `T.self is P.Type` implies that `T.self is P1.Type` * Since every type `T` conforms to `Any`, `T.self is Any.Type` is always true -* `Any` self-conforms: `Any.self is Any.Type == true` +* Since every class type `C` conforms to `AnyObject`, `C.self is AnyObject.Type` is always true (this includes Objective-C class types) ### Note: "Self conforming" protocols @@ -439,10 +440,14 @@ let b : MyGenericType(a) As above, since `a` has type `P`, this code is instantiating `MyGenericType` with `T = P`, which is only valid if `P` conforms to `P`. Note that any protocol that specifies static methods, static properties, associated types, or initializers cannot possibly be self-conforming. -As of Swift 5.3, there are only three kinds of self-conforming protocols: -* `Any` must be self-conforming since every `T.self` is an instance of `Any.Type` -* `Error` is a self-conforming protocol -* Objective-C protocols that have no static requirements are self-conforming +As of Swift 5.3, the only self-conforming protocols are `Any`, `Error`, and Objective-C protocols that have no static requirements. + +Invariants +* `Any` self-conforms: `Any.self is Any.Type == true` +* `Error` self-conforms: `Error.self is Error.Type == true` +* If `P` self-conforms and is a sub-protocol of `P1`, then `P.self is P1.Type == true` + +For example, the last invariant here implies that for any Objective-C protocol `OP` that has no static requirements, `OP.self is AnyObject.Type`. This follows from the fact that `OP` self-conforms and that every Objective-C protocol has `AnyObject` as an implicit parent protocol. ## CoreFoundation types From fd6922f92dc2a0cd6f47da0969533d30f89b186c Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Wed, 19 Aug 2020 12:30:34 -0400 Subject: [PATCH 104/120] Add error reporting when looking up types by demangled name. --- include/swift/Demangling/TypeDecoder.h | 306 ++++++++++-------- include/swift/Demangling/TypeLookupError.h | 198 ++++++++++++ include/swift/Reflection/TypeRefBuilder.h | 4 +- include/swift/Remote/MetadataReader.h | 11 +- include/swift/Runtime/Debug.h | 34 -- include/swift/Runtime/Portability.h | 39 +++ lib/AST/ASTDemangler.cpp | 11 +- lib/RemoteAST/RemoteAST.cpp | 7 +- stdlib/public/Reflection/TypeRefBuilder.cpp | 24 +- .../SwiftRemoteMirror/SwiftRemoteMirror.cpp | 2 +- .../public/runtime/CompatibilityOverride.def | 4 +- stdlib/public/runtime/Metadata.cpp | 61 ++-- stdlib/public/runtime/MetadataLookup.cpp | 255 +++++++++------ stdlib/public/runtime/Private.h | 21 +- stdlib/public/runtime/ProtocolConformance.cpp | 84 ++--- stdlib/public/runtime/ReflectionMirror.mm | 36 ++- stdlib/public/runtime/SwiftObject.mm | 3 +- stdlib/public/stubs/Assert.cpp | 1 + .../swift-reflection-dump.cpp | 11 +- unittests/runtime/CompatibilityOverride.cpp | 8 +- 20 files changed, 743 insertions(+), 377 deletions(-) create mode 100644 include/swift/Demangling/TypeLookupError.h diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index e591e978dccd1..da2ec53c6ccd4 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -18,10 +18,12 @@ #ifndef SWIFT_DEMANGLING_TYPEDECODER_H #define SWIFT_DEMANGLING_TYPEDECODER_H +#include "TypeLookupError.h" #include "swift/ABI/MetadataValues.h" +#include "swift/Basic/LLVM.h" #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" -#include "swift/Basic/LLVM.h" +#include "swift/Runtime/Portability.h" #include "swift/Runtime/Unreachable.h" #include "swift/Strings.h" #include "llvm/ADT/ArrayRef.h" @@ -317,6 +319,14 @@ getObjCClassOrProtocolName(NodePointer node) { } #endif +#define MAKE_NODE_TYPE_ERROR(Node, Fmt, ...) \ + TypeLookupError("TypeDecoder.h:%d: Node kind %u \"%.*s\" - " Fmt, __LINE__, \ + Node->getKind(), \ + Node->hasText() ? (int)Node->getText().size() : 0, \ + Node->hasText() ? Node->getText().data() : "", __VA_ARGS__) + +#define MAKE_NODE_TYPE_ERROR0(Node, Str) MAKE_NODE_TYPE_ERROR(Node, "%s", Str) + /// Decode a mangled type to construct an abstract type, forming such /// types by invoking a custom builder. template @@ -333,24 +343,25 @@ class TypeDecoder { : Builder(Builder) {} /// Given a demangle tree, attempt to turn it into a type. - BuiltType decodeMangledType(NodePointer Node) { - if (!Node) return BuiltType(); + TypeLookupErrorOr decodeMangledType(NodePointer Node) { + if (!Node) + return TypeLookupError("Node is NULL"); using NodeKind = Demangle::Node::Kind; switch (Node->getKind()) { case NodeKind::Global: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::TypeMangling: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::Type: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::Class: @@ -369,8 +380,8 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (!decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) - return BuiltType(); + if (auto error = decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) + return *error; if (typeAlias) return Builder.createTypeAliasType(typeDecl, parent); @@ -384,19 +395,21 @@ class TypeDecoder { case NodeKind::BoundGenericTypeAlias: case NodeKind::BoundGenericOtherNominalType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); llvm::SmallVector args; const auto &genericArgs = Node->getChild(1); if (genericArgs->getKind() != NodeKind::TypeList) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(genericArgs, "is not TypeList"); for (auto genericArg : *genericArgs) { auto paramType = decodeMangledType(genericArg); - if (!paramType) - return BuiltType(); - args.push_back(paramType); + if (paramType.isError()) + return paramType; + args.push_back(paramType.getType()); } auto ChildNode = Node->getChild(0); @@ -413,9 +426,9 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (!decodeMangledTypeDecl(ChildNode, typeDecl, - parent, typeAlias)) - return BuiltType(); + if (auto error = + decodeMangledTypeDecl(ChildNode, typeDecl, parent, typeAlias)) + return *error; return Builder.createBoundGenericType(typeDecl, args, parent); } @@ -445,11 +458,15 @@ class TypeDecoder { // But when resolving it to a type, we want to *keep* the argument // so that the parent type becomes 'S' and not 'P'. if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); const auto &genericArgs = Node->getChild(1); if (genericArgs->getNumChildren() != 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(genericArgs, + "expected 1 generic argument, saw %u", + genericArgs->getNumChildren()); return decodeMangledType(genericArgs->getChild(0)); } @@ -469,7 +486,7 @@ class TypeDecoder { auto reprNode = Node->getChild(i++); if (reprNode->getKind() != NodeKind::MetatypeRepresentation || !reprNode->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(reprNode, "wrong node kind or no text"); if (reprNode->getText() == "@thin") repr = ImplMetatypeRepresentation::Thin; else if (reprNode->getText() == "@thick") @@ -477,26 +494,28 @@ class TypeDecoder { else if (reprNode->getText() == "@objc_metatype") repr = ImplMetatypeRepresentation::ObjC; } else if (Node->getNumChildren() < 1) { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); } auto instance = decodeMangledType(Node->getChild(i)); - if (!instance) - return BuiltType(); + if (instance.isError()) + return instance; if (Node->getKind() == NodeKind::Metatype) { - return Builder.createMetatypeType(instance, repr); + return Builder.createMetatypeType(instance.getType(), repr); } else if (Node->getKind() == NodeKind::ExistentialMetatype) { - return Builder.createExistentialMetatypeType(instance, repr); + return Builder.createExistentialMetatypeType(instance.getType(), repr); } else { assert(false); - return nullptr; + return MAKE_NODE_TYPE_ERROR0(Node, + "Metatype/ExistentialMetatype Node " + "had a different kind when re-checked"); } } case NodeKind::ProtocolList: case NodeKind::ProtocolListWithAnyObject: case NodeKind::ProtocolListWithClass: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); // Find the protocol list. llvm::SmallVector Protocols; @@ -511,7 +530,8 @@ class TypeDecoder { if (auto Protocol = decodeMangledProtocolType(componentType)) Protocols.push_back(Protocol); else - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(componentType, + "failed to decode protocol type"); } // Superclass or AnyObject, if present. @@ -519,11 +539,15 @@ class TypeDecoder { auto Superclass = BuiltType(); if (Node->getKind() == NodeKind::ProtocolListWithClass) { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto superclassNode = Node->getChild(1); - Superclass = decodeMangledType(superclassNode); - if (!Superclass) return BuiltType(); + auto result = decodeMangledType(superclassNode); + if (result.isError()) + return result; + Superclass = result.getType(); IsClassBound = true; } else if (Node->getKind() == NodeKind::ProtocolListWithAnyObject) { @@ -541,17 +565,18 @@ class TypeDecoder { /*IsClassBound=*/false); } - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "failed to decode protocol type"); } case NodeKind::DynamicSelf: { if (Node->getNumChildren() != 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, "expected 1 child, saw %u", + Node->getNumChildren()); auto selfType = decodeMangledType(Node->getChild(0)); - if (!selfType) - return BuiltType(); + if (selfType.isError()) + return selfType; - return Builder.createDynamicSelfType(selfType); + return Builder.createDynamicSelfType(selfType.getType()); } case NodeKind::DependentGenericParamType: { auto depth = Node->getChild(0)->getIndex(); @@ -571,7 +596,9 @@ class TypeDecoder { case NodeKind::EscapingLinearFunctionType: case NodeKind::FunctionType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); FunctionTypeFlags flags; if (Node->getKind() == NodeKind::ObjCBlock || @@ -611,13 +638,16 @@ class TypeDecoder { flags = flags.withAsync(isAsync).withThrows(isThrow); if (Node->getNumChildren() < firstChildIdx + 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (%u)", + Node->getNumChildren(), firstChildIdx + 2); bool hasParamFlags = false; llvm::SmallVector, 8> parameters; if (!decodeMangledFunctionInputType(Node->getChild(firstChildIdx), parameters, hasParamFlags)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node->getChild(firstChildIdx), + "failed to decode function type"); flags = flags.withNumParameters(parameters.size()) .withParameterFlags(hasParamFlags) @@ -631,8 +661,9 @@ class TypeDecoder { NodeKind::EscapingLinearFunctionType); auto result = decodeMangledType(Node->getChild(firstChildIdx+1)); - if (!result) return BuiltType(); - return Builder.createFunctionType(parameters, result, flags); + if (result.isError()) + return result; + return Builder.createFunctionType(parameters, result.getType(), flags); } case NodeKind::ImplFunctionType: { auto calleeConvention = ImplParameterConvention::Direct_Unowned; @@ -646,7 +677,7 @@ class TypeDecoder { if (child->getKind() == NodeKind::ImplConvention) { if (!child->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); if (child->getText() == "@convention(thin)") { flags = @@ -656,7 +687,7 @@ class TypeDecoder { } } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { if (!child->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); StringRef text = child->getText(); if (text == "@convention(c)") { @@ -676,15 +707,18 @@ class TypeDecoder { flags = flags.withEscaping(); } else if (child->getKind() == NodeKind::ImplParameter) { if (decodeImplFunctionParam(child, parameters)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplResult) { if (decodeImplFunctionParam(child, results)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplErrorResult) { if (decodeImplFunctionPart(child, errorResults)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function part"); } else { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "unexpected kind"); } } @@ -696,7 +730,8 @@ class TypeDecoder { errorResult = errorResults.front(); break; default: - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, "got %zu errors", + errorResults.size()); } // TODO: Some cases not handled above, but *probably* they cannot @@ -711,13 +746,13 @@ class TypeDecoder { case NodeKind::ArgumentTuple: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); return decodeMangledType(Node->getChild(0)); case NodeKind::ReturnType: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); return decodeMangledType(Node->getChild(0)); @@ -726,12 +761,13 @@ class TypeDecoder { std::string labels; for (auto &element : *Node) { if (element->getKind() != NodeKind::TupleElement) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind"); // If the tuple element is labeled, add its label to 'labels'. unsigned typeChildIndex = 0; if (element->getChild(typeChildIndex)->getKind() == NodeKind::VariadicMarker) { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(element->getChild(typeChildIndex), + "no children"); } if (element->getChild(typeChildIndex)->getKind() == NodeKind::TupleElementName) { // Add spaces to terminate all the previous labels if this @@ -749,22 +785,23 @@ class TypeDecoder { } // Decode the element type. - BuiltType elementType = - decodeMangledType(element->getChild(typeChildIndex)); - if (!elementType) - return BuiltType(); + auto elementType = decodeMangledType(element->getChild(typeChildIndex)); + if (elementType.isError()) + return elementType; - elements.push_back(elementType); + elements.push_back(elementType.getType()); } return Builder.createTupleType(elements, std::move(labels)); } case NodeKind::TupleElement: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); if (Node->getChild(0)->getKind() == NodeKind::TupleElementName) { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } @@ -772,68 +809,75 @@ class TypeDecoder { case NodeKind::DependentGenericType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } case NodeKind::DependentMemberType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; auto assocTypeChild = Node->getChild(1); auto member = assocTypeChild->getFirstChild()->getText(); if (assocTypeChild->getNumChildren() < 2) - return Builder.createDependentMemberType(member.str(), base); + return Builder.createDependentMemberType(member.str(), base.getType()); auto protocol = decodeMangledProtocolType(assocTypeChild->getChild(1)); if (!protocol) return BuiltType(); - return Builder.createDependentMemberType(member.str(), base, protocol); + return Builder.createDependentMemberType(member.str(), base.getType(), + protocol); } case NodeKind::DependentAssociatedTypeRef: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } case NodeKind::Unowned: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createUnownedStorageType(base); + if (base.isError()) + return base; + return Builder.createUnownedStorageType(base.getType()); } case NodeKind::Unmanaged: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createUnmanagedStorageType(base); + if (base.isError()) + return base; + return Builder.createUnmanagedStorageType(base.getType()); } case NodeKind::Weak: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createWeakStorageType(base); + if (base.isError()) + return base; + return Builder.createWeakStorageType(base.getType()); } case NodeKind::SILBoxType: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createSILBoxType(base); + if (base.isError()) + return base; + return Builder.createSILBoxType(base.getType()); } case NodeKind::SILBoxTypeWithLayout: { // TODO: Implement SILBoxTypeRefs with layout. As a stopgap, specify the @@ -842,57 +886,62 @@ class TypeDecoder { } case NodeKind::SugaredOptional: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createOptionalType(base); + return Builder.createOptionalType(base.getType()); } case NodeKind::SugaredArray: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createArrayType(base); + return Builder.createArrayType(base.getType()); } case NodeKind::SugaredDictionary: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto key = decodeMangledType(Node->getChild(0)); - if (!key) - return BuiltType(); + if (key.isError()) + return key; auto value = decodeMangledType(Node->getChild(1)); - if (!key) - return BuiltType(); + if (value.isError()) + return value; - return Builder.createDictionaryType(key, value); + return Builder.createDictionaryType(key.getType(), value.getType()); } case NodeKind::SugaredParen: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createParenType(base); + return Builder.createParenType(base.getType()); } case NodeKind::OpaqueType: { if (Node->getNumChildren() < 3) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (3)", + Node->getNumChildren()); auto descriptor = Node->getChild(0); auto ordinalNode = Node->getChild(1); if (ordinalNode->getKind() != NodeKind::Index || !ordinalNode->hasIndex()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(ordinalNode, + "unexpected kind or no index"); auto ordinal = ordinalNode->getIndex(); std::vector genericArgsBuf; @@ -905,9 +954,9 @@ class TypeDecoder { break; for (auto argNode : *genericsNode) { auto arg = decodeMangledType(argNode); - if (!arg) - return BuiltType(); - genericArgsBuf.push_back(arg); + if (arg.isError()) + return arg; + genericArgsBuf.push_back(arg.getType()); } } genericArgsLevels.push_back(genericArgsBuf.size()); @@ -923,7 +972,7 @@ class TypeDecoder { // TODO: Handle OpaqueReturnType, when we're in the middle of reconstructing // the defining decl default: - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind"); } } @@ -943,11 +992,11 @@ class TypeDecoder { T::getConventionFromString(conventionString); if (!convention) return true; - BuiltType type = decodeMangledType(node->getChild(1)); - if (!type) + auto type = decodeMangledType(node->getChild(1)); + if (type.isError()) return true; - results.emplace_back(type, *convention); + results.emplace_back(type.getType(), *convention); return false; } @@ -968,8 +1017,8 @@ class TypeDecoder { auto convention = T::getConventionFromString(conventionString); if (!convention) return true; - BuiltType type = decodeMangledType(typeNode); - if (!type) + auto result = decodeMangledType(typeNode); + if (result.isError()) return true; auto diffKind = T::DifferentiabilityType::DifferentiableOrNotApplicable; @@ -984,14 +1033,13 @@ class TypeDecoder { diffKind = *optDiffKind; } - results.emplace_back(type, *convention, diffKind); + results.emplace_back(result.getType(), *convention, diffKind); return false; } - bool decodeMangledTypeDecl(Demangle::NodePointer node, - BuiltTypeDecl &typeDecl, - BuiltType &parent, - bool &typeAlias) { + llvm::Optional + decodeMangledTypeDecl(Demangle::NodePointer node, BuiltTypeDecl &typeDecl, + BuiltType &parent, bool &typeAlias) { if (node->getKind() == NodeKind::Type) return decodeMangledTypeDecl(node->getChild(0), typeDecl, parent, typeAlias); @@ -1002,7 +1050,9 @@ class TypeDecoder { declNode = node; } else { if (node->getNumChildren() < 2) - return false; + return MAKE_NODE_TYPE_ERROR( + node, "Number of node children (%u) less than required (2)", + node->getNumChildren()); auto parentContext = node->getChild(0); @@ -1018,11 +1068,14 @@ class TypeDecoder { case Node::Kind::Extension: // Decode the type being extended. if (parentContext->getNumChildren() < 2) - return false; + return MAKE_NODE_TYPE_ERROR(parentContext, + "Number of parentContext children (%u) " + "less than required (2)", + node->getNumChildren()); parentContext = parentContext->getChild(1); LLVM_FALLTHROUGH; default: - parent = decodeMangledType(parentContext); + parent = decodeMangledType(parentContext).getType(); // Remove any generic arguments from the context node, producing a // node that references the nominal type declaration. declNode = Demangle::getUnspecialized(node, Builder.getNodeFactory()); @@ -1030,9 +1083,10 @@ class TypeDecoder { } } typeDecl = Builder.createTypeDecl(declNode, typeAlias); - if (!typeDecl) return false; + if (!typeDecl) + return TypeLookupError("Failed to create type decl"); - return true; + return llvm::None; } BuiltProtocolDecl decodeMangledProtocolType(Demangle::NodePointer node) { @@ -1097,10 +1151,10 @@ class TypeDecoder { } auto paramType = decodeMangledType(node); - if (!paramType) + if (paramType.isError()) return false; - param.setType(paramType); + param.setType(paramType.getType()); return true; }; @@ -1158,14 +1212,12 @@ class TypeDecoder { } }; -template -inline typename BuilderType::BuiltType -decodeMangledType(BuilderType &Builder, - NodePointer Node) { +template +inline TypeLookupErrorOr +decodeMangledType(BuilderType &Builder, NodePointer Node) { return TypeDecoder(Builder).decodeMangledType(Node); } - SWIFT_END_INLINE_NAMESPACE } // end namespace Demangle } // end namespace swift diff --git a/include/swift/Demangling/TypeLookupError.h b/include/swift/Demangling/TypeLookupError.h new file mode 100644 index 0000000000000..c67fc35598be8 --- /dev/null +++ b/include/swift/Demangling/TypeLookupError.h @@ -0,0 +1,198 @@ +//===--- TypeLookupError.h - Type lookup error value. -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Provides the TypeLookupError class, which represents errors when demangling +// or looking up types. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DEMANGLING_TypeLookupError_H +#define SWIFT_DEMANGLING_TypeLookupError_H + +#include "swift/Basic/TaggedUnion.h" +#include "swift/Runtime/Portability.h" + +namespace swift { + +/// An error that occurred while looking up a type at runtime from a mangled +/// name. +/// /// +/// This ultimately just provides a string, but is built to take up minimal +/// space when passed around, and perform as much work lazily as possible. We +/// don't want to spend a lot of time building strings when the caller is going +/// to handle the error gracefully and try a fallback. We only want to waste +/// time/space on that when the error message is actually relevant, so build it +/// as late as possible. +/// /// +/// To be as compact as possible, this type holds a context pointer and a +/// callback function. The callback function uses the context pointer to return +/// an error string when requested. The callback function also does quadruple +/// duty to copy/destroy the context as needed, and free the returned error +/// string if needed. Commands are passed to the callback to request the +/// different operations, which means we only have to store one function pointer +/// instead of four. +class TypeLookupError { +public: + /// The commands that can be passed to the callback function. + enum class Command { + /// Return the error string to the caller, as a char *. + CopyErrorString, + + /// Destroy the error string returned from CopyErrorString, if necessary. + /// The return value is ignored. + DestroyErrorString, + + /// Return a copy of the context pointer (used for copying TypeLookupError + /// objects.) + CopyContext, + + /// Destroy the context pointer. The return value is ignored. + DestroyContext, + }; + + /// The callback used to respond to the commands. The parameters are: + /// - `context`: the context that the value was initialized with, or the + /// context returned from a CopyContext call + /// - `command`: the command to respond to + /// - `param`: when `command` is `DestroyErrorString`, the string pointer to + /// destroy, otherwise NULL + using Callback = void *(*)(void *context, Command command, void *param); + +private: + void *Context; + Callback Fn; + + /// A no-op callback used to avoid a bunch of `if (Fn)` checks. + static void *nop(void *context, Command command, void *param) { + return nullptr; + } + + /// Helper functions for getting a C string from a lambda. These allow us to + /// wrap lambdas returning `char *` or `std::string` and standardize them on + /// `char *`. + static char *getCString(char *str) { return str; } + + static char *getCString(const std::string &str) { + return strdup(str.c_str()); + } + +public: + TypeLookupError(const TypeLookupError &other) { + Fn = other.Fn; + Context = other.Fn(other.Context, Command::CopyContext, nullptr); + } + + TypeLookupError(TypeLookupError &&other) { + Fn = other.Fn; + Context = other.Context; + + other.Fn = nop; + other.Context = nullptr; + } + + ~TypeLookupError() { Fn(Context, Command::DestroyContext, nullptr); } + + TypeLookupError(void *context, Callback fn) : Context(context), Fn(fn ? fn : nop) {} + + TypeLookupError &operator=(const TypeLookupError &other) { + if (this == &other) + return *this; + + Fn(Context, Command::DestroyContext, nullptr); + Fn = other.Fn; + Context = Fn(Context, Command::CopyContext, nullptr); + + return *this; + } + + /// Construct a TypeLookupError that just returns a constant C string. + TypeLookupError(const char *str) + : TypeLookupError([=] { return const_cast(str); }) {} + + /// Construct a TypeLookupError that creates a string using asprintf. The passed-in + /// format string and arguments are passed directly to swift_asprintf when + /// the string is requested. The arguments are captured and the string is only + /// formatted when needed. + template + TypeLookupError(const char *fmt, Args... args) + : TypeLookupError([=] { + char *str; + swift_asprintf(&str, fmt, args...); + return str; + }) {} + + /// Construct a TypeLookupError that wraps a function returning a string. The + /// passed-in function can return either a `std::string` or `char *`. If it + /// returns `char *` then the string will be destroyed with `free()`. + template TypeLookupError(const F &fn) { + Context = new F(fn); + Fn = [](void *context, Command command, void *param) -> void * { + auto castContext = reinterpret_cast(context); + switch (command) { + case Command::CopyErrorString: { + return TypeLookupError::getCString((*castContext)()); + } + case Command::DestroyErrorString: + free(param); + return nullptr; + case Command::CopyContext: + return new F(*castContext); + case Command::DestroyContext: + delete castContext; + return nullptr; + } + }; + } + + /// Get the error string from the error value. The value must be passed to + /// `freeErrorString` when done. (Unless you're just calling a `fatalError` + /// in which case there's no point.) + char *copyErrorString() { + return reinterpret_cast( + Fn(Context, Command::CopyErrorString, nullptr)); + } + + /// Free an error string previously obtained from `copyErrorString`. + void freeErrorString(char *str) { + Fn(Context, Command::DestroyErrorString, str); + } +}; + +/// A value that's either a `TypeLookupError` or some parameterized type value `T`. A +/// convenience wrapper around `TaggedUnion`. +template class TypeLookupErrorOr { + TaggedUnion Value; + +public: + TypeLookupErrorOr(const T &t) : Value(t) { + if (!t) + Value = TypeLookupError("unknown error"); + } + + TypeLookupErrorOr(const TypeLookupError &te) : Value(te) {} + + T getType() { + if (auto *ptr = Value.template dyn_cast()) + return *ptr; + return T(); + } + + TypeLookupError *getError() { + return Value.template dyn_cast(); + } + + bool isError() { return getError() != nullptr; } +}; + +} // namespace swift + +#endif // SWIFT_DEMANGLING_TypeLookupError_H diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index f375918a5ff5d..3914810cbfd8f 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -618,8 +618,8 @@ class TypeRefBuilder { }), OpaqueUnderlyingTypeReader( [&reader](uint64_t descriptorAddr, unsigned ordinal) -> const TypeRef* { - return reader.readUnderlyingTypeForOpaqueTypeDescriptor(descriptorAddr, - ordinal); + return reader.readUnderlyingTypeForOpaqueTypeDescriptor( + descriptorAddr, ordinal).getType(); }) {} diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 4450066283afc..0090ae0746ed6 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -463,7 +463,8 @@ class MetadataReader { } /// Given a demangle tree, attempt to turn it into a type. - BuiltType decodeMangledType(NodePointer Node) { + TypeLookupErrorOr + decodeMangledType(NodePointer Node) { return swift::Demangle::decodeMangledType(Builder, Node); } @@ -925,8 +926,8 @@ class MetadataReader { swift_runtime_unreachable("Unhandled MetadataKind in switch"); } - BuiltType readTypeFromMangledName(const char *MangledTypeName, - size_t Length) { + TypeLookupErrorOr + readTypeFromMangledName(const char *MangledTypeName, size_t Length) { Demangle::Demangler Dem; Demangle::NodePointer Demangled = Dem.demangleSymbol(StringRef(MangledTypeName, Length)); @@ -1183,14 +1184,14 @@ class MetadataReader { MangledNameKind::Type, Dem); } - BuiltType + TypeLookupErrorOr readUnderlyingTypeForOpaqueTypeDescriptor(StoredPointer contextAddr, unsigned ordinal) { Demangle::Demangler Dem; auto node = readUnderlyingTypeManglingForOpaqueTypeDescriptor(contextAddr, ordinal, Dem); if (!node) - return BuiltType(); + return TypeLookupError("Failed to read type mangling for descriptor."); return decodeMangledType(node); } diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 86e19642e51a7..f14e8d16f1554 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -21,7 +21,6 @@ #include "swift/Runtime/Unreachable.h" #include #include -#include #include #include @@ -248,39 +247,6 @@ std::atomic _swift_debug_metadataAllocationBacktraceList; SWIFT_RUNTIME_STDLIB_SPI const void * const _swift_debug_protocolConformanceStatePointer; -SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE -inline static int swift_asprintf(char **strp, const char *fmt, ...) { - va_list args; - va_start(args, fmt); -#if defined(_WIN32) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" - int len = _vscprintf(fmt, args); -#pragma GCC diagnostic pop - if (len < 0) { - va_end(args); - return -1; - } - char *buffer = static_cast(malloc(len + 1)); - if (!buffer) { - va_end(args); - return -1; - } - int result = vsprintf(buffer, fmt, args); - if (result < 0) { - va_end(args); - free(buffer); - return -1; - } - *strp = buffer; -#else - int result = vasprintf(strp, fmt, args); -#endif - va_end(args); - return result; -} - - // namespace swift } diff --git a/include/swift/Runtime/Portability.h b/include/swift/Runtime/Portability.h index 9e4fc418e162f..cd19b2c4193ba 100644 --- a/include/swift/Runtime/Portability.h +++ b/include/swift/Runtime/Portability.h @@ -16,8 +16,47 @@ #ifndef SWIFT_RUNTIME_PORTABILITY_H #define SWIFT_RUNTIME_PORTABILITY_H + +#include #include +#include +#include size_t _swift_strlcpy(char *dst, const char *src, size_t maxlen); +// Skip the attribute when included by the compiler. +#ifdef SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE +SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE +#endif +inline static int swift_asprintf(char **strp, const char *fmt, ...) { + va_list args; + va_start(args, fmt); +#if defined(_WIN32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + int len = _vscprintf(fmt, args); +#pragma GCC diagnostic pop + if (len < 0) { + va_end(args); + return -1; + } + char *buffer = static_cast(malloc(len + 1)); + if (!buffer) { + va_end(args); + return -1; + } + int result = vsprintf(buffer, fmt, args); + if (result < 0) { + va_end(args); + free(buffer); + return -1; + } + *strp = buffer; +#else + int result = vasprintf(strp, fmt, args); +#endif + va_end(args); + return result; +} + #endif diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 6e44fd70da0ef..b613d94f17a7d 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -44,7 +44,7 @@ Type swift::Demangle::getTypeForMangling(ASTContext &ctx, return Type(); ASTBuilder builder(ctx); - return swift::Demangle::decodeMangledType(builder, node); + return swift::Demangle::decodeMangledType(builder, node).getType(); } TypeDecl *swift::Demangle::getTypeDeclForMangling(ASTContext &ctx, @@ -847,8 +847,8 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( if (child->getNumChildren() != 2) return CanGenericSignature(); - auto subjectType = swift::Demangle::decodeMangledType( - *this, child->getChild(0)); + auto subjectType = + swift::Demangle::decodeMangledType(*this, child->getChild(0)).getType(); if (!subjectType) return CanGenericSignature(); @@ -857,8 +857,9 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( Demangle::Node::Kind::DependentGenericConformanceRequirement || child->getKind() == Demangle::Node::Kind::DependentGenericSameTypeRequirement) { - constraintType = swift::Demangle::decodeMangledType( - *this, child->getChild(1)); + constraintType = + swift::Demangle::decodeMangledType(*this, child->getChild(1)) + .getType(); if (!constraintType) return CanGenericSignature(); } diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 686a8421f642d..0f937e95cec77 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -632,9 +632,10 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl { SubstitutionMap substitutions, unsigned ordinal) override { auto underlyingType = Reader - .readUnderlyingTypeForOpaqueTypeDescriptor(opaqueDescriptor.getAddressData(), - ordinal); - + .readUnderlyingTypeForOpaqueTypeDescriptor( + opaqueDescriptor.getAddressData(), ordinal) + .getType(); + if (!underlyingType) return getFailure(); diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index 772afeb1e98f2..d811f53d89036 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -135,7 +135,8 @@ lookupTypeWitness(const std::string &MangledTypeName, auto SubstitutedTypeName = readTypeRef(AssocTy, AssocTy->SubstitutedTypeName); auto Demangled = demangleTypeRef(SubstitutedTypeName); - auto *TypeWitness = swift::Demangle::decodeMangledType(*this, Demangled); + auto *TypeWitness = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); AssociatedTypeCache.insert(std::make_pair(key, TypeWitness)); return TypeWitness; @@ -155,7 +156,8 @@ lookupSuperclass(const TypeRef *TR) { return nullptr; auto Demangled = demangleTypeRef(readTypeRef(FD, FD->Superclass)); - auto Unsubstituted = swift::Demangle::decodeMangledType(*this, Demangled); + auto Unsubstituted = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); if (!Unsubstituted) return nullptr; @@ -226,7 +228,8 @@ bool TypeRefBuilder::getFieldTypeRefs( } auto Demangled = demangleTypeRef(readTypeRef(Field,Field->MangledTypeName)); - auto Unsubstituted = swift::Demangle::decodeMangledType(*this, Demangled); + auto Unsubstituted = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); if (!Unsubstituted) return false; @@ -304,7 +307,7 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { if (CR->hasMangledTypeName()) { auto MangledName = readTypeRef(CR, CR->MangledTypeName); auto DemangleTree = demangleTypeRef(MangledName); - TR = swift::Demangle::decodeMangledType(*this, DemangleTree); + TR = swift::Demangle::decodeMangledType(*this, DemangleTree).getType(); } Info.CaptureTypes.push_back(TR); } @@ -316,7 +319,7 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { if (MSR->hasMangledTypeName()) { auto MangledName = readTypeRef(MSR, MSR->MangledTypeName); auto DemangleTree = demangleTypeRef(MangledName); - TR = swift::Demangle::decodeMangledType(*this, DemangleTree); + TR = swift::Demangle::decodeMangledType(*this, DemangleTree).getType(); } const MetadataSource *MS = nullptr; @@ -344,12 +347,17 @@ TypeRefBuilder::dumpTypeRef(RemoteRef MangledName, auto DemangleTree = demangleTypeRef(MangledName); auto TypeName = nodeToString(DemangleTree); fprintf(file, "%s\n", TypeName.c_str()); - auto TR = swift::Demangle::decodeMangledType(*this, DemangleTree); - if (!TR) { + auto Result = swift::Demangle::decodeMangledType(*this, DemangleTree); + if (Result.isError()) { + auto *Error = Result.getError(); + char *ErrorStr = Error->copyErrorString(); auto str = getTypeRefString(MangledName); - fprintf(file, "!!! Invalid typeref: %s\n", std::string(str.begin(), str.end()).c_str()); + fprintf(file, "!!! Invalid typeref: %s - %s\n", + std::string(str.begin(), str.end()).c_str(), ErrorStr); + Error->freeErrorString(ErrorStr); return; } + auto TR = Result.getType(); TR->dump(file); fprintf(file, "\n"); } diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 114cf058512d0..eccfd625425a5 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -298,7 +298,7 @@ swift_reflection_typeRefForMangledTypeName(SwiftReflectionContextRef ContextRef, const char *MangledTypeName, uint64_t Length) { auto Context = ContextRef->nativeContext; - auto TR = Context->readTypeFromMangledName(MangledTypeName, Length); + auto TR = Context->readTypeFromMangledName(MangledTypeName, Length).getType(); return reinterpret_cast(TR); } diff --git a/stdlib/public/runtime/CompatibilityOverride.def b/stdlib/public/runtime/CompatibilityOverride.def index 92e32430251cd..de154bb44d50f 100644 --- a/stdlib/public/runtime/CompatibilityOverride.def +++ b/stdlib/public/runtime/CompatibilityOverride.def @@ -138,7 +138,7 @@ OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::, (const void *pattern, const void *arguments), (pattern, arguments)) -OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift::, +OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeLookupErrorOr, , SWIFT_CC(swift), swift::, (MetadataRequest request, Demangler &demangler, Demangle::NodePointer node, @@ -146,7 +146,7 @@ OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift SubstGenericParameterFn substGenericParam, SubstDependentWitnessTableFn substWitnessTable), (request, demangler, node, arguments, substGenericParam, substWitnessTable)) -OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeInfo, , SWIFT_CC(swift), swift::, +OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeLookupErrorOr, , SWIFT_CC(swift), swift::, (MetadataRequest request, StringRef typeName, const void * const *arguments, diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 1dfe45dd8c62f..a8e9f2a1b81eb 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2916,24 +2916,22 @@ getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self) { StringRef superclassName = Demangle::makeSymbolicMangledNameStringRef(superclassNameBase); SubstGenericParametersFromMetadata substitutions(self); - MetadataResponse response = - swift_getTypeByMangledName(request, superclassName, - substitutions.getGenericArgs(), + auto result = swift_getTypeByMangledName( + request, superclassName, substitutions.getGenericArgs(), [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getResponse(); - auto superclass = response.Value; - if (!superclass) { - fatalError(0, - "failed to demangle superclass of %s from mangled name '%s'\n", - self->getDescription()->Name.get(), - superclassName.str().c_str()); + }); + if (auto *error = result.getError()) { + fatalError( + 0, "failed to demangle superclass of %s from mangled name '%s': %s\n", + self->getDescription()->Name.get(), superclassName.str().c_str(), + error->copyErrorString()); } - return response; + return result.getType().getResponse(); } else { return MetadataResponse(); } @@ -4928,12 +4926,12 @@ swift_getAssociatedTypeWitnessSlowImpl( Demangle::makeSymbolicMangledNameStringRef(mangledNameBase); // Demangle the associated type. - MetadataResponse response; + TypeLookupErrorOr result = TypeInfo(); if (inProtocolContext) { // The protocol's Self is the only generic parameter that can occur in the // type. - response = - swift_getTypeByMangledName(request, mangledName, nullptr, + result = swift_getTypeByMangledName( + request, mangledName, nullptr, [conformingType](unsigned depth, unsigned index) -> const Metadata * { if (depth == 0 && index == 0) return conformingType; @@ -4950,7 +4948,7 @@ swift_getAssociatedTypeWitnessSlowImpl( return swift_getAssociatedConformanceWitness(wtable, conformingType, type, reqBase, dependentDescriptor); - }).getResponse(); + }); } else { // The generic parameters in the associated type name are those of the // conforming type. @@ -4960,29 +4958,30 @@ swift_getAssociatedTypeWitnessSlowImpl( auto originalConformingType = findConformingSuperclass(conformingType, conformance); SubstGenericParametersFromMetadata substitutions(originalConformingType); - response = swift_getTypeByMangledName(request, mangledName, - substitutions.getGenericArgs(), - [&substitutions](unsigned depth, unsigned index) { - return substitutions.getMetadata(depth, index); - }, - [&substitutions](const Metadata *type, unsigned index) { - return substitutions.getWitnessTable(type, index); - }).getResponse(); + result = swift_getTypeByMangledName( + request, mangledName, substitutions.getGenericArgs(), + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }); } + auto *error = result.getError(); + MetadataResponse response = result.getType().getResponse(); auto assocTypeMetadata = response.Value; - - if (!assocTypeMetadata) { + if (error || !assocTypeMetadata) { + const char *errStr = error ? error->copyErrorString() + : "NULL metadata but no error was provided"; auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); StringRef conformingTypeName(conformingTypeNameInfo.data, conformingTypeNameInfo.length); StringRef assocTypeName = findAssociatedTypeName(protocol, assocType); fatalError(0, "failed to demangle witness for associated type '%s' in " - "conformance '%s: %s' from mangled name '%s'\n", - assocTypeName.str().c_str(), - conformingTypeName.str().c_str(), - protocol->Name.get(), - mangledName.str().c_str()); + "conformance '%s: %s' from mangled name '%s' - %s\n", + assocTypeName.str().c_str(), conformingTypeName.str().c_str(), + protocol->Name.get(), mangledName.str().c_str(), errStr); } assert((uintptr_t(assocTypeMetadata) & @@ -5935,7 +5934,7 @@ void swift::verifyMangledNameRoundtrip(const Metadata *metadata) { nullptr, [](unsigned, unsigned){ return nullptr; }, [](const Metadata *, unsigned) { return nullptr; }) - .getMetadata(); + .getType().getMetadata(); if (metadata != result) swift::warning(RuntimeErrorFlagNone, "Metadata mangled name failed to roundtrip: %p -> %s -> %p\n", diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 93ed4227928eb..c0c2f7c0c4c9d 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -961,13 +961,54 @@ getLocalGenericParams(const ContextDescriptor *context) { return genericContext->getGenericParams().slice(startParamIndex); } -static bool +static llvm::Optional _gatherGenericParameters(const ContextDescriptor *context, llvm::ArrayRef genericArgs, const Metadata *parent, llvm::SmallVectorImpl &genericParamCounts, llvm::SmallVectorImpl &allGenericArgsVec, Demangler &demangler) { + auto makeCommonErrorStringGetter = [&] { + auto metadataVector = genericArgs.vec(); + return [=] { + std::string str; + + str += "_gatherGenericParameters: context: "; + + SymbolInfo contextInfo; + if (lookupSymbol(context, &contextInfo)) { + str += contextInfo.symbolName.get(); + str += " "; + } + + char *contextStr; + swift_asprintf(&contextStr, "%p", context); + str += contextStr; + free(contextStr); + + str += " <"; + + bool first = true; + for (const Metadata *metadata : genericArgs) { + if (!first) + str += ", "; + first = false; + str += nameForMetadata(metadata); + } + + str += "> "; + + str += "parent: "; + if (parent) + str += nameForMetadata(parent); + else + str += ""; + str += " - "; + + return str; + }; + }; + // Figure out the various levels of generic parameters we have in // this type. (void)_gatherGenericParameterCounts(context, @@ -981,7 +1022,15 @@ _gatherGenericParameters(const ContextDescriptor *context, } else if (genericArgs.size() == numTotalGenericParams && !parent) { // Okay: genericArgs is the complete set of generic arguments. } else { - return false; + auto commonString = makeCommonErrorStringGetter(); + auto genericArgsSize = genericArgs.size(); + return TypeLookupError([=] { + return commonString() + "incorrect number of generic args (" + + std::to_string(genericArgsSize) + "), " + + std::to_string(getLocalGenericParams(context).size()) + + " local params, " + std::to_string(numTotalGenericParams) + + " total params"; + }); } // If there are generic parameters at any level, check the generic @@ -1008,15 +1057,30 @@ _gatherGenericParameters(const ContextDescriptor *context, auto genericParams = generics->getGenericParams(); unsigned n = genericParams.size(); if (allGenericArgs.size() != n) { - return false; + auto commonString = makeCommonErrorStringGetter(); + auto argsVecSize = allGenericArgsVec.size(); + return TypeLookupError([=] { + return commonString() + "have " + std::to_string(argsVecSize) + + "generic args, expected " + std::to_string(n); + }); } for (unsigned i = 0; i != n; ++i) { const auto ¶m = genericParams[i]; - if (param.getKind() != GenericParamKind::Type) - return false; - if (param.hasExtraArgument()) - return false; - + if (param.getKind() != GenericParamKind::Type) { + auto commonString = makeCommonErrorStringGetter(); + return TypeLookupError([=] { + return commonString() + "param " + std::to_string(i) + + " has unexpected kind " + + std::to_string(static_cast(param.getKind())); + }); + } + if (param.hasExtraArgument()) { + auto commonString = makeCommonErrorStringGetter(); + return TypeLookupError([=] { + return commonString() + "param " + std::to_string(i) + + "has extra argument"; + }); + } if (param.hasKeyArgument()) allGenericArgsVec.push_back(allGenericArgs[i]); } @@ -1028,26 +1092,33 @@ _gatherGenericParameters(const ContextDescriptor *context, // any extra arguments we need for the instantiation function. SubstGenericParametersFromWrittenArgs substitutions(allGenericArgs, genericParamCounts); - bool failed = - _checkGenericRequirements(generics->getGenericRequirements(), - allGenericArgsVec, + auto error = _checkGenericRequirements( + generics->getGenericRequirements(), allGenericArgsVec, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); - if (failed) - return false; + if (error) + return *error; // If we still have the wrong number of generic arguments, this is // some kind of metadata mismatch. if (generics->getGenericContextHeader().getNumArguments() != - allGenericArgsVec.size()) - return false; + allGenericArgsVec.size()) { + auto commonString = makeCommonErrorStringGetter(); + auto argsVecSize = allGenericArgsVec.size(); + return TypeLookupError([=] { + return commonString() + "generic argument count mismatch, expected " + + std::to_string( + generics->getGenericContextHeader().getNumArguments()) + + ", have " + std::to_string(argsVecSize); + }); + } } - return true; + return llvm::None; } namespace { @@ -1175,7 +1246,7 @@ class DecodedMetadataBuilder { Demangle::NodeFactory &getNodeFactory() { return demangler; } - BuiltType + TypeLookupErrorOr resolveOpaqueType(NodePointer opaqueDecl, llvm::ArrayRef> genericArgs, unsigned ordinal) { @@ -1193,12 +1264,10 @@ class DecodedMetadataBuilder { llvm::SmallVector genericParamCounts; llvm::SmallVector allGenericArgsVec; - if (!_gatherGenericParameters(outerContext, - allGenericArgs, - BuiltType(), /* no parent */ - genericParamCounts, allGenericArgsVec, - demangler)) - return BuiltType(); + if (auto error = _gatherGenericParameters( + outerContext, allGenericArgs, BuiltType(), /* no parent */ + genericParamCounts, allGenericArgsVec, demangler)) + return *error; auto mangledName = descriptor->getUnderlyingTypeArgument(ordinal); SubstGenericParametersFromMetadata substitutions(descriptor, @@ -1210,7 +1279,7 @@ class DecodedMetadataBuilder { }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } BuiltTypeDecl createTypeDecl(NodePointer node, @@ -1245,8 +1314,9 @@ class DecodedMetadataBuilder { return ProtocolDescriptorRef(); #endif } - - BuiltType createObjCClassType(const std::string &mangledName) const { + + TypeLookupErrorOr + createObjCClassType(const std::string &mangledName) const { #if SWIFT_OBJC_INTEROP auto objcClass = objc_getClass(mangledName.c_str()); return swift_getObjCClassMetadata((const ClassMetadata *)objcClass); @@ -1255,7 +1325,7 @@ class DecodedMetadataBuilder { #endif } - BuiltType + TypeLookupErrorOr createBoundGenericObjCClassType(const std::string &mangledName, llvm::ArrayRef args) const { // Generic arguments of lightweight Objective-C generic classes are not @@ -1263,24 +1333,25 @@ class DecodedMetadataBuilder { return createObjCClassType(mangledName); } - BuiltType createNominalType(BuiltTypeDecl metadataOrTypeDecl, - BuiltType parent) const { + TypeLookupErrorOr + createNominalType(BuiltTypeDecl metadataOrTypeDecl, BuiltType parent) const { // Treat nominal type creation the same way as generic type creation, // but with no generic arguments at this level. return createBoundGenericType(metadataOrTypeDecl, { }, parent); } - BuiltType createTypeAliasType(BuiltTypeDecl typeAliasDecl, - BuiltType parent) const { + TypeLookupErrorOr createTypeAliasType(BuiltTypeDecl typeAliasDecl, + BuiltType parent) const { // We can't support sugared types here since we have no way to // resolve the underlying type of the type alias. However, some // CF types are mangled as type aliases. return createNominalType(typeAliasDecl, parent); } - BuiltType createBoundGenericType(BuiltTypeDecl anyTypeDecl, - llvm::ArrayRef genericArgs, - BuiltType parent) const { + TypeLookupErrorOr + createBoundGenericType(BuiltTypeDecl anyTypeDecl, + llvm::ArrayRef genericArgs, + BuiltType parent) const { auto typeDecl = dyn_cast(anyTypeDecl); if (!typeDecl) { if (auto protocol = dyn_cast(anyTypeDecl)) @@ -1294,13 +1365,11 @@ class DecodedMetadataBuilder { llvm::SmallVector genericParamCounts; llvm::SmallVector allGenericArgsVec; - if (!_gatherGenericParameters(typeDecl, - genericArgs, - parent, - genericParamCounts, allGenericArgsVec, - demangler)) - return BuiltType(); - + if (auto error = _gatherGenericParameters(typeDecl, genericArgs, parent, + genericParamCounts, + allGenericArgsVec, demangler)) + return *error; + // Call the access function. auto accessFunction = typeDecl->getAccessFunction(); if (!accessFunction) return BuiltType(); @@ -1308,8 +1377,8 @@ class DecodedMetadataBuilder { return accessFunction(MetadataState::Abstract, allGenericArgsVec).Value; } - BuiltType createBuiltinType(StringRef builtinName, - StringRef mangledName) const { + TypeLookupErrorOr createBuiltinType(StringRef builtinName, + StringRef mangledName) const { #define BUILTIN_TYPE(Symbol, _) \ if (mangledName.equals(#Symbol)) \ return &METADATA_SYM(Symbol).base; @@ -1317,19 +1386,19 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType createMetatypeType( + TypeLookupErrorOr createMetatypeType( BuiltType instance, llvm::Optional repr = None) const { return swift_getMetatypeMetadata(instance); } - BuiltType createExistentialMetatypeType( + TypeLookupErrorOr createExistentialMetatypeType( BuiltType instance, llvm::Optional repr = None) const { return swift_getExistentialMetatypeMetadata(instance); } - BuiltType + TypeLookupErrorOr createProtocolCompositionType(llvm::ArrayRef protocols, BuiltType superclass, bool isClassBound) const { // Determine whether we have a class bound. @@ -1349,13 +1418,13 @@ class DecodedMetadataBuilder { protocols.size(), protocols.data()); } - BuiltType createDynamicSelfType(BuiltType selfType) const { + TypeLookupErrorOr createDynamicSelfType(BuiltType selfType) const { // Free-standing mangled type strings should not contain DynamicSelfType. return BuiltType(); } - BuiltType createGenericTypeParameterType(unsigned depth, - unsigned index) const { + TypeLookupErrorOr + createGenericTypeParameterType(unsigned depth, unsigned index) const { // Use the callback, when provided. if (substGenericParameter) return substGenericParameter(depth, index); @@ -1363,7 +1432,7 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType + TypeLookupErrorOr createFunctionType(llvm::ArrayRef> params, BuiltType result, FunctionTypeFlags flags) const { llvm::SmallVector paramTypes; @@ -1386,7 +1455,7 @@ class DecodedMetadataBuilder { result); } - BuiltType createImplFunctionType( + TypeLookupErrorOr createImplFunctionType( Demangle::ImplParameterConvention calleeConvention, llvm::ArrayRef> params, llvm::ArrayRef> results, @@ -1396,8 +1465,9 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType createTupleType(llvm::ArrayRef elements, - std::string labels) const { + TypeLookupErrorOr + createTupleType(llvm::ArrayRef elements, + std::string labels) const { auto flags = TupleTypeFlags().withNumElements(elements.size()); if (!labels.empty()) flags = flags.withNonConstantLabels(true); @@ -1408,13 +1478,15 @@ class DecodedMetadataBuilder { .Value; } - BuiltType createDependentMemberType(StringRef name, BuiltType base) const { + TypeLookupErrorOr createDependentMemberType(StringRef name, + BuiltType base) const { // Should not have unresolved dependent member types here. return BuiltType(); } - BuiltType createDependentMemberType(StringRef name, BuiltType base, - BuiltProtocolDecl protocol) const { + TypeLookupErrorOr + createDependentMemberType(StringRef name, BuiltType base, + BuiltProtocolDecl protocol) const { #if SWIFT_OBJC_INTEROP if (protocol.isObjC()) return BuiltType(); @@ -1438,14 +1510,14 @@ class DecodedMetadataBuilder { *assocType).Value; } -#define REF_STORAGE(Name, ...) \ - BuiltType create##Name##StorageType(BuiltType base) { \ - ReferenceOwnership.set##Name(); \ - return base; \ +#define REF_STORAGE(Name, ...) \ + TypeLookupErrorOr create##Name##StorageType(BuiltType base) { \ + ReferenceOwnership.set##Name(); \ + return base; \ } #include "swift/AST/ReferenceStorage.def" - BuiltType createSILBoxType(BuiltType base) const { + TypeLookupErrorOr createSILBoxType(BuiltType base) const { // FIXME: Implement. return BuiltType(); } @@ -1454,22 +1526,23 @@ class DecodedMetadataBuilder { return ReferenceOwnership; } - BuiltType createOptionalType(BuiltType base) { + TypeLookupErrorOr createOptionalType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createArrayType(BuiltType base) { + TypeLookupErrorOr createArrayType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createDictionaryType(BuiltType key, BuiltType value) { + TypeLookupErrorOr createDictionaryType(BuiltType key, + BuiltType value) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createParenType(BuiltType base) { + TypeLookupErrorOr createParenType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } @@ -1478,13 +1551,12 @@ class DecodedMetadataBuilder { } SWIFT_CC(swift) -static TypeInfo swift_getTypeByMangledNodeImpl( - MetadataRequest request, - Demangler &demangler, - Demangle::NodePointer node, - const void * const *origArgumentVector, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +static TypeLookupErrorOr +swift_getTypeByMangledNodeImpl(MetadataRequest request, Demangler &demangler, + Demangle::NodePointer node, + const void *const *origArgumentVector, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { // Simply call an accessor function if that's all we got. if (node->getKind() == Node::Kind::AccessorFunctionReference) { // The accessor function is passed the pointer to the original argument @@ -1504,22 +1576,23 @@ static TypeInfo swift_getTypeByMangledNodeImpl( DecodedMetadataBuilder builder(demangler, substGenericParam, substWitnessTable); auto type = Demangle::decodeMangledType(builder, node); - if (!type) { - return {MetadataResponse{nullptr, MetadataState::Complete}, - TypeReferenceOwnership()}; + if (type.isError()) { + return *type.getError(); + } + if (!type.getType()) { + return TypeLookupError("NULL type but no error provided"); } - return {swift_checkMetadataState(request, type), - builder.getReferenceOwnership()}; + return TypeInfo{swift_checkMetadataState(request, type.getType()), + builder.getReferenceOwnership()}; } SWIFT_CC(swift) -static TypeInfo swift_getTypeByMangledNameImpl( - MetadataRequest request, - StringRef typeName, - const void * const *origArgumentVector, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +static TypeLookupErrorOr +swift_getTypeByMangledNameImpl(MetadataRequest request, StringRef typeName, + const void *const *origArgumentVector, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { DemanglerForRuntimeTypeResolution> demangler; NodePointer node; @@ -1587,7 +1660,7 @@ swift_getTypeByMangledNameInEnvironment( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1607,7 +1680,7 @@ swift_getTypeByMangledNameInEnvironmentInMetadataState( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1626,7 +1699,7 @@ swift_getTypeByMangledNameInContext( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1646,7 +1719,7 @@ swift_getTypeByMangledNameInContextInMetadataState( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } /// Demangle a mangled name, but don't allow symbolic references. @@ -1661,7 +1734,7 @@ swift_stdlib_getTypeByMangledNameUntrusted(const char *typeNameStart, } return swift_getTypeByMangledName(MetadataState::Complete, typeName, nullptr, - {}, {}).getMetadata(); + {}, {}).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1680,7 +1753,7 @@ swift_getOpaqueTypeMetadata(MetadataRequest request, }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getResponse(); + }).getType().getResponse(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1735,7 +1808,7 @@ getObjCClassByMangledName(const char * _Nonnull typeName, }, [&](const Metadata *type, unsigned index) { return nullptr; - }).getMetadata(); + }).getType().getMetadata(); } else { metadata = swift_stdlib_getTypeByMangledNameUntrusted(typeStr.data(), typeStr.size()); @@ -2068,7 +2141,7 @@ void swift::gatherWrittenGenericArgs( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); continue; } diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index c46e8ff9a9b38..2f5e7232cc55d 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -17,7 +17,10 @@ #ifndef SWIFT_RUNTIME_PRIVATE_H #define SWIFT_RUNTIME_PRIVATE_H +#include + #include "swift/Demangling/Demangler.h" +#include "swift/Demangling/TypeLookupError.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Metadata.h" @@ -77,6 +80,8 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } + operator bool() const { return getMetadata(); } + #define REF_STORAGE(Name, ...) \ bool is##Name() const { return ReferenceOwnership.is##Name(); } #include "swift/AST/ReferenceStorage.def" @@ -369,7 +374,7 @@ class TypeInfo { /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. SWIFT_CC(swift) - TypeInfo swift_getTypeByMangledNode( + TypeLookupErrorOr swift_getTypeByMangledNode( MetadataRequest request, Demangler &demangler, Demangle::NodePointer node, @@ -384,7 +389,7 @@ class TypeInfo { /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. SWIFT_CC(swift) - TypeInfo swift_getTypeByMangledName( + TypeLookupErrorOr swift_getTypeByMangledName( MetadataRequest request, StringRef typeName, const void * const *arguments, @@ -447,12 +452,12 @@ class TypeInfo { /// generic requirements (e.g., those that need to be /// passed to an instantiation function) will be added to this vector. /// - /// \returns true if an error occurred, false otherwise. - bool _checkGenericRequirements( - llvm::ArrayRef requirements, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable); + /// \returns the error if an error occurred, None otherwise. + llvm::Optional _checkGenericRequirements( + llvm::ArrayRef requirements, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable); /// A helper function which avoids performing a store if the destination /// address already contains the source value. This is useful when diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index e373d2a50848e..c434011faea85 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -165,15 +165,16 @@ ProtocolConformanceDescriptor::getWitnessTable(const Metadata *type) const { llvm::SmallVector conditionalArgs; if (hasConditionalRequirements()) { SubstGenericParametersFromMetadata substitutions(type); - bool failed = - _checkGenericRequirements(getConditionalRequirements(), conditionalArgs, + auto error = _checkGenericRequirements( + getConditionalRequirements(), conditionalArgs, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); - if (failed) return nullptr; + if (error) + return nullptr; } return swift_getWitnessTable(this, type, conditionalArgs.data()); @@ -642,31 +643,36 @@ static bool isSubclass(const Metadata *subclass, const Metadata *superclass) { }); } -bool swift::_checkGenericRequirements( - llvm::ArrayRef requirements, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +llvm::Optional swift::_checkGenericRequirements( + llvm::ArrayRef requirements, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { for (const auto &req : requirements) { // Make sure we understand the requirement we're dealing with. - if (!req.hasKnownKind()) return true; + if (!req.hasKnownKind()) + return TypeLookupError("unknown kind"); // Resolve the subject generic parameter. - const Metadata *subjectType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getParam(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!subjectType) - return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getParam(), extraArguments.data(), + substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + const Metadata *subjectType = result.getType().getMetadata(); // Check the requirement. switch (req.getKind()) { case GenericRequirementKind::Protocol: { const WitnessTable *witnessTable = nullptr; if (!_conformsToProtocol(nullptr, subjectType, req.getProtocol(), - &witnessTable)) - return true; + &witnessTable)) { + const char *protoName = + req.getProtocol() ? req.getProtocol().getName() : ""; + return TypeLookupError( + "subject type %s does not conform to protocol %s", req.getParam(), + protoName); + } // If we need a witness table, add it. if (req.getProtocol().needsWitnessTable()) { @@ -679,17 +685,19 @@ bool swift::_checkGenericRequirements( case GenericRequirementKind::SameType: { // Demangle the second type under the given substitutions. - auto otherType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getMangledTypeName(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!otherType) return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto otherType = result.getType().getMetadata(); assert(!req.getFlags().hasExtraArgument()); // Check that the types are equivalent. - if (subjectType != otherType) return true; + if (subjectType != otherType) + return TypeLookupError("subject type %s does not match %s", + req.getParam(), req.getMangledTypeName()); continue; } @@ -698,22 +706,24 @@ bool swift::_checkGenericRequirements( switch (req.getLayout()) { case GenericRequirementLayoutKind::Class: if (!subjectType->satisfiesClassConstraint()) - return true; + return TypeLookupError( + "subject type %s does not satisfy class constraint", + req.getParam()); continue; } // Unknown layout. - return true; + return TypeLookupError("unknown layout kind %u", req.getLayout()); } case GenericRequirementKind::BaseClass: { // Demangle the base type under the given substitutions. - auto baseType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getMangledTypeName(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!baseType) return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto baseType = result.getType().getMetadata(); // If the type which is constrained to a base class is an existential // type, and if that existential type includes a superclass constraint, @@ -725,7 +735,8 @@ bool swift::_checkGenericRequirements( } if (!isSubclass(subjectType, baseType)) - return true; + return TypeLookupError("%s is not subclass of %s", req.getParam(), + req.getMangledTypeName()); continue; } @@ -737,11 +748,12 @@ bool swift::_checkGenericRequirements( } // Unknown generic requirement kind. - return true; + return TypeLookupError("unknown generic requirement kind %u", + req.getKind()); } // Success! - return false; + return llvm::None; } const Metadata *swift::findConformingSuperclass( diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 821fecfb7fb0b..0206237d2b44b 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -400,27 +400,33 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { auto typeName = field.getMangledTypeName(); SubstGenericParametersFromMetadata substitutions(base); - auto typeInfo = swift_getTypeByMangledName(MetadataState::Complete, - typeName, - substitutions.getGenericArgs(), - [&substitutions](unsigned depth, unsigned index) { - return substitutions.getMetadata(depth, index); - }, - [&substitutions](const Metadata *type, unsigned index) { - return substitutions.getWitnessTable(type, index); - }); + auto result = swift_getTypeByMangledName( + MetadataState::Complete, typeName, substitutions.getGenericArgs(), + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }); // If demangling the type failed, pretend it's an empty type instead with // a log message. - if (!typeInfo.getMetadata()) { + TypeInfo typeInfo; + if (result.isError()) { typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING), MetadataState::Complete}, {}); + + auto *error = result.getError(); + char *str = error->copyErrorString(); missing_reflection_metadata_warning( - "warning: the Swift runtime was unable to demangle the type " - "of field '%*s'. the mangled type name is '%*s'. this field will " - "show up as an empty tuple in Mirrors\n", - (int)name.size(), name.data(), - (int)typeName.size(), typeName.data()); + "warning: the Swift runtime was unable to demangle the type " + "of field '%*s'. the mangled type name is '%*s': %s. this field will " + "show up as an empty tuple in Mirrors\n", + (int)name.size(), name.data(), (int)typeName.size(), typeName.data(), + str); + error->freeErrorString(str); + } else { + typeInfo = result.getType(); } auto fieldType = FieldType(typeInfo.getMetadata()); diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index f377a77d6f106..ba33dd19abd69 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -26,11 +26,13 @@ #include "llvm/ADT/StringRef.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Casting.h" +#include "swift/Runtime/Debug.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/ObjCBridge.h" +#include "swift/Runtime/Portability.h" #include "swift/Strings.h" #include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/AssertionReporting.h" @@ -39,7 +41,6 @@ #include "Private.h" #include "SwiftObject.h" #include "WeakReference.h" -#include "swift/Runtime/Debug.h" #if SWIFT_OBJC_INTEROP #include #endif diff --git a/stdlib/public/stubs/Assert.cpp b/stdlib/public/stubs/Assert.cpp index 62135b2312b1c..fa0169a5ad6b8 100644 --- a/stdlib/public/stubs/Assert.cpp +++ b/stdlib/public/stubs/Assert.cpp @@ -12,6 +12,7 @@ #include "swift/Runtime/Config.h" #include "swift/Runtime/Debug.h" +#include "swift/Runtime/Portability.h" #include "../SwiftShims/AssertionReporting.h" #include #include diff --git a/tools/swift-reflection-dump/swift-reflection-dump.cpp b/tools/swift-reflection-dump/swift-reflection-dump.cpp index c06ec35de5f81..8846f4452e855 100644 --- a/tools/swift-reflection-dump/swift-reflection-dump.cpp +++ b/tools/swift-reflection-dump/swift-reflection-dump.cpp @@ -668,12 +668,15 @@ static int doDumpReflectionSections(ArrayRef BinaryFilenames, Demangle::Demangler Dem; auto Demangled = Dem.demangleType(Line); - auto *TypeRef = - swift::Demangle::decodeMangledType(builder, Demangled); - if (TypeRef == nullptr) { - fprintf(file, "Invalid typeref:%s\n", Line.c_str()); + auto Result = swift::Demangle::decodeMangledType(builder, Demangled); + if (Result.isError()) { + auto *error = Result.getError(); + char *str = error->copyErrorString(); + fprintf(file, "Invalid typeref:%s - %s\n", Line.c_str(), str); + error->freeErrorString(str); continue; } + auto TypeRef = Result.getType(); TypeRef->dump(file); auto *TypeInfo = builder.getTypeConverter().getTypeInfo(TypeRef); diff --git a/unittests/runtime/CompatibilityOverride.cpp b/unittests/runtime/CompatibilityOverride.cpp index 80a9b086980b9..6056f26117930 100644 --- a/unittests/runtime/CompatibilityOverride.cpp +++ b/unittests/runtime/CompatibilityOverride.cpp @@ -35,8 +35,8 @@ namespace { return MetadataResponse{nullptr, MetadataState::Complete}; } - template<> - TypeInfo getEmptyValue() { + template <> + TypeLookupErrorOr getEmptyValue>() { return TypeInfo(); } } @@ -172,13 +172,13 @@ TEST_F(CompatibilityOverrideTest, test_swift_getTypeByMangledNode) { Demangler demangler; auto Result = swift_getTypeByMangledNode(MetadataState::Abstract, demangler, nullptr, nullptr, nullptr,nullptr); - ASSERT_EQ(Result.getMetadata(), nullptr); + ASSERT_EQ(Result.getType().getMetadata(), nullptr); } TEST_F(CompatibilityOverrideTest, test_swift_getTypeByMangledName) { auto Result = swift_getTypeByMangledName(MetadataState::Abstract, "", nullptr, nullptr, nullptr); - ASSERT_EQ(Result.getMetadata(), nullptr); + ASSERT_EQ(Result.getType().getMetadata(), nullptr); } TEST_F(CompatibilityOverrideTest, test_swift_getAssociatedTypeWitnessSlow) { From 539b241bf2b36b7d7e9362c30d7a39d1978bad38 Mon Sep 17 00:00:00 2001 From: tbkka Date: Fri, 28 Aug 2020 13:26:29 -0700 Subject: [PATCH 105/120] Fallback to building Cmake from source on all platforms, not just Linux (#33543) * Fallback to building Cmake from source on all platforms, not just Linux * Remove no-longer-used import --- utils/swift_build_support/swift_build_support/cmake.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/utils/swift_build_support/swift_build_support/cmake.py b/utils/swift_build_support/swift_build_support/cmake.py index 85a97dfa747e2..5ef74e0cf5ee4 100644 --- a/utils/swift_build_support/swift_build_support/cmake.py +++ b/utils/swift_build_support/swift_build_support/cmake.py @@ -18,7 +18,6 @@ from __future__ import absolute_import, unicode_literals import os -import platform import re from numbers import Number @@ -256,9 +255,6 @@ def build_cmake(self, source_root, build_root): # the source and build the source if necessary. Returns the path to the # cmake binary. def check_cmake_version(self, source_root, build_root): - if platform.system() != 'Linux': - return - cmake_source_dir = os.path.join(source_root, 'cmake') # If the source is not checked out then don't attempt to build cmake. if not os.path.isdir(cmake_source_dir): From 6ad2757bef212510785eef8a9755d09140b54978 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 28 Aug 2020 13:58:02 -0700 Subject: [PATCH 106/120] [Concurrency] Use completion/completionHandler parameter names for async import Extend the check for completion handler parameters to also consider the name of the parameter (not its argument label). If it's `completion` or `completionHandler`, we have a completion handler. This extends our API coverage for importing Objective-C methods with completion handlers as 'async'. --- lib/ClangImporter/ImportName.cpp | 33 ++++++++++++------- test/ClangImporter/objc_async.swift | 2 ++ test/IDE/print_clang_objc_async.swift | 1 + .../usr/include/ObjCConcurrency.h | 2 ++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 791c695ff1b91..fc0313bbc91df 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1213,10 +1213,13 @@ NameImporter::considerAsyncImport( // handler. Optional newBaseName; if (isCompletionHandlerParamName(paramNames[completionHandlerParamNameIndex])) { - // The parameter itself has an appropriate name. + // The argument label itself has an appropriate name. } else if (!hasCustomName && completionHandlerParamIndex == 0 && (newBaseName = isCompletionHandlerInBaseName(baseName))) { // The base name implies that the first parameter is a completion handler. + } else if (isCompletionHandlerParamName( + params[completionHandlerParamIndex]->getName())) { + // The parameter has an appropriate name. } else { return None; } @@ -1238,6 +1241,10 @@ NameImporter::considerAsyncImport( if (isInitializer) return notAsync("initializers cannot be async"); + // Accessors are never imported as async. + if (clangDecl->isPropertyAccessor()) + return notAsync("method is a property accessor"); + // Check whether we method has a suitable return type. if (clangDecl->getReturnType()->isVoidType()) { // 'void' is the common case; the method produces no synchronous result. @@ -1515,7 +1522,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, else if (parsedName.IsSetter) result.info.accessorKind = ImportedAccessorKind::PropertySetter; - if (method && parsedName.IsFunctionName) { + if (method && parsedName.IsFunctionName && + result.info.accessorKind == ImportedAccessorKind::None) { // Get the parameters. ArrayRef params{method->param_begin(), method->param_end()}; @@ -1787,16 +1795,6 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.errorInfo = *errorInfo; } - if (version.supportsConcurrency()) { - if (auto asyncInfo = considerAsyncImport( - objcMethod, baseName, argumentNames, params, isInitializer, - /*hasCustomName=*/false, - result.getErrorInfo())) { - result.info.hasAsyncInfo = true; - result.info.asyncInfo = *asyncInfo; - } - } - isFunction = true; // Is this one of the accessors for subscripts? @@ -1814,6 +1812,17 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.accessorKind = ImportedAccessorKind::SubscriptSetter; } + if (version.supportsConcurrency() && + result.info.accessorKind == ImportedAccessorKind::None) { + if (auto asyncInfo = considerAsyncImport( + objcMethod, baseName, argumentNames, params, isInitializer, + /*hasCustomName=*/false, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + } + } + break; } } diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 8ec33ae643905..15b149a89c5dd 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -11,6 +11,8 @@ func testSlowServer(slowServer: SlowServer) async { let _: String = await slowServer.findAnswerFailingly() ?? "nope" // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} + let _: Void = await slowServer.doSomethingFun("jump") + let _: (Int) -> Void = slowServer.completionHandler } func testSlowServerOldSchool(slowServer: SlowServer) { diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index 6e4539de8825c..7d3029fff8be6 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -16,4 +16,5 @@ // CHECK-DAG: func findAnswer() async throws -> String? // CHECK-DAG: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws // CHECK-DAG: func findAnswerFailingly() async throws -> String? +// CHECK-DAG: func doSomethingFun(_ operation: String) async // CHECK: {{^[}]$}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index 9903bd0a91a64..b03e3530a4968 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -9,6 +9,8 @@ -(void)checkAvailabilityWithCompletionHandler:(void (^)(BOOL isAvailable))completionHandler; -(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); -(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); +-(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; +@property(readwrite) void (^completionHandler)(NSInteger); @end #pragma clang assume_nonnull end From c4f05052da564c5f47552af02cb6c185af8b5149 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Fri, 14 Aug 2020 14:58:42 -0700 Subject: [PATCH 107/120] [Parse] Don't drop throws containing a code completion expression. --- lib/Parse/ParseStmt.cpp | 7 ++++--- test/IDE/complete_exception.swift | 11 +++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index e39f0daf1846b..1a95fc1585f46 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -898,9 +898,7 @@ ParserResult Parser::parseStmtThrow(SourceLoc tryLoc) { exprLoc = Tok.getLoc(); ParserResult Result = parseExpr(diag::expected_expr_throw); - - if (Result.hasCodeCompletion()) - return makeParserCodeCompletionResult(); + bool hasCodeCompletion = Result.hasCodeCompletion(); if (Result.isNull()) Result = makeParserErrorResult(new (Context) ErrorExpr(throwLoc)); @@ -916,6 +914,9 @@ ParserResult Parser::parseStmtThrow(SourceLoc tryLoc) { Result = makeParserResult(new (Context) TryExpr(exprLoc, Result.get())); } + if (hasCodeCompletion) + Result.setHasCodeCompletion(); + return makeParserResult(Result, new (Context) ThrowStmt(throwLoc, Result.get())); } diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index 48d222ced7565..0a52f94e2928d 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -5,11 +5,13 @@ // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH2 | %FileCheck %s -check-prefix=CATCH2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW2 | %FileCheck %s -check-prefix=THROW2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH3 | %FileCheck %s -check-prefix=CATCH3 +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW3 | %FileCheck %s -check-prefix=THROW3 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH1 | %FileCheck %s -check-prefix=CATCH1 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW1 | %FileCheck %s -check-prefix=THROW1 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH2 | %FileCheck %s -check-prefix=CATCH2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW2 | %FileCheck %s -check-prefix=THROW2 +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW3 | %FileCheck %s -check-prefix=THROW3 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH1 > %t.inside_catch1 // RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch1 @@ -126,11 +128,20 @@ func test005() { // CATCH3: End completions } +func testInvalid() { + try throw Error4.#^THROW3^# +// THROW3: Begin completions +// THROW3: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}} +// THROW3: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}} +// THROW3: End completions +} + //===--- Top-level throw/catch do {} catch #^TOP_LEVEL_CATCH1^# {} throw #^TOP_LEVEL_THROW1^# do {} catch Error4.#^TOP_LEVEL_CATCH2^# {} throw Error4.#^TOP_LEVEL_THROW2^# +try throw Error4.#^TOP_LEVEL_THROW3^# //===--- Inside catch body From 00994f114768917a8037920e99352d872f2aa2b1 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Thu, 27 Aug 2020 14:34:56 -0700 Subject: [PATCH 108/120] [Parse] Stop early exiting when parsing catch statements and ObjCSelector expressions that contain code completions. --- lib/Parse/ParseExpr.cpp | 4 +--- lib/Parse/ParseStmt.cpp | 3 --- test/IDE/complete_exception.swift | 8 ++++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1d3a63a826d11..474cfb2cade44 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -802,8 +802,6 @@ ParserResult Parser::parseExprSelector() { parseExpr(selectorKind == ObjCSelectorExpr::Method ? diag::expr_selector_expected_method_expr : diag::expr_selector_expected_property_expr); - if (subExpr.hasCodeCompletion()) - return makeParserCodeCompletionResult(); // Parse the closing ')'. SourceLoc rParenLoc; @@ -819,7 +817,7 @@ ParserResult Parser::parseExprSelector() { } // If the subexpression was in error, just propagate the error. - if (subExpr.isParseError()) + if (subExpr.isParseError() && !subExpr.hasCodeCompletion()) return makeParserResult( new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc))); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 1a95fc1585f46..c4a64ca54973e 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2013,9 +2013,6 @@ ParserResult Parser::parseStmtCatch() { GuardedPattern PatternResult; parseGuardedPattern(*this, PatternResult, status, boundDecls, GuardedPatternContext::Catch, isFirst); - if (status.hasCodeCompletion()) { - return makeParserCodeCompletionResult(); - } caseLabelItems.emplace_back(PatternResult.ThePattern, PatternResult.WhereLoc, PatternResult.Guard); isFirst = false; diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index 0a52f94e2928d..a5cef6bb5175b 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -72,10 +72,10 @@ func test001() { do {} catch #^CATCH1^# // CATCH1: Begin completions -// CATCH1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}} +// CATCH1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}} // CATCH1-DAG: Keyword[let]/None: let{{; name=.+$}} // CATCH1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}} // CATCH1-DAG: Decl[Class]/OtherModule[Foundation]/IsSystem: NSError[#NSError#]{{; name=.+$}} From 3e0500d73c1f1efbbc166e22228fd589da19695d Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Mon, 24 Aug 2020 11:39:47 -0700 Subject: [PATCH 109/120] [Parse][IDE] Don't drop default argument init exprs containing code completion exprs and type check them for code completion. Fixes up some tests marked as non-ideal to give the ideal result now too. --- lib/IDE/ExprContextAnalysis.cpp | 5 +++++ lib/Parse/ParsePattern.cpp | 4 +++- test/IDE/complete_unresolved_members.swift | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 787606e321891..ad3a32fb7ada2 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -85,6 +85,11 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { typeCheckPatternBinding(PBD, i); } } + } else if (auto *defaultArg = dyn_cast(DC)) { + if (auto *AFD = dyn_cast(defaultArg->getParent())) { + auto *Param = AFD->getParameters()->get(defaultArg->getIndex()); + (void*)Param->getTypeCheckedDefaultExpr(); + } } break; diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index e26a0dd1cd238..c98ea0c6f04da 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -130,8 +130,10 @@ static ParserStatus parseDefaultArgument( defaultArgs->HasDefaultArgument = true; - if (initR.hasCodeCompletion()) + if (initR.hasCodeCompletion()) { + init = initR.get(); return makeParserCodeCompletionStatus(); + } if (initR.isNull()) return makeParserError(); diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 7a895c550c665..804d9cd5bcba9 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -109,9 +109,9 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_21 | %FileCheck %s -check-prefix=GENERICPARAM_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DECL_MEMBER_INIT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPEPARAM_IN_CONTEXTTYPE_1 | %FileCheck %s -check-prefix=TYPEPARAM_IN_CONTEXTTYPE_1 From 89803560f9a1d11d438b06392a9938648aa3be62 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Tue, 25 Aug 2020 21:28:38 -0700 Subject: [PATCH 110/120] [Parse] Perform the single expression function body transform for delayed parsing as well We were previously only doing it when parsing up front. --- include/swift/Parse/Parser.h | 3 +- lib/Parse/ParseDecl.cpp | 129 ++++++++++++++++++----------------- lib/Parse/Parser.cpp | 2 +- 3 files changed, 68 insertions(+), 66 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 723fdcd956a22..7f1b3ddba9249 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1133,8 +1133,7 @@ class Parser { ParseDeclOptions Flags, DeclAttributes &Attributes, bool HasFuncKeyword = true); - ParserResult - parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); + BraceStmt *parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); void parseAbstractFunctionBody(AbstractFunctionDecl *AFD); BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD); ParserResult parseDeclProtocol(ParseDeclOptions Flags, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index aa3cf8ce5cad1..ac374bee7e53b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6442,10 +6442,9 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, return DCC.fixupParserResult(FD); } -/// Parse a function body for \p AFD and returns it without setting the body -/// to \p AFD . -ParserResult -Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { +/// Parse a function body for \p AFD, setting the body to \p AFD before +/// returning it. +BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { assert(Tok.is(tok::l_brace)); // Enter the arguments for the function into a new function-body scope. We @@ -6473,13 +6472,70 @@ Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { CodeCompletion->completeAccessorBeginning(CCE); RBraceLoc = Tok.getLoc(); consumeToken(tok::code_complete); - return makeParserCodeCompletionResult( - BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc, - /*implicit*/ true)); + auto *BS = BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc, + /*implicit*/ true); + AFD->setBodyParsed(BS); + return BS; } } - return parseBraceItemList(diag::invalid_diagnostic); + ParserResult Body = parseBraceItemList(diag::invalid_diagnostic); + if (Body.isNull()) + return nullptr; + + BraceStmt *BS = Body.get(); + AFD->setBodyParsed(BS); + + // If the body consists of a single expression, turn it into a return + // statement. + // + // But don't do this transformation during code completion, as the source + // may be incomplete and the type mismatch in return statement will just + // confuse the type checker. + if (BS->getNumElements() != 1 || Body.hasCodeCompletion()) + return BS; + + auto Element = BS->getFirstElement(); + if (auto *stmt = Element.dyn_cast()) { + if (isa(AFD)) { + if (auto *returnStmt = dyn_cast(stmt)) { + if (!returnStmt->hasResult()) { + auto returnExpr = TupleExpr::createEmpty(Context, + SourceLoc(), + SourceLoc(), + /*implicit*/true); + returnStmt->setResult(returnExpr); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(returnExpr); + } + } + } + } else if (auto *E = Element.dyn_cast()) { + if (auto SE = dyn_cast(E->getSemanticsProvidingExpr())) { + if (SE->getNumElements() > 1 && isa(SE->getElement(1))) { + // This is an assignment. We don't want to implicitly return + // it. + return BS; + } + } + if (isa(AFD)) { + auto RS = new (Context) ReturnStmt(SourceLoc(), E); + BS->setFirstElement(RS); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(E); + } else if (auto *F = dyn_cast(AFD)) { + if (F->isFailable() && isa(E)) { + // If it's a nil literal, just insert return. This is the only + // legal thing to return. + auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); + BS->setFirstElement(RS); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(E); + } + } + } + + return BS; } /// Parse function body into \p AFD or skip it for delayed parsing. @@ -6504,60 +6560,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { } Scope S(this, ScopeKind::FunctionBody); - - ParserResult Body = parseAbstractFunctionBodyImpl(AFD); - if (!Body.isNull()) { - BraceStmt * BS = Body.get(); - AFD->setBodyParsed(BS); - - // If the body consists of a single expression, turn it into a return - // statement. - // - // But don't do this transformation during code completion, as the source - // may be incomplete and the type mismatch in return statement will just - // confuse the type checker. - if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) { - auto Element = BS->getFirstElement(); - if (auto *stmt = Element.dyn_cast()) { - if (isa(AFD)) { - if (auto *returnStmt = dyn_cast(stmt)) { - if (!returnStmt->hasResult()) { - auto returnExpr = TupleExpr::createEmpty(Context, - SourceLoc(), - SourceLoc(), - /*implicit*/true); - returnStmt->setResult(returnExpr); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(returnExpr); - } - } - } - } else if (auto *E = Element.dyn_cast()) { - if (auto SE = dyn_cast(E->getSemanticsProvidingExpr())) { - if (SE->getNumElements() > 1 && isa(SE->getElement(1))) { - // This is an assignment. We don't want to implicitly return - // it. - return; - } - } - if (isa(AFD)) { - auto RS = new (Context) ReturnStmt(SourceLoc(), E); - BS->setFirstElement(RS); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(E); - } else if (auto *F = dyn_cast(AFD)) { - if (F->isFailable() && isa(E)) { - // If it's a nil literal, just insert return. This is the only - // legal thing to return. - auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); - BS->setFirstElement(RS); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(E); - } - } - } - } - } + (void)parseAbstractFunctionBodyImpl(AFD); } BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { @@ -6589,7 +6592,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { Scope TopLevelScope(this, ScopeKind::TopLevel); Scope S(this, ScopeKind::FunctionBody); - return parseAbstractFunctionBodyImpl(AFD).getPtrOrNull(); + return parseAbstractFunctionBodyImpl(AFD); } /// Parse a 'enum' declaration, returning true (and doing no token diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 7b56a1f629368..25161aabaa9ee 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -203,7 +203,7 @@ void Parser::performCodeCompletionSecondPassImpl( case CodeCompletionDelayedDeclKind::FunctionBody: { auto *AFD = cast(DC); - AFD->setBodyParsed(parseAbstractFunctionBodyImpl(AFD).getPtrOrNull()); + (void)parseAbstractFunctionBodyImpl(AFD); break; } } From c03d76291cfa03676286b01807944f793e9d8a0c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 28 Aug 2020 17:26:52 -0700 Subject: [PATCH 111/120] [ConstraintSystem] NFC: Clarify why upward propagation of literal conformances is ncessary at the moment This is NFC because only literal protocols are tracked at the moment. Forward propagate (subtype -> supertype) only literal conformance requirements since that helps solver to infer more types at parameter positions. ```swift func foo(_: String, _: T) -> T { fatalError() } func bar(_: Any?) {} func test() { bar(foo("", "")) } ``` If one of the literal arguments doesn't propagate its `ExpressibleByStringLiteral` conformance, we'd end up picking `T` with only one type `Any?` which is incorrect. This is not going to be necessary once bindings are filtered based of requirements placed on a type variable. --- lib/Sema/CSBindings.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a82b389991788..90344be7b8154 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -56,8 +56,33 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings( auto &bindings = relatedBindings->getSecond(); - // Infer transitive protocol requirements. - llvm::copy(bindings.Protocols, std::back_inserter(Protocols)); + // FIXME: This is a workaround necessary because solver doesn't filter + // bindings based on protocol requirements placed on a type variable. + // + // Forward propagate (subtype -> supertype) only literal conformance + // requirements since that helps solver to infer more types at + // parameter positions. + // + // \code + // func foo(_: String, _: T) -> T { + // fatalError() + // } + // + // func bar(_: Any?) {} + // + // func test() { + // bar(foo("", "")) + // } + // \endcode + // + // If one of the literal arguments doesn't propagate its + // `ExpressibleByStringLiteral` conformance, we'd end up picking + // `T` with only one type `Any?` which is incorrect. + llvm::copy_if(bindings.Protocols, std::back_inserter(Protocols), + [](const Constraint *protocol) { + return protocol->getKind() == + ConstraintKind::LiteralConformsTo; + }); // Infer transitive defaults. llvm::copy(bindings.Defaults, std::back_inserter(Defaults)); From ebc03a1805eb9e2c8c6b6315dae10872095c49f3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 28 Aug 2020 17:39:21 -0700 Subject: [PATCH 112/120] [CSBindings] NFC: Remove dead condition leftover from Swift version 3 support --- lib/Sema/CSBindings.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 90344be7b8154..5f324d7579e64 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -931,15 +931,7 @@ bool ConstraintSystem::PotentialBindings::infer( case ConstraintKind::ConformsTo: case ConstraintKind::SelfObjectOfProtocol: - // Swift 3 allowed the use of default types for normal conformances - // to expressible-by-literal protocols. - if (cs.getASTContext().LangOpts.EffectiveLanguageVersion[0] >= 4) - return false; - - if (!constraint->getSecondType()->is()) - return false; - - LLVM_FALLTHROUGH; + return false; case ConstraintKind::LiteralConformsTo: { // Record constraint where protocol requirement originated From 172c4be02d3c582235cf256103bbb3d7af914a79 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Sat, 29 Aug 2020 02:38:42 +0100 Subject: [PATCH 113/120] [Sema] Diagnose use of ambiguous property wrappers (#33688) --- include/swift/AST/DiagnosticsCommon.def | 14 ++++++ include/swift/AST/DiagnosticsParse.def | 2 - lib/AST/NameLookup.cpp | 47 ++++++++++++++++++++ lib/Sema/TypeCheckAttr.cpp | 11 ----- test/NameLookup/Inputs/custom_attrs_A.swift | 13 ++++++ test/NameLookup/Inputs/custom_attrs_B.swift | 13 ++++++ test/NameLookup/custom_attrs_ambiguous.swift | 21 +++++++++ 7 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 test/NameLookup/Inputs/custom_attrs_A.swift create mode 100644 test/NameLookup/Inputs/custom_attrs_B.swift create mode 100644 test/NameLookup/custom_attrs_ambiguous.swift diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index f4c39ce036d06..72d8d64bbecb7 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -188,5 +188,19 @@ ERROR(scanner_find_cycle, none, ERROR(scanner_arguments_invalid, none, "dependencies scanner cannot be configured with arguments: '%0'", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: custom attribute diagnostics +//------------------------------------------------------------------------------ + +ERROR(ambiguous_custom_attribute_ref,none, + "ambiguous use of attribute %0", (Identifier)) +NOTE(ambiguous_custom_attribute_ref_fix,none, + "use '%0.' to reference the attribute %1 in module %2", + (StringRef, Identifier, Identifier)) +NOTE(found_attribute_candidate,none, + "found this attribute", ()) +ERROR(unknown_attribute,none, + "unknown attribute '%0'", (StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6c7f4e6533b04..3a278eb7dea14 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1324,8 +1324,6 @@ ERROR(replace_equal_with_colon_for_value,none, "'=' has been replaced with ':' in attribute arguments", ()) ERROR(expected_attribute_name,none, "expected an attribute name", ()) -ERROR(unknown_attribute,none, - "unknown attribute '%0'", (StringRef)) ERROR(unexpected_lparen_in_attribute,none, "unexpected '(' in attribute '%0'", (StringRef)) ERROR(duplicate_attribute,none, diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index b581137926415..7ef21cadcb205 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2434,6 +2434,53 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, } } + // If we have more than one attribute declaration, we have an ambiguity. + // So, emit an ambiguity diagnostic. + if (auto typeRepr = attr->getTypeRepr()) { + if (nominals.size() > 1) { + SmallVector ambiguousCandidates; + // Filter out declarations that cannot be attributes. + for (auto decl : nominals) { + if (isa(decl)) { + continue; + } + ambiguousCandidates.push_back(decl); + } + if (ambiguousCandidates.size() > 1) { + auto attrName = nominals.front()->getName(); + ctx.Diags.diagnose(typeRepr->getLoc(), + diag::ambiguous_custom_attribute_ref, attrName); + for (auto candidate : ambiguousCandidates) { + ctx.Diags.diagnose(candidate->getLoc(), + diag::found_attribute_candidate); + // If the candidate is a top-level attribute, let's suggest + // adding module name to resolve the ambiguity. + if (candidate->getDeclContext()->isModuleScopeContext()) { + auto moduleName = candidate->getParentModule()->getName(); + ctx.Diags + .diagnose(typeRepr->getLoc(), + diag::ambiguous_custom_attribute_ref_fix, + moduleName.str(), attrName, moduleName) + .fixItInsert(typeRepr->getLoc(), moduleName.str().str() + "."); + } + } + return nullptr; + } + } + } + + // There is no nominal type with this name, so complain about this being + // an unknown attribute. + std::string typeName; + if (auto typeRepr = attr->getTypeRepr()) { + llvm::raw_string_ostream out(typeName); + typeRepr->print(out); + } else { + typeName = attr->getType().getString(); + } + + ctx.Diags.diagnose(attr->getLocation(), diag::unknown_attribute, typeName); + return nullptr; } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 94478abf4f50f..df4080e85a1ba 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2901,18 +2901,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { auto nominal = evaluateOrDefault( Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); - // If there is no nominal type with this name, complain about this being - // an unknown attribute. if (!nominal) { - std::string typeName; - if (auto typeRepr = attr->getTypeRepr()) { - llvm::raw_string_ostream out(typeName); - typeRepr->print(out); - } else { - typeName = attr->getType().getString(); - } - - diagnose(attr->getLocation(), diag::unknown_attribute, typeName); attr->setInvalid(); return; } diff --git a/test/NameLookup/Inputs/custom_attrs_A.swift b/test/NameLookup/Inputs/custom_attrs_A.swift new file mode 100644 index 0000000000000..c8035b4fa167e --- /dev/null +++ b/test/NameLookup/Inputs/custom_attrs_A.swift @@ -0,0 +1,13 @@ +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +@_functionBuilder +public struct Builder { + static func buildBlock(_ component: T) -> T { component } +} diff --git a/test/NameLookup/Inputs/custom_attrs_B.swift b/test/NameLookup/Inputs/custom_attrs_B.swift new file mode 100644 index 0000000000000..c8035b4fa167e --- /dev/null +++ b/test/NameLookup/Inputs/custom_attrs_B.swift @@ -0,0 +1,13 @@ +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +@_functionBuilder +public struct Builder { + static func buildBlock(_ component: T) -> T { component } +} diff --git a/test/NameLookup/custom_attrs_ambiguous.swift b/test/NameLookup/custom_attrs_ambiguous.swift new file mode 100644 index 0000000000000..b549e34e273ba --- /dev/null +++ b/test/NameLookup/custom_attrs_ambiguous.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_A.swift +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_B.swift +// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t %s +import custom_attrs_A +import custom_attrs_B + +// SR-13470 + +struct Test { + @Wrapper var x: Int = 17 + // expected-error@-1 {{ambiguous use of attribute 'Wrapper'}} + // expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Wrapper' in module 'custom_attrs_A'}} {{4-4=custom_attrs_A.}} + // expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Wrapper' in module 'custom_attrs_B'}} {{4-4=custom_attrs_B.}} + + init(@Builder closure: () -> Int) {} + // expected-error@-1 {{ambiguous use of attribute 'Builder'}} + // expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Builder' in module 'custom_attrs_A'}} {{9-9=custom_attrs_A.}} + // expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Builder' in module 'custom_attrs_B'}} {{9-9=custom_attrs_B.}} +} + From 7a6f84dc644607114237d85571d5a747c13bca71 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Fri, 28 Aug 2020 20:50:20 -0700 Subject: [PATCH 114/120] When building standalone stdlib, explicitly use C/C++ compilers from NATIVE_CLANG_TOOLS_PATH (#33675) --- utils/build-script-impl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/build-script-impl b/utils/build-script-impl index 1e80510fa7ad2..4c4d499371b9a 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1407,6 +1407,13 @@ for host in "${ALL_HOSTS[@]}"; do fi fi + if [[ "${NATIVE_CLANG_TOOLS_PATH}" ]] ; then + common_cmake_options_host+=( + -DCMAKE_C_COMPILER="${NATIVE_CLANG_TOOLS_PATH}/clang" + -DCMAKE_CXX_COMPILER="${NATIVE_CLANG_TOOLS_PATH}/clang++" + ) + fi + llvm_cmake_options=( "${llvm_cmake_options[@]}" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" From 73f427369fc7ec1c7e13ae91aabf0a0829e3d22e Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Fri, 28 Aug 2020 20:50:47 -0700 Subject: [PATCH 115/120] Mark Reflection_jit.swift as UNSUPPORTED for standalone stdlib builds (#33682) --- test/stdlib/Reflection_jit.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/stdlib/Reflection_jit.swift b/test/stdlib/Reflection_jit.swift index c36b6b6e8cc9f..8704ba6069ceb 100644 --- a/test/stdlib/Reflection_jit.swift +++ b/test/stdlib/Reflection_jit.swift @@ -2,3 +2,8 @@ // RUN: %target-jit-run -parse-stdlib %S/Reflection.swift -- %S/Inputs/shuffle.jpg | %FileCheck %S/Reflection.swift // REQUIRES: swift_interpreter + +// Only run this test when we build host tools (e.g. bin/swift-frontend), avoid running it for standalone stdlib builds. +// Standalone stdlib builds use downloadable toolchains from swift.org, which are codesigned in a way that doesn't let +// the interpreter process (swift-frontend) dynamically load locally-built modules (libswiftCore). +// REQUIRES: swift_tools_extra From dbd16d2e05108bc922ab87fc40f2a0b7891ea5e9 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Fri, 28 Aug 2020 18:56:17 -0700 Subject: [PATCH 116/120] swift_build_sdk_interfaces.py: adjust output directory path to an SDK-versioned directory When clients specify an output directory without SDK version, the script will change the input so we emit all prebuilt module caches into SDK-versioned directories without modifying the client side. rdar://67951012 --- utils/swift_build_sdk_interfaces.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/utils/swift_build_sdk_interfaces.py b/utils/swift_build_sdk_interfaces.py index 900bae30064e0..c8381193a9bd4 100755 --- a/utils/swift_build_sdk_interfaces.py +++ b/utils/swift_build_sdk_interfaces.py @@ -354,6 +354,14 @@ def process_module_files(pool, module_files): return overall_exit_status +def getSDKVersion(sdkroot): + settingPath = os.path.join(sdkroot, 'SDKSettings.json') + with open(settingPath) as json_file: + data = json.load(json_file) + return data['Version'] + fatal("Failed to get SDK version from: " + settingPath) + + def main(): global args, shared_output_lock parser = create_parser() @@ -373,6 +381,12 @@ def main(): if not os.path.isdir(args.sdk): fatal("invalid SDK: " + args.sdk) + # if the given output dir ends with 'prebuilt-modules', we should + # append the SDK version number so all modules will built into + # the SDK-versioned sub-directory. + if os.path.basename(args.output_dir) == 'prebuilt-modules': + args.output_dir = os.path.join(args.output_dir, getSDKVersion(args.sdk)) + xfails = () if args.ignore_non_stdlib_failures: if args.xfails: From c34212aa20019141f4bccd0f2cb7fc0ffe217303 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 28 Aug 2020 20:00:21 -0700 Subject: [PATCH 117/120] Turn on Verbose Output For Test --- validation-test/BuildSystem/skip-local-build.test-sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation-test/BuildSystem/skip-local-build.test-sh b/validation-test/BuildSystem/skip-local-build.test-sh index b658957c70a85..36657e3d6fa37 100644 --- a/validation-test/BuildSystem/skip-local-build.test-sh +++ b/validation-test/BuildSystem/skip-local-build.test-sh @@ -1,8 +1,8 @@ # REQUIRES: standalone_build # -# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG +# RUN: %swift_src_root/utils/build-script --dump-config --skip-local-build 2>&1 | %FileCheck %s -check-prefix=CONFIG --dump-input=always # CONFIG: "skip_local_build": true -# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY +# RUN: %swift_src_root/utils/build-script --dry-run --verbose-build --skip-local-build 2>&1 | %FileCheck %s -check-prefix=DRY --dump-input=always # DRY: build-script-impl # DRY-SAME: --skip-local-build From a412e48065dece9681451127504d20ccdc2065d7 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 29 Aug 2020 11:09:54 -0700 Subject: [PATCH 118/120] Revert "Add A Reproducer For rdar://48443680" This reverts commit 57f2944aad1d08d9bbd530a8534e52895c783eb1. --- .../ClangImporter/ClangImporterTests.cpp | 107 +----------------- 1 file changed, 1 insertion(+), 106 deletions(-) diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index 4068678fd8433..e353fba57745f 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -6,10 +6,6 @@ #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterOptions.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/FileSystemStatCache.h" -#include "clang/Frontend/CompilerInstance.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -58,6 +54,7 @@ TEST(ClangImporterTest, emitPCHInMemory) { // Create the includes. std::string include = createFilename(temp, "include"); ASSERT_FALSE(llvm::sys::fs::create_directory(include)); + options.ExtraArgs.emplace_back("-nosysteminc"); options.ExtraArgs.emplace_back((llvm::Twine("-I") + include).str()); ASSERT_FALSE(emitFileWithContents(include, "module.modulemap", "module A {\n" @@ -91,105 +88,3 @@ TEST(ClangImporterTest, emitPCHInMemory) { ASSERT_FALSE(emitFileWithContents(PCH, "garbage")); ASSERT_TRUE(importer->canReadPCH(PCH)); } - -class ForgetfulStatCache : public clang::FileSystemStatCache { -private: - std::unique_ptr RealStatCache; - -public: - ForgetfulStatCache() { - RealStatCache = std::make_unique(); - } - ~ForgetfulStatCache() = default; - - std::error_code getStat(StringRef Path, llvm::vfs::Status &Status, - bool isFile, - std::unique_ptr *F, - llvm::vfs::FileSystem &FS) override { - if (llvm::sys::path::extension(Path) == ".pcm") { - const auto UID = llvm::sys::fs::UniqueID(1, std::numeric_limits::max()); - Status = llvm::vfs::Status(Path, UID, - /*MTime*/{}, /*User*/0, /*Group*/0, - /*Size*/0, - llvm::sys::fs::file_type::regular_file, - llvm::sys::fs::perms::all_all); - return std::error_code(); - } - - return clang::FileSystemStatCache::get(Path, Status, isFile, F, - RealStatCache.get(), FS); - } -}; - -TEST(ClangImporterTest, missingSubmodule) { - // Create a temporary cache on disk and clean it up at the end. - ClangImporterOptions options; - SmallString<256> temp; - ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory( - "ClangImporterTest.missingSubmodule", temp)); - SWIFT_DEFER { llvm::sys::fs::remove_directories(temp); }; - - // Create a cache subdirectory for the modules and PCH. - std::string cache = createFilename(temp, "cache"); - ASSERT_FALSE(llvm::sys::fs::create_directory(cache)); - options.ModuleCachePath = cache; - options.PrecompiledHeaderOutputDir = cache; - - // Create the includes. - std::string include1 = createFilename(temp, "include1"); - ASSERT_FALSE(llvm::sys::fs::create_directory(include1)); - - std::string include2 = createFilename(temp, "include2"); - ASSERT_FALSE(llvm::sys::fs::create_directory(include2)); - - options.ExtraArgs.emplace_back((llvm::Twine("-I") + include1).str()); - options.ExtraArgs.emplace_back((llvm::Twine("-I") + include2).str()); - options.ExtraArgs.emplace_back("-DEXTRA_C_DEFINE=2"); - - ASSERT_FALSE(emitFileWithContents(include1, "module.modulemap", - "module CLib1 {\n" - " umbrella header \"" + include1 + "/Clib1.h\"\n" - " export * \n" - "}\n" - "module CLib2 {\n" - " umbrella header \"" + include2 + "/Clib2.h\"\n" - " export * \n" - "}\n")); - ASSERT_FALSE(emitFileWithContents(include1, "CLib1.h", - "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" - "#error \"unexpected compiler flags\"\n" - "#endif\n" - "void foo(void);\n")); - ASSERT_FALSE(emitFileWithContents(include2, "CLib2.h", - "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" - "#error \"unexpected compiler flags\"\n" - "#endif\n" - "void foo(void);\n")); - - // Set up the importer. - swift::LangOptions langOpts; - langOpts.Target = llvm::Triple("x86_64", "apple", "darwin"); - swift::TypeCheckerOptions typeckOpts; - INITIALIZE_LLVM(); - swift::SearchPathOptions searchPathOpts; - swift::SourceManager sourceMgr; - swift::DiagnosticEngine diags(sourceMgr); - std::unique_ptr context( - ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags)); - auto importer = ClangImporter::create(*context, options); - - // Install a stats cache that conveniently "forgets" that PCMs have different - // underlying FileEntry values. - importer->getClangInstance() - .getFileManager() - .setStatCache(std::make_unique()); - - context->addModuleLoader(std::move(importer)); - - auto CLib1 = context->getIdentifier("CLib1"); - auto CLib2 = context->getIdentifier("CLib2"); - // The first load succeeds and primes the ModuleManager with PCM data. - (void)context->getModule({ Located{ CLib1, {} } }); - // The second load fails because we collide in the ModuleManager. - ASSERT_TRUE(context->getModule({ Located{ CLib2, {} } }) == nullptr); -} From fa746766c89c0d20e216a47c141fbf3e55235c91 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sun, 30 Aug 2020 17:32:42 +0000 Subject: [PATCH 119/120] [TF] Update AutoDiff test. `-print-ast` no longer prints the AST if diagnostics are emitted. Edit test case to no longer emit diagnostics. --- ...onpublic_differentiable_attr_type_checking.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/AutoDiff/downstream/implicit_nonpublic_differentiable_attr_type_checking.swift b/test/AutoDiff/downstream/implicit_nonpublic_differentiable_attr_type_checking.swift index 63ca9f642166b..36532957495b9 100644 --- a/test/AutoDiff/downstream/implicit_nonpublic_differentiable_attr_type_checking.swift +++ b/test/AutoDiff/downstream/implicit_nonpublic_differentiable_attr_type_checking.swift @@ -3,24 +3,12 @@ // Test implicit `@differentiable` attributes for non-public protocol witnesses. protocol InternalProtocol: Differentiable { - // expected-note @+3 {{protocol requires function 'publicMethod' with type '(Float) -> Float'}} - @differentiable(wrt: self) - @differentiable(wrt: (self, x)) - func publicMethod(_ x: Float) -> Float - @differentiable(wrt: self) @differentiable(wrt: (self, x)) func internalMethod(_ x: Float) -> Float } -// expected-error @+1 {{type 'PublicConformingStruct' does not conform to protocol 'InternalProtocol'}} public struct PublicConformingStruct: InternalProtocol { - // Expected: error for missing `@differentiable` attribute on public protocol witness. - // expected-note @+1 {{candidate is missing attribute '@differentiable'}} - public func publicMethod(_ x: Float) -> Float { - x - } - // Expected: no error for missing `@differentiable` attribute on internal protocol witness. // Implicit `@differentiable` attributes should be created. func internalMethod(_ x: Float) -> Float { @@ -29,7 +17,6 @@ public struct PublicConformingStruct: InternalProtocol { } // CHECK-LABEL: public struct PublicConformingStruct : InternalProtocol { -// CHECK: public func publicMethod(_ x: Float) -> Float // CHECK: @differentiable(wrt: (self, x)) // CHECK: @differentiable(wrt: self) // CHECK: internal func internalMethod(_ x: Float) -> Float From 9216b35fafc8a2ecd8f7e63929ef3bf53b847e25 Mon Sep 17 00:00:00 2001 From: Denys Shabalin Date: Wed, 2 Sep 2020 16:55:19 +0200 Subject: [PATCH 120/120] update-checkout-config.json: adjust for tensorflow merge --- .../update-checkout-config.json | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index 23dc17101acab..bd3fe3346c470 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -348,26 +348,26 @@ "tensorflow": { "aliases": ["tensorflow"], "repos": { - "llvm-project": "8215885fbe34c8d3d2a472e4b731d04c42f190bc", + "llvm-project": "14a10d635b229fb3d8fc0d80340f98430b46fcff", "swift": "tensorflow", - "cmark": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "llbuild": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", + "cmark": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "llbuild": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", "swift-tools-support-core": "0.1.8", - "swiftpm": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", + "swiftpm": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", "swift-argument-parser": "0.3.0", "swift-driver": "b590b0c8e8a26c68602af7224af1bb203f23c31a", - "swift-syntax": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "swift-stress-tester": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "swift-corelibs-xctest": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "swift-corelibs-foundation": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "swift-corelibs-libdispatch": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "swift-integration-tests": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "swift-xcode-playground-support": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", + "swift-syntax": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "swift-stress-tester": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "swift-corelibs-xctest": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "swift-corelibs-foundation": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "swift-corelibs-libdispatch": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "swift-integration-tests": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "swift-xcode-playground-support": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", "ninja": "release", "icu": "release-65-1", "yams": "3.0.1-patched", - "indexstore-db": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", - "sourcekit-lsp": "swift-DEVELOPMENT-SNAPSHOT-2020-08-11-a", + "indexstore-db": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", + "sourcekit-lsp": "swift-DEVELOPMENT-SNAPSHOT-2020-08-31-a", "PythonKit": "master", "swift-format": "master", "tensorflow": "v2.3.0",