From 880698ef3f6364171828f4e168744c7023f01d85 Mon Sep 17 00:00:00 2001 From: NevinBR Date: Sat, 10 Mar 2018 21:18:37 -0500 Subject: [PATCH 001/237] Fixed typos and grammar in README.md --- lib/Syntax/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Syntax/README.md b/lib/Syntax/README.md index 55d0f42463f86..d866a01d7d130 100644 --- a/lib/Syntax/README.md +++ b/lib/Syntax/README.md @@ -11,7 +11,7 @@ representation of source, and facilities for *structured editing*. What is structured editing? It's an editing strategy that is keenly aware of the *structure* of source code, not necessarily its *representation* (i.e. characters or bytes). This can be achieved at different granularities: replacing -an identifier, changing a call to global function to a method call, or indenting +an identifier, changing a global function call to a method call, or indenting and formatting an entire source file based on declarative rules. These kinds of diverse operations are critical to the Swift Migrator, which is the immediate client for this library, now developed in the open. Along with that, the library @@ -163,8 +163,8 @@ struct YourStruct {} At any point in the building process, you can call `build()` and get a reasonably formed Syntax node (i.e. with no raw `nullptr`s) using what you've provided to the builder so far. Anything that you haven't supplied is marked as -*missing*. This is essentially what the parser does so, looking forward to -future adoption, the builders are designed with the parser in mind, with the +*missing*. This is essentially what the parser does, so looking forward to +future adoption the builders are designed with the parser in mind, with the hope that we can better specify recovery behavior and incremental (re-)parsing. **Example** @@ -175,7 +175,7 @@ StructDeclSyntaxBuilder Builder; // We previously parsed a struct keyword, let's tell the builder to use it. Builder.useStructKeyword(StructKeyword); -// Hm, we didn't see an identifier, but saw a left brace. Let's keep going. +// Hmm, we didn't see an identifier, but saw a left brace. Let's keep going. Builder.useLeftBrace(ParsedLeftBrace) // No members of the struct; we saw a right brace. @@ -387,7 +387,8 @@ Beyond this, `SyntaxData` nodes have *no significant public API*. - `SyntaxData` are immutable. However, they may mutate themselves in order to implement lazy instantiation - of children and caching. That caching operation transparent and thread-safe. + of children and caching. That caching operation is transparent and + thread-safe. - `SyntaxData` have identity, i.e. they can be compared with "pointer equality". - `SyntaxData` are implementation detail have no public API. @@ -428,7 +429,7 @@ auto ReturnKW = SyntaxFactory::makeReturnKeyword({}, Trivia::spaces(1)); auto Return = SyntaxFactory::makeReturnStmt(ReturnKW, Integer, /*Semicolon=*/ None); -auto RightBrace = SyntaxFactory::makeLeftBraceToken({}, {}); +auto RightBrace = SyntaxFactory::makeRightBraceToken({}, {}); auto Statements = SyntaxFactory::makeBlankStmtList() .addStmt(Return); From e0ff39becd4fe488ce7401eb974914ecfee2cf33 Mon Sep 17 00:00:00 2001 From: NevinBR Date: Wed, 14 Mar 2018 14:40:32 -0400 Subject: [PATCH 002/237] Fixed typos and grammar in lib/Syntax/README.md Incorporated suggestions from others. --- lib/Syntax/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Syntax/README.md b/lib/Syntax/README.md index d866a01d7d130..a6a2e921e83c7 100644 --- a/lib/Syntax/README.md +++ b/lib/Syntax/README.md @@ -163,8 +163,8 @@ struct YourStruct {} At any point in the building process, you can call `build()` and get a reasonably formed Syntax node (i.e. with no raw `nullptr`s) using what you've provided to the builder so far. Anything that you haven't supplied is marked as -*missing*. This is essentially what the parser does, so looking forward to -future adoption the builders are designed with the parser in mind, with the +*missing*. This is essentially what the parser does, so—looking forward to +future adoption—the builders are designed with the parser in mind, with the hope that we can better specify recovery behavior and incremental (re-)parsing. **Example** @@ -175,7 +175,7 @@ StructDeclSyntaxBuilder Builder; // We previously parsed a struct keyword, let's tell the builder to use it. Builder.useStructKeyword(StructKeyword); -// Hmm, we didn't see an identifier, but saw a left brace. Let's keep going. +// Hm, we didn't see an identifier, but saw a left brace. Let's keep going. Builder.useLeftBrace(ParsedLeftBrace) // No members of the struct; we saw a right brace. From ae3f14297082c09ceb65c4e69cc973f5c99a66e4 Mon Sep 17 00:00:00 2001 From: NevinBR Date: Fri, 23 Mar 2018 11:05:06 -0400 Subject: [PATCH 003/237] Fixed typos and grammar in lib/Syntax/README.md Added a semicolon to a run-on sentence, and restored the original commas there. --- lib/Syntax/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Syntax/README.md b/lib/Syntax/README.md index a6a2e921e83c7..a594f27b7b4ef 100644 --- a/lib/Syntax/README.md +++ b/lib/Syntax/README.md @@ -163,8 +163,8 @@ struct YourStruct {} At any point in the building process, you can call `build()` and get a reasonably formed Syntax node (i.e. with no raw `nullptr`s) using what you've provided to the builder so far. Anything that you haven't supplied is marked as -*missing*. This is essentially what the parser does, so—looking forward to -future adoption—the builders are designed with the parser in mind, with the +*missing*. This is essentially what the parser does; so, looking forward to +future adoption, the builders are designed with the parser in mind, with the hope that we can better specify recovery behavior and incremental (re-)parsing. **Example** From 210ec69a1bc8703044df08039af9785cd0e2fd0b Mon Sep 17 00:00:00 2001 From: taylorswift Date: Sun, 1 Sep 2019 13:41:16 -0500 Subject: [PATCH 004/237] comparable enums --- include/swift/AST/ASTContext.h | 3 + include/swift/AST/KnownIdentifiers.def | 2 + lib/AST/ASTContext.cpp | 19 +- lib/Sema/DerivedConformanceComparable.cpp | 233 ++++++++++++++++++ .../DerivedConformanceEquatableHashable.cpp | 80 +----- lib/Sema/DerivedConformances.cpp | 83 ++++++- lib/Sema/DerivedConformances.h | 21 ++ test/Sema/enum_conformance_synthesis.swift | 34 ++- test/stdlib/EnumComparableTests.swift | 31 +++ 9 files changed, 418 insertions(+), 88 deletions(-) create mode 100644 lib/Sema/DerivedConformanceComparable.cpp create mode 100644 test/stdlib/EnumComparableTests.swift diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index d1dc64ffc998c..2f6584ed2ab5a 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -514,6 +514,9 @@ class ASTContext final { KnownProtocolKind builtinProtocol, llvm::function_ref initName) const; + /// Retrieve the declaration of Swift.<(Int, Int) -> Bool. + FuncDecl *getLessThanIntDecl() const; + /// Retrieve the declaration of Swift.==(Int, Int) -> Bool. FuncDecl *getEqualIntDecl() const; diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 60d0eec59de44..0115cede5ad46 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -140,7 +140,9 @@ IDENTIFIER_WITH_NAME(NativeClassLayout, "_NativeClass") // Operators IDENTIFIER_WITH_NAME(MatchOperator, "~=") +IDENTIFIER_WITH_NAME(LessThanOperator, "<") IDENTIFIER_WITH_NAME(EqualsOperator, "==") +IDENTIFIER_WITH_NAME(derived_enum_less_than, "__derived_enum_less_than") IDENTIFIER_WITH_NAME(derived_enum_equals, "__derived_enum_equals") IDENTIFIER_WITH_NAME(derived_struct_equals, "__derived_struct_equals") diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6b9d67f6a4308..e6957ddeb7b2b 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -226,6 +226,9 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) #define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr; #include "swift/AST/KnownDecls.def" + /// func Bool + FuncDecl *LessThanIntDecl = nullptr; + /// func ==(Int, Int) -> Bool FuncDecl *EqualIntDecl = nullptr; @@ -1080,9 +1083,9 @@ ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, return witness; } -FuncDecl *ASTContext::getEqualIntDecl() const { - if (getImpl().EqualIntDecl) - return getImpl().EqualIntDecl; +FuncDecl *ASTContext::getBinaryComparisonOperatorIntDecl(StringRef op, FuncDecl **cached) const { + if (*cached) + return *cached; if (!getIntDecl() || !getBoolDecl()) return nullptr; @@ -1093,7 +1096,7 @@ FuncDecl *ASTContext::getEqualIntDecl() const { param.getPlainType()->isEqual(intType)); }; auto boolType = getBoolDecl()->getDeclaredType(); - auto decl = lookupOperatorFunc(*this, "==", + auto decl = lookupOperatorFunc(*this, op, intType, [=](FunctionType *type) { // Check for the signature: (Int, Int) -> Bool if (type->getParams().size() != 2) return false; @@ -1101,9 +1104,15 @@ FuncDecl *ASTContext::getEqualIntDecl() const { !isIntParam(type->getParams()[1])) return false; return type->getResult()->isEqual(boolType); }); - getImpl().EqualIntDecl = decl; + *cached = decl; return decl; } +FuncDecl *ASTContext::getLessThanIntDecl() const { + return getBinaryComparisonOperatorIntDecl("<", &getImpl().LessThanIntDecl); +} +FuncDecl *ASTContext::getEqualIntDecl() const { + return getBinaryComparisonOperatorIntDecl("==", &getImpl().EqualIntDecl); +} FuncDecl *ASTContext::getHashValueForDecl() const { if (getImpl().HashValueForDecl) diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp new file mode 100644 index 0000000000000..0df053e2da9d7 --- /dev/null +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -0,0 +1,233 @@ +//===--- DerivedConformanceComparable.cpp - Derived Comparable -===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements implicit derivation of the Comparable protocol. +// (Most of this code is similar to code in `DerivedConformanceEquatableHashable.cpp`) +// +//===----------------------------------------------------------------------===// + +#include "TypeChecker.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Stmt.h" +#include "swift/AST/Expr.h" +#include "swift/AST/Module.h" +#include "swift/AST/Pattern.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/ProtocolConformance.h" +#include "swift/AST/Types.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include "DerivedConformances.h" + +using namespace swift; + +// how does this code ever even get invoked? you can’t compare uninhabited enums... +static void +deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { + auto parentDC = ltDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto args = ltDecl->getParameters(); + auto aParam = args->get(0); + auto bParam = args->get(1); + + assert(!cast(aParam->getType()->getAnyNominal())->hasCases()); + + SmallVector statements; + SmallVector cases; + + // switch (a, b) { } + auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/ true); + auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/ true); + auto abExpr = TupleExpr::create(C, SourceLoc(), {aRef, bRef}, {}, {}, + SourceLoc(), /*HasTrailingClosure*/ false, + /*implicit*/ true); + auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), abExpr, + SourceLoc(), cases, SourceLoc(), C); + statements.push_back(switchStmt); + + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); + ltDecl->setBody(body); +} + +/// Derive the body for a '<' operator for an enum that has no associated +/// values. This generates code that converts each value to its integer ordinal +/// and compares them, which produces an optimal single icmp instruction. +static void +deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, + void *) { + auto parentDC = ltDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto args = ltDecl->getParameters(); + auto aParam = args->get(0); + auto bParam = args->get(1); + + auto enumDecl = cast(aParam->getType()->getAnyNominal()); + + // Generate the conversion from the enums to integer indices. + SmallVector statements; + DeclRefExpr *aIndex = convertEnumToIndex(statements, parentDC, enumDecl, + aParam, ltDecl, "index_a"); + DeclRefExpr *bIndex = convertEnumToIndex(statements, parentDC, enumDecl, + bParam, ltDecl, "index_b"); + + // Generate the compare of the indices. + FuncDecl *cmpFunc = C.getLessThanIntDecl(); + assert(cmpFunc && "should have a < for int as we already checked for it"); + + auto fnType = cmpFunc->getInterfaceType()->castTo(); + + Expr *cmpFuncExpr; + if (cmpFunc->getDeclContext()->isTypeContext()) { + auto contextTy = cmpFunc->getDeclContext()->getSelfInterfaceType(); + Expr *base = TypeExpr::createImplicitHack(SourceLoc(), contextTy, C); + Expr *ref = new (C) DeclRefExpr(cmpFunc, DeclNameLoc(), /*Implicit*/ true, + AccessSemantics::Ordinary, fnType); + + fnType = fnType->getResult()->castTo(); + cmpFuncExpr = new (C) DotSyntaxCallExpr(ref, SourceLoc(), base, fnType); + cmpFuncExpr->setImplicit(); + } else { + cmpFuncExpr = new (C) DeclRefExpr(cmpFunc, DeclNameLoc(), + /*implicit*/ true, + AccessSemantics::Ordinary, + fnType); + } + + TupleExpr *abTuple = TupleExpr::create(C, SourceLoc(), { aIndex, bIndex }, + { }, { }, SourceLoc(), + /*HasTrailingClosure*/ false, + /*Implicit*/ true); + + auto *cmpExpr = new (C) BinaryExpr(cmpFuncExpr, abTuple, /*implicit*/ true); + statements.push_back(new (C) ReturnStmt(SourceLoc(), cmpExpr)); + + BraceStmt *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); + ltDecl->setBody(body); +} + +/// Derive an '<' operator implementation for an enum. +static ValueDecl * +deriveComparable_lt(DerivedConformance &derived, + void (*bodySynthesizer)(AbstractFunctionDecl *, void *)) { + ASTContext &C = derived.TC.Context; + + auto parentDC = derived.getConformanceContext(); + auto selfIfaceTy = parentDC->getDeclaredInterfaceType(); + + auto getParamDecl = [&](StringRef s) -> ParamDecl * { + auto *param = new (C) ParamDecl(VarDecl::Specifier::Default, SourceLoc(), + SourceLoc(), Identifier(), SourceLoc(), + C.getIdentifier(s), parentDC); + param->setInterfaceType(selfIfaceTy); + return param; + }; + + ParameterList *params = ParameterList::create(C, { + getParamDecl("a"), + getParamDecl("b") + }); + + auto boolTy = C.getBoolDecl()->getDeclaredType(); + + Identifier generatedIdentifier; + if (parentDC->getParentModule()->isResilient()) { + generatedIdentifier = C.Id_LessThanOperator; + } else { + assert(selfIfaceTy->getEnumOrBoundGenericEnum()); + generatedIdentifier = C.Id_derived_enum_less_than; + } + + DeclName name(C, generatedIdentifier, params); + auto comparableDecl = + FuncDecl::create(C, /*StaticLoc=*/SourceLoc(), + StaticSpellingKind::KeywordStatic, + /*FuncLoc=*/SourceLoc(), name, /*NameLoc=*/SourceLoc(), + /*Throws=*/false, /*ThrowsLoc=*/SourceLoc(), + /*GenericParams=*/nullptr, + params, + TypeLoc::withoutLoc(boolTy), + parentDC); + comparableDecl->setImplicit(); + comparableDecl->setUserAccessible(false); + comparableDecl->getAttrs().add(new (C) InfixAttr(/*implicit*/false)); + + // Add the @_implements(Comparable, < (_:_:)) attribute + if (generatedIdentifier != C.Id_LessThanOperator) { + auto comparable = C.getProtocol(KnownProtocolKind::Comparable); + auto comparableType = comparable->getDeclaredType(); + auto comparableTypeLoc = TypeLoc::withoutLoc(comparableType); + SmallVector argumentLabels = { Identifier(), Identifier() }; + auto comparableDeclName = DeclName(C, DeclBaseName(C.Id_LessThanOperator), + argumentLabels); + comparableDecl->getAttrs().add(new (C) ImplementsAttr(SourceLoc(), + SourceRange(), + comparableTypeLoc, + comparableDeclName, + DeclNameLoc())); + } + + if (!C.getEqualIntDecl()) { + derived.TC.diagnose(derived.ConformanceDecl->getLoc(), + diag::no_equal_overload_for_int); + return nullptr; + } + + comparableDecl->setBodySynthesizer(bodySynthesizer); + + // Compute the interface type. + if (auto genericEnv = parentDC->getGenericEnvironmentOfContext()) + comparableDecl->setGenericEnvironment(genericEnv); + comparableDecl->computeType(); + + comparableDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); + comparableDecl->setValidationToChecked(); + + C.addSynthesizedDecl(comparableDecl); + + // Add the operator to the parent scope. + derived.addMembersToConformanceContext({comparableDecl}); + + return comparableDecl; +} + +bool +DerivedConformance::canDeriveComparable(DeclContext *context, NominalTypeDecl *declaration) { + // The type must be an enum. + if (EnumDecl const *const enumeration = dyn_cast(declaration)) { + // The cases must not have associated values or raw backing + return enumeration->hasOnlyCasesWithoutAssociatedValues() && !enumeration->hasRawType(); + } else { + return false; + } +} + +ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { + if (checkAndDiagnoseDisallowedContext(requirement)) + return nullptr; + + // Build the necessary decl. + if (requirement->getBaseName() == "<") { + if (EnumDecl const *const enumeration = dyn_cast(this->Nominal)) { + auto bodySynthesizer = enumeration->hasCases() + ? &deriveBodyComparable_enum_noAssociatedValues_lt + : &deriveBodyComparable_enum_uninhabited_lt; + return deriveComparable_lt(*this, bodySynthesizer); + } else { + llvm_unreachable("todo"); + } + } + TC.diagnose(requirement->getLoc(), diag::broken_equatable_requirement); + return nullptr; +} diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 159ad9f5e6cd6..8804ec63a2779 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -11,8 +11,7 @@ //===----------------------------------------------------------------------===// // // This file implements implicit derivation of the Equatable and Hashable -// protocols. (Comparable is similar enough in spirit that it would make -// sense to live here too when we implement its derivation.) +// protocols. // //===----------------------------------------------------------------------===// @@ -268,83 +267,6 @@ static IntegerLiteralExpr *buildIntegerLiteral(ASTContext &C, unsigned index) { return literal; } -/// Create AST statements which convert from an enum to an Int with a switch. -/// \p stmts The generated statements are appended to this vector. -/// \p parentDC Either an extension or the enum itself. -/// \p enumDecl The enum declaration. -/// \p enumVarDecl The enum input variable. -/// \p funcDecl The parent function. -/// \p indexName The name of the output variable. -/// \return A DeclRefExpr of the output variable (of type Int). -static DeclRefExpr *convertEnumToIndex(SmallVectorImpl &stmts, - DeclContext *parentDC, - EnumDecl *enumDecl, - VarDecl *enumVarDecl, - AbstractFunctionDecl *funcDecl, - const char *indexName) { - ASTContext &C = enumDecl->getASTContext(); - Type enumType = enumVarDecl->getType(); - Type intType = C.getIntDecl()->getDeclaredType(); - - auto indexVar = new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var, - /*IsCaptureList*/false, SourceLoc(), - C.getIdentifier(indexName), - funcDecl); - indexVar->setInterfaceType(intType); - indexVar->setImplicit(); - - // generate: var indexVar - Pattern *indexPat = new (C) NamedPattern(indexVar, /*implicit*/ true); - indexPat->setType(intType); - indexPat = TypedPattern::createImplicit(C, indexPat, intType); - indexPat->setType(intType); - auto *indexBind = PatternBindingDecl::createImplicit( - C, StaticSpellingKind::None, indexPat, /*InitExpr*/ nullptr, funcDecl); - - unsigned index = 0; - SmallVector cases; - for (auto elt : enumDecl->getAllElements()) { - // generate: case .: - auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, nullptr); - pat->setImplicit(); - pat->setType(enumType); - - auto labelItem = CaseLabelItem(pat); - - // generate: indexVar = - auto indexExpr = buildIntegerLiteral(C, index++); - - auto indexRef = new (C) DeclRefExpr(indexVar, DeclNameLoc(), - /*implicit*/true, - AccessSemantics::Ordinary, - LValueType::get(intType)); - auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), - indexExpr, /*implicit*/ true); - assignExpr->setType(TupleType::getEmpty(C)); - auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), - SourceLoc()); - cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, SourceLoc(), - SourceLoc(), body, - /*case body vardecls*/ None)); - } - - // generate: switch enumVar { } - auto enumRef = new (C) DeclRefExpr(enumVarDecl, DeclNameLoc(), - /*implicit*/true, - AccessSemantics::Ordinary, - enumVarDecl->getType()); - auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, - SourceLoc(), cases, SourceLoc(), C); - - stmts.push_back(indexBind); - stmts.push_back(switchStmt); - - return new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/ true, - AccessSemantics::Ordinary, intType); -} - /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns false. diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 0d843db0a0d8d..5f6f805e873fe 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -73,6 +73,10 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, // conformance. case KnownProtocolKind::Equatable: return canDeriveEquatable(DC, Nominal); + + case KnownProtocolKind::Comparable: + return !enumDecl->hasPotentiallyUnavailableCaseValue() + && canDeriveComparable(DC, Nominal); // why do y’all pass wide `NominalTypeDecl*` value when u could pass downcast `EnumDecl*` value? // "Simple" enums without availability attributes can explicitly derive // a CaseIterable conformance. @@ -100,7 +104,7 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, } // hasOnlyCasesWithoutAssociatedValues will return true for empty enums; - // empty enumas are allowed to conform as well. + // empty enums are allowed to conform as well. return enumDecl->hasOnlyCasesWithoutAssociatedValues(); } @@ -143,7 +147,9 @@ void DerivedConformance::tryDiagnoseFailedDerivation(DeclContext *DC, auto knownProtocol = protocol->getKnownProtocolKind(); if (!knownProtocol) return; - + + // Comparable on eligible type kinds should never fail + if (*knownProtocol == KnownProtocolKind::Equatable) { tryDiagnoseFailedEquatableDerivation(DC, nominal); } @@ -212,6 +218,9 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal, // Functions. if (auto func = dyn_cast(requirement)) { + if (func->isOperator() && name.getBaseName() == "<") + return getRequirement(KnownProtocolKind::Comparable); + if (func->isOperator() && name.getBaseName() == "==") return getRequirement(KnownProtocolKind::Equatable); @@ -389,3 +398,73 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( return false; } + +/// Create AST statements which convert from an enum to an Int with a switch. +/// \p stmts The generated statements are appended to this vector. +/// \p parentDC Either an extension or the enum itself. +/// \p enumDecl The enum declaration. +/// \p enumVarDecl The enum input variable. +/// \p funcDecl The parent function. +/// \p indexName The name of the output variable. +/// \return A DeclRefExpr of the output variable (of type Int). +DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, + DeclContext *parentDC, + EnumDecl *enumDecl, + VarDecl *enumVarDecl, + AbstractFunctionDecl *funcDecl, + const char *indexName) { + ASTContext &C = enumDecl->getASTContext(); + Type enumType = enumVarDecl->getType(); + Type intType = C.getIntDecl()->getDeclaredType(); + + auto indexVar = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, + /*IsCaptureList*/false, SourceLoc(), + C.getIdentifier(indexName), + funcDecl); + indexVar->setInterfaceType(intType); + indexVar->setImplicit(); + + // generate: var indexVar + Pattern *indexPat = new (C) NamedPattern(indexVar, /*implicit*/ true); + indexPat->setType(intType); + indexPat = TypedPattern::createImplicit(C, indexPat, intType); + indexPat->setType(intType); + auto *indexBind = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, indexPat, /*InitExpr*/ nullptr, funcDecl); + + unsigned index = 0; + SmallVector cases; + for (auto elt : enumDecl->getAllElements()) { + // generate: case .: + auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), + SourceLoc(), SourceLoc(), + Identifier(), elt, nullptr); + pat->setImplicit(); + + auto labelItem = CaseLabelItem(pat); + + // generate: indexVar = + auto indexExpr = IntegerLiteralExpr::createFromUnsigned(C, index++); + auto indexRef = new (C) DeclRefExpr(indexVar, DeclNameLoc(), + /*implicit*/true); + auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), + indexExpr, /*implicit*/ true); + auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, SourceLoc(), + SourceLoc(), body, + /*case body vardecls*/ None)); + } + + // generate: switch enumVar { } + auto enumRef = new (C) DeclRefExpr(enumVarDecl, DeclNameLoc(), + /*implicit*/true); + auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, + SourceLoc(), cases, SourceLoc(), C); + + stmts.push_back(indexBind); + stmts.push_back(switchStmt); + + return new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/ true, + AccessSemantics::Ordinary, intType); +} diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index 2ec98f23de6da..ceba904643c81 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -121,6 +121,20 @@ class DerivedConformance { /// \returns the derived member, which will also be added to the type. Type deriveRawRepresentable(AssociatedTypeDecl *assocType); + /// Determine if a Comparable requirement can be derived for a type. + /// + /// This is implemented for enums without associated or raw values. + /// + /// \returns True if the requirement can be derived. + static bool canDeriveComparable(DeclContext *DC, NominalTypeDecl *type); + + /// Derive an Equatable requirement for a type. + /// + /// This is implemented for enums without associated or raw values. + /// + /// \returns the derived member, which will also be added to the type. + ValueDecl *deriveComparable(ValueDecl *requirement); + /// Determine if an Equatable requirement can be derived for a type. /// /// This is implemented for enums without associated values or all-Equatable @@ -213,6 +227,13 @@ class DerivedConformance { /// \param synthesizing The decl that is being synthesized. bool checkAndDiagnoseDisallowedContext(ValueDecl *synthesizing) const; }; + +DeclRefExpr *convertEnumToIndex( SmallVectorImpl &stmts, + DeclContext *parentDC, + EnumDecl *enumDecl, + VarDecl *enumVarDecl, + AbstractFunctionDecl *funcDecl, + const char *indexName); } #endif diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index c6e6fe7ef459e..952855c7609ed 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -202,7 +202,7 @@ extension Instrument : Equatable {} extension Instrument : CaseIterable {} enum UnusedGeneric { - case a, b, c + case a, b, c } extension UnusedGeneric : CaseIterable {} @@ -289,7 +289,7 @@ case only([NotEquatable]) //expected-note {{associated value type '[NotEquatable // Conditional conformances should be able to be synthesized enum GenericDeriveExtension { - case A(T) + case A(T) } extension GenericDeriveExtension: Equatable where T: Equatable {} extension GenericDeriveExtension: Hashable where T: Hashable {} @@ -328,6 +328,36 @@ enum ImpliedMain: ImplierMain { } extension ImpliedOther: ImplierMain {} +// Comparable enum synthesis +enum Angel: Comparable { + case lily, elsa, karlie +} + +func pit(_ a: Angel, against b: Angel) -> Bool { + return a < b +} + +// enums with payloads don’t get synthesized Comparable +enum Notice: Comparable { // expected-error{{type 'Notice' does not conform to protocol 'Comparable'}} + case taylor(Int), taylornation(Int) +} + +// neither do enums with raw values +enum Track: Int, Comparable { // expected-error{{type 'Track' does not conform to protocol 'Comparable'}} + case four = 4 + case five = 5 + case six = 6 +} + +// synthesized Comparable must be explicit +enum Publicist { + case thow, paine +} + +func miss(_ a: Publicist, outsold b: Publicist) -> Bool { + return b < a // expected-error{{binary operator '<' cannot be applied to two 'Publicist' operands}} + // expected-note @-1 {{overloads for '<' exist with these partially matching parameter lists: }} +} // FIXME: Remove -verify-ignore-unknown. // :0: error: unexpected error produced: invalid redeclaration of 'hashValue' diff --git a/test/stdlib/EnumComparableTests.swift b/test/stdlib/EnumComparableTests.swift new file mode 100644 index 0000000000000..767a897fd38eb --- /dev/null +++ b/test/stdlib/EnumComparableTests.swift @@ -0,0 +1,31 @@ +// RUN: %target-run-simple-swift %t +// REQUIRES: executable_test + +import StdlibUnittest + +var SynthesizedComparableTests = TestSuite("SynthesizedComparableTests") + +SynthesizedComparableTests.test("Simple Enum sorting") { + enum Album: Comparable { + case debut, be, fearless, sn, red, roses, reputation, lover + } + + let unsorted: [Album] = [.be, .debut, .lover, .reputation, .sn, .fearless, .roses, .red] + + expectEqual(unsorted.sorted(), [.debut, .be, .fearless, .sn, .red, .roses, .reputation, .lover]) +} + +SynthesizedComparableTests.test("Simple Enum sorting with duplicates") { + enum Album: Comparable { + case debut, be, fearless, sn, red, roses, reputation, lover + } + + let unsorted: [Album] = [.be, .debut, .lover, .lover, .reputation, .sn, .sn, .fearless, .roses, .red] + + expectEqual(Album.fearless == Album.fearless, true) + expectEqual(Album.fearless < Album.red, true) + expectEqual(Album.fearless < Album.fearless, false) + expectEqual(unsorted.sorted(), [.debut, .be, .fearless, .sn, .sn, .red, .roses, .reputation, .lover, .lover]) +} + +runAllTests() From 08aa01f9aa4883a6d1e846d21e9d0985ba335bdb Mon Sep 17 00:00:00 2001 From: taylorswift Date: Wed, 18 Sep 2019 18:46:16 -0500 Subject: [PATCH 005/237] fix compiler crash --- include/swift/AST/ASTContext.h | 5 ++- include/swift/AST/DiagnosticsSema.def | 4 ++ lib/Sema/CMakeLists.txt | 2 +- lib/Sema/DerivedConformanceComparable.cpp | 44 ++++++++++++------- .../DerivedConformanceEquatableHashable.cpp | 11 ----- lib/Sema/DerivedConformances.cpp | 36 +++++++++++---- lib/Sema/TypeCheckProtocol.cpp | 3 ++ 7 files changed, 66 insertions(+), 39 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 2f6584ed2ab5a..76ff2f07da952 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -422,6 +422,9 @@ class ASTContext final { private: /// Set the lazy resolver for this context. void setLazyResolver(LazyResolver *resolver); + + // get `<` or `==` + FuncDecl *getBinaryComparisonOperatorIntDecl(StringRef op, FuncDecl **cached) const; public: /// getIdentifier - Return the uniqued and AST-Context-owned version of the @@ -513,7 +516,7 @@ class ASTContext final { ConcreteDeclRef getBuiltinInitDecl(NominalTypeDecl *decl, KnownProtocolKind builtinProtocol, llvm::function_ref initName) const; - + /// Retrieve the declaration of Swift.<(Int, Int) -> Bool. FuncDecl *getLessThanIntDecl() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 29b65ef405c9a..e3adca19cec0e 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2543,6 +2543,8 @@ ERROR(broken_case_iterable_requirement,none, "CaseIterable protocol is broken: unexpected requirement", ()) ERROR(broken_raw_representable_requirement,none, "RawRepresentable protocol is broken: unexpected requirement", ()) +ERROR(broken_comparable_requirement,none, + "Comparable protocol is broken: unexpected requirement", ()) ERROR(broken_equatable_requirement,none, "Equatable protocol is broken: unexpected requirement", ()) ERROR(broken_hashable_requirement,none, @@ -2555,6 +2557,8 @@ ERROR(broken_int_hashable_conformance,none, "Int type is broken: does not conform to Hashable", ()) ERROR(broken_int_integer_literal_convertible_conformance,none, "Int type is broken: does not conform to ExpressibleByIntegerLiteral", ()) +ERROR(no_less_than_overload_for_int,none, + "no overload of '<' for Int", ()) ERROR(no_equal_overload_for_int,none, "no overload of '==' for Int", ()) ERROR(broken_coding_key_requirement,none, diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 847f5d6150254..2518bcdcde3ce 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -26,6 +26,7 @@ add_swift_host_library(swiftSema STATIC DerivedConformanceCodable.cpp DerivedConformanceCodingKey.cpp DerivedConformanceEquatableHashable.cpp + DerivedConformanceComparable.cpp DerivedConformanceError.cpp DerivedConformanceRawRepresentable.cpp DerivedConformances.cpp @@ -69,4 +70,3 @@ target_link_libraries(swiftSema PRIVATE swiftAST swiftParse swiftSerialization) - diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index 0df053e2da9d7..95c0473b8b8fb 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -32,7 +32,7 @@ using namespace swift; // how does this code ever even get invoked? you can’t compare uninhabited enums... -static void +static std::pair deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { auto parentDC = ltDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); @@ -47,23 +47,29 @@ deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { SmallVector cases; // switch (a, b) { } - auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/ true); - auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/ true); + auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/ true, + AccessSemantics::Ordinary, + aParam->getType()); + auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/ true, + AccessSemantics::Ordinary, + bParam->getType()); + TupleTypeElt abTupleElts[2] = { aParam->getType(), bParam->getType() }; auto abExpr = TupleExpr::create(C, SourceLoc(), {aRef, bRef}, {}, {}, SourceLoc(), /*HasTrailingClosure*/ false, - /*implicit*/ true); + /*implicit*/ true, + TupleType::get(abTupleElts, C)); auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), abExpr, SourceLoc(), cases, SourceLoc(), C); statements.push_back(switchStmt); auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); - ltDecl->setBody(body); + return { body, /*isTypeChecked=*/true }; } /// Derive the body for a '<' operator for an enum that has no associated /// values. This generates code that converts each value to its integer ordinal /// and compares them, which produces an optimal single icmp instruction. -static void +static std::pair deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, void *) { auto parentDC = ltDecl->getDeclContext(); @@ -105,29 +111,35 @@ deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, fnType); } + TupleTypeElt abTupleElts[2] = { aIndex->getType(), bIndex->getType() }; TupleExpr *abTuple = TupleExpr::create(C, SourceLoc(), { aIndex, bIndex }, { }, { }, SourceLoc(), /*HasTrailingClosure*/ false, - /*Implicit*/ true); + /*Implicit*/ true, + TupleType::get(abTupleElts, C)); - auto *cmpExpr = new (C) BinaryExpr(cmpFuncExpr, abTuple, /*implicit*/ true); + auto *cmpExpr = new (C) BinaryExpr( + cmpFuncExpr, abTuple, /*implicit*/ true, + fnType->castTo()->getResult()); statements.push_back(new (C) ReturnStmt(SourceLoc(), cmpExpr)); BraceStmt *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); - ltDecl->setBody(body); + return { body, /*isTypeChecked=*/true }; } /// Derive an '<' operator implementation for an enum. static ValueDecl * -deriveComparable_lt(DerivedConformance &derived, - void (*bodySynthesizer)(AbstractFunctionDecl *, void *)) { +deriveComparable_lt( + DerivedConformance &derived, + std::pair (*bodySynthesizer)(AbstractFunctionDecl *, + void *)) { ASTContext &C = derived.TC.Context; auto parentDC = derived.getConformanceContext(); auto selfIfaceTy = parentDC->getDeclaredInterfaceType(); auto getParamDecl = [&](StringRef s) -> ParamDecl * { - auto *param = new (C) ParamDecl(VarDecl::Specifier::Default, SourceLoc(), + auto *param = new (C) ParamDecl(ParamDecl::Specifier::Default, SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), C.getIdentifier(s), parentDC); param->setInterfaceType(selfIfaceTy); @@ -161,7 +173,6 @@ deriveComparable_lt(DerivedConformance &derived, parentDC); comparableDecl->setImplicit(); comparableDecl->setUserAccessible(false); - comparableDecl->getAttrs().add(new (C) InfixAttr(/*implicit*/false)); // Add the @_implements(Comparable, < (_:_:)) attribute if (generatedIdentifier != C.Id_LessThanOperator) { @@ -178,9 +189,9 @@ deriveComparable_lt(DerivedConformance &derived, DeclNameLoc())); } - if (!C.getEqualIntDecl()) { + if (!C.getLessThanIntDecl()) { derived.TC.diagnose(derived.ConformanceDecl->getLoc(), - diag::no_equal_overload_for_int); + diag::no_less_than_overload_for_int); return nullptr; } @@ -216,7 +227,6 @@ DerivedConformance::canDeriveComparable(DeclContext *context, NominalTypeDecl *d ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { if (checkAndDiagnoseDisallowedContext(requirement)) return nullptr; - // Build the necessary decl. if (requirement->getBaseName() == "<") { if (EnumDecl const *const enumeration = dyn_cast(this->Nominal)) { @@ -228,6 +238,6 @@ ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { llvm_unreachable("todo"); } } - TC.diagnose(requirement->getLoc(), diag::broken_equatable_requirement); + TC.diagnose(requirement->getLoc(), diag::broken_comparable_requirement); return nullptr; } diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 8804ec63a2779..0f6860105e83f 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -256,17 +256,6 @@ enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, return pat; } -/// Build a type-checked integer literal. -static IntegerLiteralExpr *buildIntegerLiteral(ASTContext &C, unsigned index) { - Type intType = C.getIntDecl()->getDeclaredType(); - - auto literal = IntegerLiteralExpr::createFromUnsigned(C, index); - literal->setType(intType); - literal->setBuiltinInitializer(C.getIntBuiltinInitDecl(C.getIntDecl())); - - return literal; -} - /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns false. diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 5f6f805e873fe..859c80e87a9b3 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -399,6 +399,17 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( return false; } +/// Build a type-checked integer literal. +static IntegerLiteralExpr *buildIntegerLiteral(ASTContext &C, unsigned index) { + Type intType = C.getIntDecl()->getDeclaredType(); + + auto literal = IntegerLiteralExpr::createFromUnsigned(C, index); + literal->setType(intType); + literal->setBuiltinInitializer(C.getIntBuiltinInitDecl(C.getIntDecl())); + + return literal; +} + /// Create AST statements which convert from an enum to an Int with a switch. /// \p stmts The generated statements are appended to this vector. /// \p parentDC Either an extension or the enum itself. @@ -408,16 +419,16 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( /// \p indexName The name of the output variable. /// \return A DeclRefExpr of the output variable (of type Int). DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, - DeclContext *parentDC, - EnumDecl *enumDecl, - VarDecl *enumVarDecl, - AbstractFunctionDecl *funcDecl, - const char *indexName) { + DeclContext *parentDC, + EnumDecl *enumDecl, + VarDecl *enumVarDecl, + AbstractFunctionDecl *funcDecl, + const char *indexName) { ASTContext &C = enumDecl->getASTContext(); Type enumType = enumVarDecl->getType(); Type intType = C.getIntDecl()->getDeclaredType(); - auto indexVar = new (C) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, + auto indexVar = new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Var, /*IsCaptureList*/false, SourceLoc(), C.getIdentifier(indexName), funcDecl); @@ -440,15 +451,20 @@ DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, SourceLoc(), SourceLoc(), Identifier(), elt, nullptr); pat->setImplicit(); + pat->setType(enumType); auto labelItem = CaseLabelItem(pat); // generate: indexVar = - auto indexExpr = IntegerLiteralExpr::createFromUnsigned(C, index++); + auto indexExpr = buildIntegerLiteral(C, index++); + auto indexRef = new (C) DeclRefExpr(indexVar, DeclNameLoc(), - /*implicit*/true); + /*implicit*/true, + AccessSemantics::Ordinary, + LValueType::get(intType)); auto assignExpr = new (C) AssignExpr(indexRef, SourceLoc(), indexExpr, /*implicit*/ true); + assignExpr->setType(TupleType::getEmpty(C)); auto body = BraceStmt::create(C, SourceLoc(), ASTNode(assignExpr), SourceLoc()); cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, SourceLoc(), @@ -458,7 +474,9 @@ DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, // generate: switch enumVar { } auto enumRef = new (C) DeclRefExpr(enumVarDecl, DeclNameLoc(), - /*implicit*/true); + /*implicit*/true, + AccessSemantics::Ordinary, + enumVarDecl->getType()); auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), enumRef, SourceLoc(), cases, SourceLoc(), C); diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index c69e7bf37b3ac..1f3ada5c2af1c 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5213,6 +5213,9 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC, case KnownProtocolKind::CaseIterable: return derived.deriveCaseIterable(Requirement); + case KnownProtocolKind::Comparable: + return derived.deriveComparable(Requirement); + case KnownProtocolKind::Equatable: return derived.deriveEquatable(Requirement); From c915fb6fc3e075b81824fc02a498f08c810b3bb2 Mon Sep 17 00:00:00 2001 From: taylorswift Date: Wed, 18 Sep 2019 20:08:21 -0500 Subject: [PATCH 006/237] support associated values --- lib/Sema/DerivedConformanceComparable.cpp | 190 +++++++++++++++++- .../DerivedConformanceEquatableHashable.cpp | 126 ------------ lib/Sema/DerivedConformances.cpp | 127 ++++++++++++ lib/Sema/DerivedConformances.h | 34 ++++ 4 files changed, 344 insertions(+), 133 deletions(-) diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index 95c0473b8b8fb..424adb8e25e0a 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -31,6 +31,52 @@ using namespace swift; +/// Returns a generated guard statement that checks whether the given lhs and +/// rhs expressions are equal. If not equal, the else block for the guard +/// returns lhs < rhs. +/// \p C The AST context. +/// \p lhsExpr The first expression to compare for equality. +/// \p rhsExpr The second expression to compare for equality. +static GuardStmt *returnComparisonIfNotEqualGuard(ASTContext &C, + Expr *lhsExpr, + Expr *rhsExpr) { + SmallVector conditions; + SmallVector statements; + + // First, generate the statement for the body of the guard. + // return lhs < rhs + auto ltFuncExpr = new (C) UnresolvedDeclRefExpr( + DeclName(C.getIdentifier("<")), DeclRefKind::BinaryOperator, + DeclNameLoc()); + auto ltArgsTuple = TupleExpr::create(C, SourceLoc(), + { lhsExpr, rhsExpr }, + { }, { }, SourceLoc(), + /*HasTrailingClosure*/false, + /*Implicit*/true); + auto ltExpr = new (C) BinaryExpr(ltFuncExpr, ltArgsTuple, /*Implicit*/true); + auto returnStmt = new (C) ReturnStmt(SourceLoc(), ltExpr); + statements.emplace_back(ASTNode(returnStmt)); + + // Next, generate the condition being checked. + // lhs == rhs + auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( + DeclName(C.getIdentifier("==")), DeclRefKind::BinaryOperator, + DeclNameLoc()); + auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), + { lhsExpr, rhsExpr }, + { }, { }, SourceLoc(), + /*HasTrailingClosure*/false, + /*Implicit*/true); + auto cmpExpr = new (C) BinaryExpr(cmpFuncExpr, cmpArgsTuple, + /*Implicit*/true); + conditions.emplace_back(cmpExpr); + + // Build and return the complete guard statement. + // guard lhs == rhs else { return lhs < rhs } + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); + return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body); +} + // how does this code ever even get invoked? you can’t compare uninhabited enums... static std::pair deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { @@ -71,7 +117,7 @@ deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { /// and compares them, which produces an optimal single icmp instruction. static std::pair deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, - void *) { + void *) { auto parentDC = ltDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); @@ -127,6 +173,133 @@ deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, return { body, /*isTypeChecked=*/true }; } +/// Derive the body for an '==' operator for an enum where at least one of the +/// cases has associated values. +static std::pair +deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, void *) { + auto parentDC = ltDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + auto args = ltDecl->getParameters(); + auto aParam = args->get(0); + auto bParam = args->get(1); + + Type enumType = aParam->getType(); + auto enumDecl = cast(aParam->getType()->getAnyNominal()); + + SmallVector statements; + SmallVector cases; + unsigned elementCount = 0; + + // For each enum element, generate a case statement matching a pair containing + // the same case, binding variables for the left- and right-hand associated + // values. + for (auto elt : enumDecl->getAllElements()) { + elementCount++; + + // .(let l0, let l1, ...) + SmallVector lhsPayloadVars; + auto lhsSubpattern = enumElementPayloadSubpattern(elt, 'l', ltDecl, + lhsPayloadVars); + auto lhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), + SourceLoc(), SourceLoc(), + Identifier(), elt, + lhsSubpattern); + lhsElemPat->setImplicit(); + + // .(let r0, let r1, ...) + SmallVector rhsPayloadVars; + auto rhsSubpattern = enumElementPayloadSubpattern(elt, 'r', ltDecl, + rhsPayloadVars); + auto rhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), + SourceLoc(), SourceLoc(), + Identifier(), elt, + rhsSubpattern); + rhsElemPat->setImplicit(); + + auto hasBoundDecls = !lhsPayloadVars.empty(); + Optional> caseBodyVarDecls; + if (hasBoundDecls) { + // We allocated a direct copy of our lhs var decls for the case + // body. + auto copy = C.Allocate(lhsPayloadVars.size()); + for (unsigned i : indices(lhsPayloadVars)) { + auto *vOld = lhsPayloadVars[i]; + auto *vNew = new (C) VarDecl( + /*IsStatic*/ false, vOld->getIntroducer(), false /*IsCaptureList*/, + vOld->getNameLoc(), vOld->getName(), vOld->getDeclContext()); + vNew->setHasNonPatternBindingInit(); + vNew->setImplicit(); + copy[i] = vNew; + } + caseBodyVarDecls.emplace(copy); + } + + // case (.(let l0, let l1, ...), .(let r0, let r1, ...)) + auto caseTuplePattern = TuplePattern::create(C, SourceLoc(), { + TuplePatternElt(lhsElemPat), TuplePatternElt(rhsElemPat) }, + SourceLoc()); + caseTuplePattern->setImplicit(); + + auto labelItem = CaseLabelItem(caseTuplePattern); + + // Generate a guard statement for each associated value in the payload, + // breaking out early if any pair is unequal. (same as Equatable synthesis.) + // the else statement performs the lexicographic comparison. + SmallVector statementsInCase; + for (size_t varIdx = 0; varIdx < lhsPayloadVars.size(); varIdx++) { + auto lhsVar = lhsPayloadVars[varIdx]; + auto lhsExpr = new (C) DeclRefExpr(lhsVar, DeclNameLoc(), + /*implicit*/true); + auto rhsVar = rhsPayloadVars[varIdx]; + auto rhsExpr = new (C) DeclRefExpr(rhsVar, DeclNameLoc(), + /*Implicit*/true); + auto guardStmt = returnComparisonIfNotEqualGuard(C, lhsExpr, rhsExpr); + statementsInCase.emplace_back(guardStmt); + } + + // If none of the guard statements caused an early exit, then all the pairs + // were true. (equal) + // return false + auto falseExpr = new (C) BooleanLiteralExpr(false, SourceLoc(), + /*Implicit*/true); + auto returnStmt = new (C) ReturnStmt(SourceLoc(), falseExpr); + statementsInCase.push_back(returnStmt); + + auto body = BraceStmt::create(C, SourceLoc(), statementsInCase, + SourceLoc()); + cases.push_back(CaseStmt::create(C, SourceLoc(), labelItem, SourceLoc(), + SourceLoc(), body, caseBodyVarDecls)); + } + + // default: result = (lhs) < (rhs) + // + // We only generate this if the enum has more than one case. If it has exactly + // one case, then that single case statement is already exhaustive. + if (elementCount > 1) { + auto defaultPattern = new (C) AnyPattern(SourceLoc()); + defaultPattern->setImplicit(); + auto defaultItem = CaseLabelItem::getDefault(defaultPattern); + auto body = deriveBodyComparable_enum_noAssociatedValues_lt(ltDecl, nullptr).first; + cases.push_back(CaseStmt::create(C, SourceLoc(), defaultItem, SourceLoc(), + SourceLoc(), body, + /*case body var decls*/ None)); + } + + // switch (a, b) { } + auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/true); + auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/true); + auto abExpr = TupleExpr::create(C, SourceLoc(), { aRef, bRef }, {}, {}, + SourceLoc(), /*HasTrailingClosure*/ false, + /*implicit*/ true); + auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), abExpr, + SourceLoc(), cases, SourceLoc(), C); + statements.push_back(switchStmt); + + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); + return { body, /*isTypeChecked=*/false }; +} + /// Derive an '<' operator implementation for an enum. static ValueDecl * deriveComparable_lt( @@ -216,9 +389,10 @@ deriveComparable_lt( bool DerivedConformance::canDeriveComparable(DeclContext *context, NominalTypeDecl *declaration) { // The type must be an enum. - if (EnumDecl const *const enumeration = dyn_cast(declaration)) { - // The cases must not have associated values or raw backing - return enumeration->hasOnlyCasesWithoutAssociatedValues() && !enumeration->hasRawType(); + if (EnumDecl *const enumeration = dyn_cast(declaration)) { + // The cases must not have non-comparable associated values or raw backing + auto comparable = context->getASTContext().getProtocol(KnownProtocolKind::Comparable); + return allAssociatedValuesConformToProtocol(context, enumeration, comparable) && !enumeration->hasRawType(); } else { return false; } @@ -230,9 +404,11 @@ ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { // Build the necessary decl. if (requirement->getBaseName() == "<") { if (EnumDecl const *const enumeration = dyn_cast(this->Nominal)) { - auto bodySynthesizer = enumeration->hasCases() - ? &deriveBodyComparable_enum_noAssociatedValues_lt - : &deriveBodyComparable_enum_uninhabited_lt; + auto bodySynthesizer = !enumeration->hasCases() + ? &deriveBodyComparable_enum_uninhabited_lt + : enumeration->hasOnlyCasesWithoutAssociatedValues() + ? &deriveBodyComparable_enum_noAssociatedValues_lt + : &deriveBodyComparable_enum_hasAssociatedValues_lt; return deriveComparable_lt(*this, bodySynthesizer); } else { llvm_unreachable("todo"); diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 0f6860105e83f..a46b3e7099fdc 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -36,46 +36,6 @@ enum NonconformingMemberKind { StoredProperty }; -/// Returns the ParamDecl for each associated value of the given enum whose type -/// does not conform to a protocol -/// \p theEnum The enum whose elements and associated values should be checked. -/// \p protocol The protocol being requested. -/// \return The ParamDecl of each associated value whose type does not conform. -static SmallVector -associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, - ProtocolDecl *protocol) { - auto lazyResolver = DC->getASTContext().getLazyResolver(); - SmallVector nonconformingAssociatedValues; - for (auto elt : theEnum->getAllElements()) { - if (!elt->hasInterfaceType()) - lazyResolver->resolveDeclSignature(elt); - - auto PL = elt->getParameterList(); - if (!PL) - continue; - - for (auto param : *PL) { - auto type = param->getInterfaceType(); - if (!TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), - protocol, DC, None)) { - nonconformingAssociatedValues.push_back(param); - } - } - } - return nonconformingAssociatedValues; -} - -/// Returns true if, for every element of the given enum, it either has no -/// associated values or all of them conform to a protocol. -/// \p theEnum The enum whose elements and associated values should be checked. -/// \p protocol The protocol being requested. -/// \return True if all associated values of all elements of the enum conform. -static bool allAssociatedValuesConformToProtocol(DeclContext *DC, - EnumDecl *theEnum, - ProtocolDecl *protocol) { - return associatedValuesNotConformingToProtocol(DC, theEnum, protocol).empty(); -} - /// Returns the VarDecl of each stored property in the given struct whose type /// does not conform to a protocol. /// \p theStruct The struct whose stored properties should be checked. @@ -170,92 +130,6 @@ void diagnoseFailedDerivation(DeclContext *DC, NominalTypeDecl *nominal, } } -/// Creates a named variable based on a prefix character and a numeric index. -/// \p prefixChar The prefix character for the variable's name. -/// \p index The numeric index to append to the variable's name. -/// \p type The type of the variable. -/// \p varContext The context of the variable. -/// \return A VarDecl named with the prefix and number. -static VarDecl *indexedVarDecl(char prefixChar, int index, Type type, - DeclContext *varContext) { - ASTContext &C = varContext->getASTContext(); - - llvm::SmallString<8> indexVal; - indexVal.append(1, prefixChar); - APInt(32, index).toString(indexVal, 10, /*signed*/ false); - auto indexStr = C.AllocateCopy(indexVal); - auto indexStrRef = StringRef(indexStr.data(), indexStr.size()); - - auto varDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, - /*IsCaptureList*/true, SourceLoc(), - C.getIdentifier(indexStrRef), - varContext); - varDecl->setType(type); - varDecl->setHasNonPatternBindingInit(true); - return varDecl; -} - -/// Returns the pattern used to match and bind the associated values (if any) of -/// an enum case. -/// \p enumElementDecl The enum element to match. -/// \p varPrefix The prefix character for variable names (e.g., a0, a1, ...). -/// \p varContext The context into which payload variables should be declared. -/// \p boundVars The array to which the pattern's variables will be appended. -static Pattern* -enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, - char varPrefix, DeclContext *varContext, - SmallVectorImpl &boundVars) { - auto parentDC = enumElementDecl->getDeclContext(); - ASTContext &C = parentDC->getASTContext(); - - // No arguments, so no subpattern to match. - if (!enumElementDecl->hasAssociatedValues()) - return nullptr; - - auto argumentType = enumElementDecl->getArgumentInterfaceType(); - if (auto tupleType = argumentType->getAs()) { - // Either multiple (labeled or unlabeled) arguments, or one labeled - // argument. Return a tuple pattern that matches the enum element in arity, - // types, and labels. For example: - // case a(x: Int) => (x: let a0) - // case b(Int, String) => (let a0, let a1) - SmallVector elementPatterns; - int index = 0; - for (auto tupleElement : tupleType->getElements()) { - auto payloadVar = indexedVarDecl(varPrefix, index++, - tupleElement.getType(), varContext); - boundVars.push_back(payloadVar); - - auto namedPattern = new (C) NamedPattern(payloadVar); - namedPattern->setImplicit(); - auto letPattern = new (C) VarPattern(SourceLoc(), /*isLet*/ true, - namedPattern); - elementPatterns.push_back(TuplePatternElt(tupleElement.getName(), - SourceLoc(), letPattern)); - } - - auto pat = TuplePattern::create(C, SourceLoc(), elementPatterns, - SourceLoc()); - pat->setImplicit(); - return pat; - } - - // Otherwise, a one-argument unlabeled payload. Return a paren pattern whose - // underlying type is the same as the payload. For example: - // case a(Int) => (let a0) - auto underlyingType = argumentType->getWithoutParens(); - auto payloadVar = indexedVarDecl(varPrefix, 0, underlyingType, varContext); - boundVars.push_back(payloadVar); - - auto namedPattern = new (C) NamedPattern(payloadVar); - namedPattern->setImplicit(); - auto letPattern = new (C) VarPattern(SourceLoc(), /*isLet*/ true, - namedPattern); - auto pat = new (C) ParenPattern(SourceLoc(), letPattern, SourceLoc()); - pat->setImplicit(); - return pat; -} - /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns false. diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 859c80e87a9b3..7e3e7bf430cef 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -486,3 +486,130 @@ DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, return new (C) DeclRefExpr(indexVar, DeclNameLoc(), /*implicit*/ true, AccessSemantics::Ordinary, intType); } + +/// Returns the ParamDecl for each associated value of the given enum whose type +/// does not conform to a protocol +/// \p theEnum The enum whose elements and associated values should be checked. +/// \p protocol The protocol being requested. +/// \return The ParamDecl of each associated value whose type does not conform. +SmallVector +swift::associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, + ProtocolDecl *protocol) { + auto lazyResolver = DC->getASTContext().getLazyResolver(); + SmallVector nonconformingAssociatedValues; + for (auto elt : theEnum->getAllElements()) { + if (!elt->hasInterfaceType()) + lazyResolver->resolveDeclSignature(elt); + + auto PL = elt->getParameterList(); + if (!PL) + continue; + + for (auto param : *PL) { + auto type = param->getInterfaceType(); + if (!TypeChecker::conformsToProtocol(DC->mapTypeIntoContext(type), + protocol, DC, None)) { + nonconformingAssociatedValues.push_back(param); + } + } + } + return nonconformingAssociatedValues; +} + +/// Returns true if, for every element of the given enum, it either has no +/// associated values or all of them conform to a protocol. +/// \p theEnum The enum whose elements and associated values should be checked. +/// \p protocol The protocol being requested. +/// \return True if all associated values of all elements of the enum conform. +bool swift::allAssociatedValuesConformToProtocol(DeclContext *DC, + EnumDecl *theEnum, + ProtocolDecl *protocol) { + return associatedValuesNotConformingToProtocol(DC, theEnum, protocol).empty(); +} + +/// Returns the pattern used to match and bind the associated values (if any) of +/// an enum case. +/// \p enumElementDecl The enum element to match. +/// \p varPrefix The prefix character for variable names (e.g., a0, a1, ...). +/// \p varContext The context into which payload variables should be declared. +/// \p boundVars The array to which the pattern's variables will be appended. +Pattern* +swift::enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, + char varPrefix, DeclContext *varContext, + SmallVectorImpl &boundVars) { + auto parentDC = enumElementDecl->getDeclContext(); + ASTContext &C = parentDC->getASTContext(); + + // No arguments, so no subpattern to match. + if (!enumElementDecl->hasAssociatedValues()) + return nullptr; + + auto argumentType = enumElementDecl->getArgumentInterfaceType(); + if (auto tupleType = argumentType->getAs()) { + // Either multiple (labeled or unlabeled) arguments, or one labeled + // argument. Return a tuple pattern that matches the enum element in arity, + // types, and labels. For example: + // case a(x: Int) => (x: let a0) + // case b(Int, String) => (let a0, let a1) + SmallVector elementPatterns; + int index = 0; + for (auto tupleElement : tupleType->getElements()) { + auto payloadVar = indexedVarDecl(varPrefix, index++, + tupleElement.getType(), varContext); + boundVars.push_back(payloadVar); + + auto namedPattern = new (C) NamedPattern(payloadVar); + namedPattern->setImplicit(); + auto letPattern = new (C) VarPattern(SourceLoc(), /*isLet*/ true, + namedPattern); + elementPatterns.push_back(TuplePatternElt(tupleElement.getName(), + SourceLoc(), letPattern)); + } + + auto pat = TuplePattern::create(C, SourceLoc(), elementPatterns, + SourceLoc()); + pat->setImplicit(); + return pat; + } + + // Otherwise, a one-argument unlabeled payload. Return a paren pattern whose + // underlying type is the same as the payload. For example: + // case a(Int) => (let a0) + auto underlyingType = argumentType->getWithoutParens(); + auto payloadVar = indexedVarDecl(varPrefix, 0, underlyingType, varContext); + boundVars.push_back(payloadVar); + + auto namedPattern = new (C) NamedPattern(payloadVar); + namedPattern->setImplicit(); + auto letPattern = new (C) VarPattern(SourceLoc(), /*isLet*/ true, + namedPattern); + auto pat = new (C) ParenPattern(SourceLoc(), letPattern, SourceLoc()); + pat->setImplicit(); + return pat; +} + + +/// Creates a named variable based on a prefix character and a numeric index. +/// \p prefixChar The prefix character for the variable's name. +/// \p index The numeric index to append to the variable's name. +/// \p type The type of the variable. +/// \p varContext The context of the variable. +/// \return A VarDecl named with the prefix and number. +VarDecl *swift::indexedVarDecl(char prefixChar, int index, Type type, + DeclContext *varContext) { + ASTContext &C = varContext->getASTContext(); + + llvm::SmallString<8> indexVal; + indexVal.append(1, prefixChar); + APInt(32, index).toString(indexVal, 10, /*signed*/ false); + auto indexStr = C.AllocateCopy(indexVal); + auto indexStrRef = StringRef(indexStr.data(), indexStr.size()); + + auto varDecl = new (C) VarDecl(/*IsStatic*/false, VarDecl::Introducer::Let, + /*IsCaptureList*/true, SourceLoc(), + C.getIdentifier(indexStrRef), + varContext); + varDecl->setType(type); + varDecl->setHasNonPatternBindingInit(true); + return varDecl; +} diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index ceba904643c81..8df6a2322aae7 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -228,12 +228,46 @@ class DerivedConformance { bool checkAndDiagnoseDisallowedContext(ValueDecl *synthesizing) const; }; +/// Create AST statements which convert from an enum to an Int with a switch. +/// \p stmts The generated statements are appended to this vector. +/// \p parentDC Either an extension or the enum itself. +/// \p enumDecl The enum declaration. +/// \p enumVarDecl The enum input variable. +/// \p funcDecl The parent function. +/// \p indexName The name of the output variable. +/// \return A DeclRefExpr of the output variable (of type Int). DeclRefExpr *convertEnumToIndex( SmallVectorImpl &stmts, DeclContext *parentDC, EnumDecl *enumDecl, VarDecl *enumVarDecl, AbstractFunctionDecl *funcDecl, const char *indexName); + +/// Returns the ParamDecl for each associated value of the given enum whose type +/// does not conform to a protocol +/// \p theEnum The enum whose elements and associated values should be checked. +/// \p protocol The protocol being requested. +/// \return The ParamDecl of each associated value whose type does not conform. +SmallVector +associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, + ProtocolDecl *protocol); + +/// Returns true if, for every element of the given enum, it either has no +/// associated values or all of them conform to a protocol. +/// \p theEnum The enum whose elements and associated values should be checked. +/// \p protocol The protocol being requested. +/// \return True if all associated values of all elements of the enum conform. +bool allAssociatedValuesConformToProtocol(DeclContext *DC, + EnumDecl *theEnum, + ProtocolDecl *protocol); + +Pattern* +enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, + char varPrefix, DeclContext *varContext, + SmallVectorImpl &boundVars); + +VarDecl *indexedVarDecl(char prefixChar, int index, Type type, + DeclContext *varContext); } #endif From e13beb122789e4f7277e679b2a81d8f144460828 Mon Sep 17 00:00:00 2001 From: taylorswift Date: Wed, 18 Sep 2019 21:32:14 -0500 Subject: [PATCH 007/237] update tests --- test/Sema/enum_conformance_synthesis.swift | 8 ++++---- test/stdlib/EnumComparableTests.swift | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index 952855c7609ed..dd3668994e98b 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -337,13 +337,13 @@ func pit(_ a: Angel, against b: Angel) -> Bool { return a < b } -// enums with payloads don’t get synthesized Comparable -enum Notice: Comparable { // expected-error{{type 'Notice' does not conform to protocol 'Comparable'}} - case taylor(Int), taylornation(Int) +// enums with non-conforming payloads don’t get synthesized Comparable +enum Notice: Comparable { // expected-error{{type 'Notice' does not conform to protocol 'Comparable'}} expected-error{{type 'Notice' does not conform to protocol 'Equatable'}} expected-note {{do you want to add protocol stubs?}} expected-note {{do you want to add protocol stubs?}} + case taylor((Int, Int)), taylornation(Int) // expected-note{{associated value type '(Int, Int)' does not conform to protocol 'Equatable', preventing synthesized conformance of 'Notice' to 'Equatable'}} } // neither do enums with raw values -enum Track: Int, Comparable { // expected-error{{type 'Track' does not conform to protocol 'Comparable'}} +enum Track: Int, Comparable { // expected-error{{type 'Track' does not conform to protocol 'Comparable'}} expected-note {{do you want to add protocol stubs?}} case four = 4 case five = 5 case six = 6 diff --git a/test/stdlib/EnumComparableTests.swift b/test/stdlib/EnumComparableTests.swift index 767a897fd38eb..0851cc0f23afa 100644 --- a/test/stdlib/EnumComparableTests.swift +++ b/test/stdlib/EnumComparableTests.swift @@ -28,4 +28,17 @@ SynthesizedComparableTests.test("Simple Enum sorting with duplicates") { expectEqual(unsorted.sorted(), [.debut, .be, .fearless, .sn, .sn, .red, .roses, .reputation, .lover, .lover]) } +SynthesizedComparableTests.test("Associated Values Enum sorting") { + enum Bar:Comparable + { + case a(Int, Int) + case b(Int) + case c + } + + let unsorted:[Bar] = [.b(89), .a(12, 4), .c, .a(5, 4), .b(9), .a(5, 1)] + + expectEqual(unsorted.sorted(), [.a(5, 1), .a(5, 4), .a(12, 4), .b(9), .b(89), .c]) +} + runAllTests() From 37edea60bedda0559aa400003d6a745e3bddaba2 Mon Sep 17 00:00:00 2001 From: taylorswift Date: Thu, 19 Sep 2019 19:36:35 -0500 Subject: [PATCH 008/237] fix setGenericSignature --- lib/Sema/DerivedConformanceComparable.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index 424adb8e25e0a..e574afffb8ee1 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -371,8 +371,7 @@ deriveComparable_lt( comparableDecl->setBodySynthesizer(bodySynthesizer); // Compute the interface type. - if (auto genericEnv = parentDC->getGenericEnvironmentOfContext()) - comparableDecl->setGenericEnvironment(genericEnv); + comparableDecl->setGenericSignature(parentDC->getGenericSignatureOfContext()); comparableDecl->computeType(); comparableDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); From 25e4cfc1a2ff8e4e22d135184803cd815164e7d8 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Thu, 21 Nov 2019 18:47:50 -0600 Subject: [PATCH 009/237] fix build failures --- include/swift/AST/ASTContext.h | 3 --- lib/Sema/DerivedConformanceComparable.cpp | 25 ++++++++-------------- test/Sema/enum_conformance_synthesis.swift | 4 ++-- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index ebc1355d8d54d..5a0c5dfd08b47 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -415,9 +415,6 @@ class ASTContext final { void setStatsReporter(UnifiedStatsReporter *stats); private: - /// Set the lazy resolver for this context. - void setLazyResolver(LazyResolver *resolver); - // get `<` or `==` FuncDecl *getBinaryComparisonOperatorIntDecl(StringRef op, FuncDecl **cached) const; diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index e574afffb8ee1..b5b16bbaeb18d 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -306,15 +306,16 @@ deriveComparable_lt( DerivedConformance &derived, std::pair (*bodySynthesizer)(AbstractFunctionDecl *, void *)) { - ASTContext &C = derived.TC.Context; + ASTContext &C = derived.Context; auto parentDC = derived.getConformanceContext(); auto selfIfaceTy = parentDC->getDeclaredInterfaceType(); auto getParamDecl = [&](StringRef s) -> ParamDecl * { - auto *param = new (C) ParamDecl(ParamDecl::Specifier::Default, SourceLoc(), + auto *param = new (C) ParamDecl(SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), C.getIdentifier(s), parentDC); + param->setSpecifier(ParamSpecifier::Default); param->setInterfaceType(selfIfaceTy); return param; }; @@ -356,28 +357,20 @@ deriveComparable_lt( auto comparableDeclName = DeclName(C, DeclBaseName(C.Id_LessThanOperator), argumentLabels); comparableDecl->getAttrs().add(new (C) ImplementsAttr(SourceLoc(), - SourceRange(), - comparableTypeLoc, - comparableDeclName, - DeclNameLoc())); + SourceRange(), + comparableTypeLoc, + comparableDeclName, + DeclNameLoc())); } if (!C.getLessThanIntDecl()) { - derived.TC.diagnose(derived.ConformanceDecl->getLoc(), - diag::no_less_than_overload_for_int); + derived.ConformanceDecl->diagnose(diag::no_less_than_overload_for_int); return nullptr; } comparableDecl->setBodySynthesizer(bodySynthesizer); - // Compute the interface type. - comparableDecl->setGenericSignature(parentDC->getGenericSignatureOfContext()); - comparableDecl->computeType(); - comparableDecl->copyFormalAccessFrom(derived.Nominal, /*sourceIsParentContext*/ true); - comparableDecl->setValidationToChecked(); - - C.addSynthesizedDecl(comparableDecl); // Add the operator to the parent scope. derived.addMembersToConformanceContext({comparableDecl}); @@ -413,6 +406,6 @@ ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { llvm_unreachable("todo"); } } - TC.diagnose(requirement->getLoc(), diag::broken_comparable_requirement); + requirement->diagnose(diag::broken_comparable_requirement); return nullptr; } diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index ebef5e730add7..32001c94ed33c 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -337,12 +337,12 @@ func pit(_ a: Angel, against b: Angel) -> Bool { } // enums with non-conforming payloads don’t get synthesized Comparable -enum Notice: Comparable { // expected-error{{type 'Notice' does not conform to protocol 'Comparable'}} expected-error{{type 'Notice' does not conform to protocol 'Equatable'}} expected-note {{do you want to add protocol stubs?}} expected-note {{do you want to add protocol stubs?}} +enum Notice: Comparable { // expected-error{{type 'Notice' does not conform to protocol 'Comparable'}} expected-error{{type 'Notice' does not conform to protocol 'Equatable'}} case taylor((Int, Int)), taylornation(Int) // expected-note{{associated value type '(Int, Int)' does not conform to protocol 'Equatable', preventing synthesized conformance of 'Notice' to 'Equatable'}} } // neither do enums with raw values -enum Track: Int, Comparable { // expected-error{{type 'Track' does not conform to protocol 'Comparable'}} expected-note {{do you want to add protocol stubs?}} +enum Track: Int, Comparable { // expected-error{{type 'Track' does not conform to protocol 'Comparable'}} case four = 4 case five = 5 case six = 6 From eb79a08a42e046b1333ef8134a076909b1e1ef55 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Thu, 2 Jan 2020 22:31:21 -0600 Subject: [PATCH 010/237] (wip) update comparable implementations to match new internal apis --- lib/Sema/DerivedConformanceComparable.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index b5b16bbaeb18d..aad96233dbe7a 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -46,7 +46,7 @@ static GuardStmt *returnComparisonIfNotEqualGuard(ASTContext &C, // First, generate the statement for the body of the guard. // return lhs < rhs auto ltFuncExpr = new (C) UnresolvedDeclRefExpr( - DeclName(C.getIdentifier("<")), DeclRefKind::BinaryOperator, + DeclNameRef(C.Id_LessThanOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); auto ltArgsTuple = TupleExpr::create(C, SourceLoc(), { lhsExpr, rhsExpr }, @@ -60,7 +60,7 @@ static GuardStmt *returnComparisonIfNotEqualGuard(ASTContext &C, // Next, generate the condition being checked. // lhs == rhs auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( - DeclName(C.getIdentifier("==")), DeclRefKind::BinaryOperator, + DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, DeclNameLoc()); auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), { lhsExpr, rhsExpr }, @@ -202,8 +202,8 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v auto lhsSubpattern = enumElementPayloadSubpattern(elt, 'l', ltDecl, lhsPayloadVars); auto lhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, lhsSubpattern); lhsElemPat->setImplicit(); @@ -212,8 +212,8 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v auto rhsSubpattern = enumElementPayloadSubpattern(elt, 'r', ltDecl, rhsPayloadVars); auto rhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), - SourceLoc(), SourceLoc(), - Identifier(), elt, + SourceLoc(), DeclNameLoc(), + DeclNameRef(), elt, rhsSubpattern); rhsElemPat->setImplicit(); From 1f0cbac410808f0b676548ac09ea328b79d661e7 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Thu, 2 Jan 2020 23:02:26 -0600 Subject: [PATCH 011/237] remove expected-note --- test/Sema/enum_conformance_synthesis.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index 304dc16b31e0f..29a2d8305197f 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -355,7 +355,6 @@ enum Publicist { func miss(_ a: Publicist, outsold b: Publicist) -> Bool { return b < a // expected-error{{binary operator '<' cannot be applied to two 'Publicist' operands}} - // expected-note @-1 {{overloads for '<' exist with these partially matching parameter lists: }} } // FIXME: Remove -verify-ignore-unknown. From b56c77d161914a07b735dbbab1e304ca58015691 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 21 Jan 2020 16:02:27 -0800 Subject: [PATCH 012/237] [Preset] Update the macOS pull request preset to not build and test tvOS platform to get faster trun around --- utils/build-presets.ini | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 8342321cac4c1..5df82d1a05dc6 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1459,13 +1459,17 @@ playgroundsupport dash-dash +# Skip build and test for tvOS +skip-build-tvos +skip-test-tvos +skip-test-tvos-host + # Run the SIL verifier after each transform when building swift files sil-verify-all # 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 From 270101243496d1eeb21a6955d110d6683496f256 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Wed, 22 Jan 2020 21:56:47 -0600 Subject: [PATCH 013/237] fix some reviewed issues --- include/swift/AST/ASTContext.h | 3 - lib/AST/ASTContext.cpp | 23 ++-- lib/Sema/DerivedConformanceComparable.cpp | 89 +++++---------- .../DerivedConformanceEquatableHashable.cpp | 61 +++-------- lib/Sema/DerivedConformances.cpp | 81 +++++++++++++- lib/Sema/DerivedConformances.h | 101 +++++++++++------- 6 files changed, 186 insertions(+), 172 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 546ef565e72ed..607cbca4df3a6 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -437,9 +437,6 @@ class ASTContext final { void setStatsReporter(UnifiedStatsReporter *stats); private: - // get `<` or `==` - FuncDecl *getBinaryComparisonOperatorIntDecl(StringRef op, FuncDecl **cached) const; - friend class TypeChecker; void installGlobalTypeChecker(TypeChecker *TC); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index c00e820b7867c..ee494634f1023 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1086,35 +1086,36 @@ ASTContext::getBuiltinInitDecl(NominalTypeDecl *decl, return witness; } -FuncDecl *ASTContext::getBinaryComparisonOperatorIntDecl(StringRef op, FuncDecl **cached) const { - if (*cached) - return *cached; +static +FuncDecl *getBinaryComparisonOperatorIntDecl(const ASTContext &C, StringRef op, FuncDecl *&cached) { + if (cached) + return cached; - if (!getIntDecl() || !getBoolDecl()) + if (!C.getIntDecl() || !C.getBoolDecl()) return nullptr; - auto intType = getIntDecl()->getDeclaredType(); + auto intType = C.getIntDecl()->getDeclaredType(); auto isIntParam = [&](AnyFunctionType::Param param) { return (!param.isVariadic() && !param.isInOut() && param.getPlainType()->isEqual(intType)); }; - auto boolType = getBoolDecl()->getDeclaredType(); - auto decl = lookupOperatorFunc(*this, op, - intType, [=](FunctionType *type) { + auto boolType = C.getBoolDecl()->getDeclaredType(); + auto decl = lookupOperatorFunc(C, op, intType, + [=](FunctionType *type) { // Check for the signature: (Int, Int) -> Bool if (type->getParams().size() != 2) return false; if (!isIntParam(type->getParams()[0]) || !isIntParam(type->getParams()[1])) return false; return type->getResult()->isEqual(boolType); }); - *cached = decl; + cached = decl; return decl; } FuncDecl *ASTContext::getLessThanIntDecl() const { - return getBinaryComparisonOperatorIntDecl("<", &getImpl().LessThanIntDecl); + return getBinaryComparisonOperatorIntDecl(*this, "<", getImpl().LessThanIntDecl); } FuncDecl *ASTContext::getEqualIntDecl() const { - return getBinaryComparisonOperatorIntDecl("==", &getImpl().EqualIntDecl); + return getBinaryComparisonOperatorIntDecl(*this, "==", getImpl().EqualIntDecl); } FuncDecl *ASTContext::getHashValueForDecl() const { diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index aad96233dbe7a..a3455c9617a96 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -31,52 +31,6 @@ using namespace swift; -/// Returns a generated guard statement that checks whether the given lhs and -/// rhs expressions are equal. If not equal, the else block for the guard -/// returns lhs < rhs. -/// \p C The AST context. -/// \p lhsExpr The first expression to compare for equality. -/// \p rhsExpr The second expression to compare for equality. -static GuardStmt *returnComparisonIfNotEqualGuard(ASTContext &C, - Expr *lhsExpr, - Expr *rhsExpr) { - SmallVector conditions; - SmallVector statements; - - // First, generate the statement for the body of the guard. - // return lhs < rhs - auto ltFuncExpr = new (C) UnresolvedDeclRefExpr( - DeclNameRef(C.Id_LessThanOperator), DeclRefKind::BinaryOperator, - DeclNameLoc()); - auto ltArgsTuple = TupleExpr::create(C, SourceLoc(), - { lhsExpr, rhsExpr }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/false, - /*Implicit*/true); - auto ltExpr = new (C) BinaryExpr(ltFuncExpr, ltArgsTuple, /*Implicit*/true); - auto returnStmt = new (C) ReturnStmt(SourceLoc(), ltExpr); - statements.emplace_back(ASTNode(returnStmt)); - - // Next, generate the condition being checked. - // lhs == rhs - auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( - DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, - DeclNameLoc()); - auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), - { lhsExpr, rhsExpr }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/false, - /*Implicit*/true); - auto cmpExpr = new (C) BinaryExpr(cmpFuncExpr, cmpArgsTuple, - /*Implicit*/true); - conditions.emplace_back(cmpExpr); - - // Build and return the complete guard statement. - // guard lhs == rhs else { return lhs < rhs } - auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); - return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body); -} - // how does this code ever even get invoked? you can’t compare uninhabited enums... static std::pair deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { @@ -129,9 +83,9 @@ deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, // Generate the conversion from the enums to integer indices. SmallVector statements; - DeclRefExpr *aIndex = convertEnumToIndex(statements, parentDC, enumDecl, + DeclRefExpr *aIndex = DerivedConformance::convertEnumToIndex(statements, parentDC, enumDecl, aParam, ltDecl, "index_a"); - DeclRefExpr *bIndex = convertEnumToIndex(statements, parentDC, enumDecl, + DeclRefExpr *bIndex = DerivedConformance::convertEnumToIndex(statements, parentDC, enumDecl, bParam, ltDecl, "index_b"); // Generate the compare of the indices. @@ -199,7 +153,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v // .(let l0, let l1, ...) SmallVector lhsPayloadVars; - auto lhsSubpattern = enumElementPayloadSubpattern(elt, 'l', ltDecl, + auto lhsSubpattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'l', ltDecl, lhsPayloadVars); auto lhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), DeclNameLoc(), @@ -209,7 +163,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v // .(let r0, let r1, ...) SmallVector rhsPayloadVars; - auto rhsSubpattern = enumElementPayloadSubpattern(elt, 'r', ltDecl, + auto rhsSubpattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'r', ltDecl, rhsPayloadVars); auto rhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), DeclNameLoc(), @@ -254,7 +208,8 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v auto rhsVar = rhsPayloadVars[varIdx]; auto rhsExpr = new (C) DeclRefExpr(rhsVar, DeclNameLoc(), /*Implicit*/true); - auto guardStmt = returnComparisonIfNotEqualGuard(C, lhsExpr, rhsExpr); + auto guardStmt = DerivedConformance::returnComparisonIfNotEqualGuard(C, + lhsExpr, rhsExpr); statementsInCase.emplace_back(guardStmt); } @@ -380,14 +335,17 @@ deriveComparable_lt( bool DerivedConformance::canDeriveComparable(DeclContext *context, NominalTypeDecl *declaration) { + auto enumeration = dyn_cast(declaration); // The type must be an enum. - if (EnumDecl *const enumeration = dyn_cast(declaration)) { - // The cases must not have non-comparable associated values or raw backing - auto comparable = context->getASTContext().getProtocol(KnownProtocolKind::Comparable); - return allAssociatedValuesConformToProtocol(context, enumeration, comparable) && !enumeration->hasRawType(); - } else { - return false; + if (!enumeration) { + return false; } + auto comparable = context->getASTContext().getProtocol(KnownProtocolKind::Comparable); + if (!comparable) { + return false; // not sure what should be done here instead + } + // The cases must not have non-comparable associated values or raw backing + return allAssociatedValuesConformToProtocol(context, enumeration, comparable) && !enumeration->hasRawType(); } ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { @@ -396,12 +354,17 @@ ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { // Build the necessary decl. if (requirement->getBaseName() == "<") { if (EnumDecl const *const enumeration = dyn_cast(this->Nominal)) { - auto bodySynthesizer = !enumeration->hasCases() - ? &deriveBodyComparable_enum_uninhabited_lt - : enumeration->hasOnlyCasesWithoutAssociatedValues() - ? &deriveBodyComparable_enum_noAssociatedValues_lt - : &deriveBodyComparable_enum_hasAssociatedValues_lt; - return deriveComparable_lt(*this, bodySynthesizer); + std::pair (*synthesizer)(AbstractFunctionDecl *, void *); + if (enumeration->hasCases()) { + if (enumeration->hasOnlyCasesWithoutAssociatedValues()) { + synthesizer = &deriveBodyComparable_enum_noAssociatedValues_lt; + } else { + synthesizer = &deriveBodyComparable_enum_hasAssociatedValues_lt; + } + } else { + synthesizer = &deriveBodyComparable_enum_uninhabited_lt; + } + return deriveComparable_lt(*this, synthesizer); } else { llvm_unreachable("todo"); } diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index d41c0e98a7f14..776d2b534e80f 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -82,7 +82,7 @@ static bool canDeriveConformance(DeclContext *DC, if (auto enumDecl = dyn_cast(target)) { // The cases must not have associated values, or all associated values must // conform to the protocol. - return allAssociatedValuesConformToProtocol(DC, enumDecl, protocol); + return DerivedConformance::allAssociatedValuesConformToProtocol(DC, enumDecl, protocol); } if (auto structDecl = dyn_cast(target)) { @@ -101,7 +101,7 @@ void diagnoseFailedDerivation(DeclContext *DC, NominalTypeDecl *nominal, if (auto *enumDecl = dyn_cast(nominal)) { auto nonconformingAssociatedTypes = - associatedValuesNotConformingToProtocol(DC, enumDecl, protocol); + DerivedConformance::associatedValuesNotConformingToProtocol(DC, enumDecl, protocol); for (auto *typeToDiagnose : nonconformingAssociatedTypes) { SourceLoc reprLoc; if (auto *repr = typeToDiagnose->getTypeRepr()) @@ -135,45 +135,6 @@ void diagnoseFailedDerivation(DeclContext *DC, NominalTypeDecl *nominal, } } -/// Returns a generated guard statement that checks whether the given lhs and -/// rhs expressions are equal. If not equal, the else block for the guard -/// returns false. -/// \p C The AST context. -/// \p lhsExpr The first expression to compare for equality. -/// \p rhsExpr The second expression to compare for equality. -static GuardStmt *returnIfNotEqualGuard(ASTContext &C, - Expr *lhsExpr, - Expr *rhsExpr) { - SmallVector conditions; - SmallVector statements; - - // First, generate the statement for the body of the guard. - // return false - auto falseExpr = new (C) BooleanLiteralExpr(false, SourceLoc(), - /*Implicit*/true); - auto returnStmt = new (C) ReturnStmt(SourceLoc(), falseExpr); - statements.emplace_back(ASTNode(returnStmt)); - - // Next, generate the condition being checked. - // lhs == rhs - auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( - DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, - DeclNameLoc()); - auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), - { lhsExpr, rhsExpr }, - { }, { }, SourceLoc(), - /*HasTrailingClosure*/false, - /*Implicit*/true); - auto cmpExpr = new (C) BinaryExpr(cmpFuncExpr, cmpArgsTuple, - /*Implicit*/true); - conditions.emplace_back(cmpExpr); - - // Build and return the complete guard statement. - // guard lhs == rhs else { return false } - auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); - return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body); -} - static std::pair deriveBodyEquatable_enum_uninhabited_eq(AbstractFunctionDecl *eqDecl, void *) { auto parentDC = eqDecl->getDeclContext(); @@ -225,9 +186,9 @@ deriveBodyEquatable_enum_noAssociatedValues_eq(AbstractFunctionDecl *eqDecl, // Generate the conversion from the enums to integer indices. SmallVector statements; - DeclRefExpr *aIndex = convertEnumToIndex(statements, parentDC, enumDecl, + DeclRefExpr *aIndex = DerivedConformance::convertEnumToIndex(statements, parentDC, enumDecl, aParam, eqDecl, "index_a"); - DeclRefExpr *bIndex = convertEnumToIndex(statements, parentDC, enumDecl, + DeclRefExpr *bIndex = DerivedConformance::convertEnumToIndex(statements, parentDC, enumDecl, bParam, eqDecl, "index_b"); // Generate the compare of the indices. @@ -296,7 +257,7 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl, // .(let l0, let l1, ...) SmallVector lhsPayloadVars; - auto lhsSubpattern = enumElementPayloadSubpattern(elt, 'l', eqDecl, + auto lhsSubpattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'l', eqDecl, lhsPayloadVars); auto lhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), DeclNameLoc(), @@ -306,7 +267,7 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl, // .(let r0, let r1, ...) SmallVector rhsPayloadVars; - auto rhsSubpattern = enumElementPayloadSubpattern(elt, 'r', eqDecl, + auto rhsSubpattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'r', eqDecl, rhsPayloadVars); auto rhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), DeclNameLoc(), @@ -352,7 +313,8 @@ deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl, auto rhsVar = rhsPayloadVars[varIdx]; auto rhsExpr = new (C) DeclRefExpr(rhsVar, DeclNameLoc(), /*Implicit*/true); - auto guardStmt = returnIfNotEqualGuard(C, lhsExpr, rhsExpr); + auto guardStmt = DerivedConformance::returnFalseIfNotEqualGuard(C, + lhsExpr, rhsExpr); statementsInCase.emplace_back(guardStmt); } @@ -438,7 +400,8 @@ deriveBodyEquatable_struct_eq(AbstractFunctionDecl *eqDecl, void *) { auto bPropertyExpr = new (C) DotSyntaxCallExpr(bPropertyRef, SourceLoc(), bParamRef); - auto guardStmt = returnIfNotEqualGuard(C, aPropertyExpr, bPropertyExpr); + auto guardStmt = DerivedConformance::returnFalseIfNotEqualGuard(C, + aPropertyExpr, bPropertyExpr); statements.emplace_back(guardStmt); } @@ -763,7 +726,7 @@ deriveBodyHashable_enum_noAssociatedValues_hashInto( // generate: switch self {...} SmallVector stmts; - auto discriminatorExpr = convertEnumToIndex(stmts, parentDC, enumDecl, + auto discriminatorExpr = DerivedConformance::convertEnumToIndex(stmts, parentDC, enumDecl, selfDecl, hashIntoDecl, "discriminator"); // generate: hasher.combine(discriminator) @@ -818,7 +781,7 @@ deriveBodyHashable_enum_hasAssociatedValues_hashInto( SmallVector payloadVars; SmallVector statements; - auto payloadPattern = enumElementPayloadSubpattern(elt, 'a', hashIntoDecl, + auto payloadPattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'a', hashIntoDecl, payloadVars); auto pat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), SourceLoc(), DeclNameLoc(), diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index be025b578ae01..ccd77fc55df02 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -390,6 +390,77 @@ bool DerivedConformance::checkAndDiagnoseDisallowedContext( return false; } +/// Returns a generated guard statement that checks whether the given lhs and +/// rhs expressions are equal. If not equal, the else block for the guard +/// returns `guardReturnValue`. +/// \p C The AST context. +/// \p lhsExpr The first expression to compare for equality. +/// \p rhsExpr The second expression to compare for equality. +/// \p guardReturnValue The expression to return if the two sides are not equal +GuardStmt *DerivedConformance::returnIfNotEqualGuard(ASTContext &C, + Expr *lhsExpr, + Expr *rhsExpr, + Expr *guardReturnValue) { + SmallVector conditions; + SmallVector statements; + + auto returnStmt = new (C) ReturnStmt(SourceLoc(), guardReturnValue); + statements.emplace_back(ASTNode(returnStmt)); + + // Next, generate the condition being checked. + // lhs == rhs + auto cmpFuncExpr = new (C) UnresolvedDeclRefExpr( + DeclNameRef(C.Id_EqualsOperator), DeclRefKind::BinaryOperator, + DeclNameLoc()); + auto cmpArgsTuple = TupleExpr::create(C, SourceLoc(), + { lhsExpr, rhsExpr }, + { }, { }, SourceLoc(), + /*HasTrailingClosure*/false, + /*Implicit*/true); + auto cmpExpr = new (C) BinaryExpr(cmpFuncExpr, cmpArgsTuple, + /*Implicit*/true); + conditions.emplace_back(cmpExpr); + + // Build and return the complete guard statement. + // guard lhs == rhs else { return lhs < rhs } + auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); + return new (C) GuardStmt(SourceLoc(), C.AllocateCopy(conditions), body); +} +/// Returns a generated guard statement that checks whether the given lhs and +/// rhs expressions are equal. If not equal, the else block for the guard +/// returns `false`. +/// \p C The AST context. +/// \p lhsExpr The first expression to compare for equality. +/// \p rhsExpr The second expression to compare for equality. +GuardStmt *DerivedConformance::returnFalseIfNotEqualGuard(ASTContext &C, + Expr *lhsExpr, + Expr *rhsExpr) { + // return false + auto falseExpr = new (C) BooleanLiteralExpr(false, SourceLoc(), true); + return returnIfNotEqualGuard(C, lhsExpr, rhsExpr, falseExpr); +} +/// Returns a generated guard statement that checks whether the given lhs and +/// rhs expressions are equal. If not equal, the else block for the guard +/// returns lhs < rhs. +/// \p C The AST context. +/// \p lhsExpr The first expression to compare for equality. +/// \p rhsExpr The second expression to compare for equality. +GuardStmt *DerivedConformance::returnComparisonIfNotEqualGuard(ASTContext &C, + Expr *lhsExpr, + Expr *rhsExpr) { + // return lhs < rhs + auto ltFuncExpr = new (C) UnresolvedDeclRefExpr( + DeclNameRef(C.Id_LessThanOperator), DeclRefKind::BinaryOperator, + DeclNameLoc()); + auto ltArgsTuple = TupleExpr::create(C, SourceLoc(), + { lhsExpr, rhsExpr }, + { }, { }, SourceLoc(), + /*HasTrailingClosure*/false, + /*Implicit*/true); + auto ltExpr = new (C) BinaryExpr(ltFuncExpr, ltArgsTuple, /*Implicit*/true); + return returnIfNotEqualGuard(C, lhsExpr, rhsExpr, ltExpr); +} + /// Build a type-checked integer literal. static IntegerLiteralExpr *buildIntegerLiteral(ASTContext &C, unsigned index) { Type intType = C.getIntDecl()->getDeclaredType(); @@ -409,7 +480,7 @@ static IntegerLiteralExpr *buildIntegerLiteral(ASTContext &C, unsigned index) { /// \p funcDecl The parent function. /// \p indexName The name of the output variable. /// \return A DeclRefExpr of the output variable (of type Int). -DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, +DeclRefExpr *DerivedConformance::convertEnumToIndex(SmallVectorImpl &stmts, DeclContext *parentDC, EnumDecl *enumDecl, VarDecl *enumVarDecl, @@ -484,7 +555,7 @@ DeclRefExpr *swift::convertEnumToIndex(SmallVectorImpl &stmts, /// \p protocol The protocol being requested. /// \return The ParamDecl of each associated value whose type does not conform. SmallVector -swift::associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, +DerivedConformance::associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol) { SmallVector nonconformingAssociatedValues; for (auto elt : theEnum->getAllElements()) { @@ -509,7 +580,7 @@ swift::associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnu /// \p theEnum The enum whose elements and associated values should be checked. /// \p protocol The protocol being requested. /// \return True if all associated values of all elements of the enum conform. -bool swift::allAssociatedValuesConformToProtocol(DeclContext *DC, +bool DerivedConformance::allAssociatedValuesConformToProtocol(DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol) { return associatedValuesNotConformingToProtocol(DC, theEnum, protocol).empty(); @@ -522,7 +593,7 @@ bool swift::allAssociatedValuesConformToProtocol(DeclContext *DC, /// \p varContext The context into which payload variables should be declared. /// \p boundVars The array to which the pattern's variables will be appended. Pattern* -swift::enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, +DerivedConformance::enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, char varPrefix, DeclContext *varContext, SmallVectorImpl &boundVars) { auto parentDC = enumElementDecl->getDeclContext(); @@ -583,7 +654,7 @@ swift::enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, /// \p type The type of the variable. /// \p varContext The context of the variable. /// \return A VarDecl named with the prefix and number. -VarDecl *swift::indexedVarDecl(char prefixChar, int index, Type type, +VarDecl *DerivedConformance::indexedVarDecl(char prefixChar, int index, Type type, DeclContext *varContext) { ASTContext &C = varContext->getASTContext(); diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index e09e676fdc617..a70b857b3a0d6 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -232,48 +232,67 @@ class DerivedConformance { /// /// \param synthesizing The decl that is being synthesized. bool checkAndDiagnoseDisallowedContext(ValueDecl *synthesizing) const; -}; - -/// Create AST statements which convert from an enum to an Int with a switch. -/// \p stmts The generated statements are appended to this vector. -/// \p parentDC Either an extension or the enum itself. -/// \p enumDecl The enum declaration. -/// \p enumVarDecl The enum input variable. -/// \p funcDecl The parent function. -/// \p indexName The name of the output variable. -/// \return A DeclRefExpr of the output variable (of type Int). -DeclRefExpr *convertEnumToIndex( SmallVectorImpl &stmts, - DeclContext *parentDC, - EnumDecl *enumDecl, - VarDecl *enumVarDecl, - AbstractFunctionDecl *funcDecl, - const char *indexName); -/// Returns the ParamDecl for each associated value of the given enum whose type -/// does not conform to a protocol -/// \p theEnum The enum whose elements and associated values should be checked. -/// \p protocol The protocol being requested. -/// \return The ParamDecl of each associated value whose type does not conform. -SmallVector -associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, - ProtocolDecl *protocol); - -/// Returns true if, for every element of the given enum, it either has no -/// associated values or all of them conform to a protocol. -/// \p theEnum The enum whose elements and associated values should be checked. -/// \p protocol The protocol being requested. -/// \return True if all associated values of all elements of the enum conform. -bool allAssociatedValuesConformToProtocol(DeclContext *DC, - EnumDecl *theEnum, - ProtocolDecl *protocol); - -Pattern* -enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, - char varPrefix, DeclContext *varContext, - SmallVectorImpl &boundVars); - -VarDecl *indexedVarDecl(char prefixChar, int index, Type type, - DeclContext *varContext); + /// Returns a generated guard statement that checks whether the given lhs and + /// rhs expressions are equal. If not equal, the else block for the guard + /// returns `guardReturnValue`. + /// \p C The AST context. + /// \p lhsExpr The first expression to compare for equality. + /// \p rhsExpr The second expression to compare for equality. + /// \p guardReturnValue The expression to return if the two sides are not equal + static + GuardStmt *returnIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr, + Expr *guardReturnValue); + // return false + static + GuardStmt *returnFalseIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr); + // return lhs < rhs + static + GuardStmt *returnComparisonIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr); + + /// Returns the ParamDecl for each associated value of the given enum whose type + /// does not conform to a protocol + /// \p theEnum The enum whose elements and associated values should be checked. + /// \p protocol The protocol being requested. + /// \return The ParamDecl of each associated value whose type does not conform. + static SmallVector + associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, + ProtocolDecl *protocol); + + /// Returns true if, for every element of the given enum, it either has no + /// associated values or all of them conform to a protocol. + /// \p theEnum The enum whose elements and associated values should be checked. + /// \p protocol The protocol being requested. + /// \return True if all associated values of all elements of the enum conform. + static bool + allAssociatedValuesConformToProtocol(DeclContext *DC, + EnumDecl *theEnum, + ProtocolDecl *protocol); + /// Create AST statements which convert from an enum to an Int with a switch. + /// \p stmts The generated statements are appended to this vector. + /// \p parentDC Either an extension or the enum itself. + /// \p enumDecl The enum declaration. + /// \p enumVarDecl The enum input variable. + /// \p funcDecl The parent function. + /// \p indexName The name of the output variable. + /// \return A DeclRefExpr of the output variable (of type Int). + static DeclRefExpr * + convertEnumToIndex( SmallVectorImpl &stmts, + DeclContext *parentDC, + EnumDecl *enumDecl, + VarDecl *enumVarDecl, + AbstractFunctionDecl *funcDecl, + const char *indexName); + + static Pattern* + enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, + char varPrefix, DeclContext *varContext, + SmallVectorImpl &boundVars); + + static VarDecl * + indexedVarDecl(char prefixChar, int index, Type type, + DeclContext *varContext); +}; } #endif From 1715ebcf9dbd7fbc290aa1646394e49a231a33f3 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Thu, 23 Jan 2020 22:46:30 -0600 Subject: [PATCH 014/237] address more reviewed issues --- include/swift/AST/ASTContext.h | 2 +- lib/AST/ASTContext.cpp | 2 +- lib/Sema/DerivedConformanceComparable.cpp | 66 +++++++------------ .../DerivedConformanceEquatableHashable.cpp | 2 +- lib/Sema/DerivedConformances.cpp | 6 +- lib/Sema/DerivedConformances.h | 4 +- 6 files changed, 33 insertions(+), 49 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 607cbca4df3a6..b5281ddd1f97c 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.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 diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ee494634f1023..83dda630d2503 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 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 diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index a3455c9617a96..cf5a5c313904d 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.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 @@ -31,7 +31,6 @@ using namespace swift; -// how does this code ever even get invoked? you can’t compare uninhabited enums... static std::pair deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { auto parentDC = ltDecl->getDeclContext(); @@ -42,26 +41,9 @@ deriveBodyComparable_enum_uninhabited_lt(AbstractFunctionDecl *ltDecl, void *) { auto bParam = args->get(1); assert(!cast(aParam->getType()->getAnyNominal())->hasCases()); + assert(!cast(bParam->getType()->getAnyNominal())->hasCases()); - SmallVector statements; - SmallVector cases; - - // switch (a, b) { } - auto aRef = new (C) DeclRefExpr(aParam, DeclNameLoc(), /*implicit*/ true, - AccessSemantics::Ordinary, - aParam->getType()); - auto bRef = new (C) DeclRefExpr(bParam, DeclNameLoc(), /*implicit*/ true, - AccessSemantics::Ordinary, - bParam->getType()); - TupleTypeElt abTupleElts[2] = { aParam->getType(), bParam->getType() }; - auto abExpr = TupleExpr::create(C, SourceLoc(), {aRef, bRef}, {}, {}, - SourceLoc(), /*HasTrailingClosure*/ false, - /*implicit*/ true, - TupleType::get(abTupleElts, C)); - auto switchStmt = SwitchStmt::create(LabeledStmtInfo(), SourceLoc(), abExpr, - SourceLoc(), cases, SourceLoc(), C); - statements.push_back(switchStmt); - + SmallVector statements; auto body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc()); return { body, /*isTypeChecked=*/true }; } @@ -143,7 +125,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v SmallVector statements; SmallVector cases; - unsigned elementCount = 0; + unsigned elementCount = 0; // need this as `getAllElements` returns a generator // For each enum element, generate a case statement matching a pair containing // the same case, binding variables for the left- and right-hand associated @@ -333,9 +315,10 @@ deriveComparable_lt( return comparableDecl; } +// for now, only enums can synthesize `Comparable`, so this function can take +// an `EnumDecl` instead of a `NominalTypeDecl` bool -DerivedConformance::canDeriveComparable(DeclContext *context, NominalTypeDecl *declaration) { - auto enumeration = dyn_cast(declaration); +DerivedConformance::canDeriveComparable(DeclContext *context, EnumDecl *enumeration) { // The type must be an enum. if (!enumeration) { return false; @@ -349,26 +332,27 @@ DerivedConformance::canDeriveComparable(DeclContext *context, NominalTypeDecl *d } ValueDecl *DerivedConformance::deriveComparable(ValueDecl *requirement) { - if (checkAndDiagnoseDisallowedContext(requirement)) + if (checkAndDiagnoseDisallowedContext(requirement)) { return nullptr; + } + if (requirement->getBaseName() != "<") { + requirement->diagnose(diag::broken_comparable_requirement); + return nullptr; + } + // Build the necessary decl. - if (requirement->getBaseName() == "<") { - if (EnumDecl const *const enumeration = dyn_cast(this->Nominal)) { - std::pair (*synthesizer)(AbstractFunctionDecl *, void *); - if (enumeration->hasCases()) { - if (enumeration->hasOnlyCasesWithoutAssociatedValues()) { - synthesizer = &deriveBodyComparable_enum_noAssociatedValues_lt; - } else { - synthesizer = &deriveBodyComparable_enum_hasAssociatedValues_lt; - } - } else { - synthesizer = &deriveBodyComparable_enum_uninhabited_lt; - } - return deriveComparable_lt(*this, synthesizer); + auto enumeration = dyn_cast(this->Nominal); + assert(enumeration); + + std::pair (*synthesizer)(AbstractFunctionDecl *, void *); + if (enumeration->hasCases()) { + if (enumeration->hasOnlyCasesWithoutAssociatedValues()) { + synthesizer = &deriveBodyComparable_enum_noAssociatedValues_lt; } else { - llvm_unreachable("todo"); + synthesizer = &deriveBodyComparable_enum_hasAssociatedValues_lt; } + } else { + synthesizer = &deriveBodyComparable_enum_uninhabited_lt; } - requirement->diagnose(diag::broken_comparable_requirement); - return nullptr; + return deriveComparable_lt(*this, synthesizer); } diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 776d2b534e80f..29fc56be27542 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.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 diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index ccd77fc55df02..5d06c23b2da41 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.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 @@ -80,7 +80,7 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC, case KnownProtocolKind::Comparable: return !enumDecl->hasPotentiallyUnavailableCaseValue() - && canDeriveComparable(DC, Nominal); // why do y’all pass wide `NominalTypeDecl*` value when u could pass downcast `EnumDecl*` value? + && canDeriveComparable(DC, enumDecl); // "Simple" enums without availability attributes can explicitly derive // a CaseIterable conformance. @@ -405,7 +405,7 @@ GuardStmt *DerivedConformance::returnIfNotEqualGuard(ASTContext &C, SmallVector statements; auto returnStmt = new (C) ReturnStmt(SourceLoc(), guardReturnValue); - statements.emplace_back(ASTNode(returnStmt)); + statements.push_back(returnStmt); // Next, generate the condition being checked. // lhs == rhs diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index a70b857b3a0d6..c07f99ca38329 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.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 @@ -132,7 +132,7 @@ class DerivedConformance { /// This is implemented for enums without associated or raw values. /// /// \returns True if the requirement can be derived. - static bool canDeriveComparable(DeclContext *DC, NominalTypeDecl *type); + static bool canDeriveComparable(DeclContext *DC, EnumDecl *enumeration); /// Derive an Equatable requirement for a type. /// From 884584546a4c3a0c1e29fa750755dbfdf1c040f8 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Thu, 23 Jan 2020 22:53:23 -0600 Subject: [PATCH 015/237] clang format --- lib/Sema/DerivedConformances.h | 80 ++++++++++++++++------------------ 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index c07f99ca38329..db93df9c3483d 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -133,14 +133,14 @@ class DerivedConformance { /// /// \returns True if the requirement can be derived. static bool canDeriveComparable(DeclContext *DC, EnumDecl *enumeration); - + /// Derive an Equatable requirement for a type. /// /// This is implemented for enums without associated or raw values. /// /// \returns the derived member, which will also be added to the type. ValueDecl *deriveComparable(ValueDecl *requirement); - + /// Determine if an Equatable requirement can be derived for a type. /// /// This is implemented for enums without associated values or all-Equatable @@ -232,42 +232,42 @@ class DerivedConformance { /// /// \param synthesizing The decl that is being synthesized. bool checkAndDiagnoseDisallowedContext(ValueDecl *synthesizing) const; - + /// Returns a generated guard statement that checks whether the given lhs and /// rhs expressions are equal. If not equal, the else block for the guard /// returns `guardReturnValue`. /// \p C The AST context. /// \p lhsExpr The first expression to compare for equality. /// \p rhsExpr The second expression to compare for equality. - /// \p guardReturnValue The expression to return if the two sides are not equal - static - GuardStmt *returnIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr, - Expr *guardReturnValue); - // return false - static - GuardStmt *returnFalseIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr); + /// \p guardReturnValue The expression to return if the two sides are not + /// equal + static GuardStmt *returnIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, + Expr *rhsExpr, + Expr *guardReturnValue); + // return false + static GuardStmt *returnFalseIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, + Expr *rhsExpr); // return lhs < rhs - static - GuardStmt *returnComparisonIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr); - - /// Returns the ParamDecl for each associated value of the given enum whose type - /// does not conform to a protocol - /// \p theEnum The enum whose elements and associated values should be checked. - /// \p protocol The protocol being requested. - /// \return The ParamDecl of each associated value whose type does not conform. + static GuardStmt * + returnComparisonIfNotEqualGuard(ASTContext &C, Expr *lhsExpr, Expr *rhsExpr); + + /// Returns the ParamDecl for each associated value of the given enum whose + /// type does not conform to a protocol \p theEnum The enum whose elements and + /// associated values should be checked. \p protocol The protocol being + /// requested. \return The ParamDecl of each associated value whose type does + /// not conform. static SmallVector associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, - ProtocolDecl *protocol); + ProtocolDecl *protocol); /// Returns true if, for every element of the given enum, it either has no /// associated values or all of them conform to a protocol. - /// \p theEnum The enum whose elements and associated values should be checked. - /// \p protocol The protocol being requested. - /// \return True if all associated values of all elements of the enum conform. - static bool - allAssociatedValuesConformToProtocol(DeclContext *DC, - EnumDecl *theEnum, - ProtocolDecl *protocol); + /// \p theEnum The enum whose elements and associated values should be + /// checked. \p protocol The protocol being requested. \return True if all + /// associated values of all elements of the enum conform. + static bool allAssociatedValuesConformToProtocol(DeclContext *DC, + EnumDecl *theEnum, + ProtocolDecl *protocol); /// Create AST statements which convert from an enum to an Int with a switch. /// \p stmts The generated statements are appended to this vector. /// \p parentDC Either an extension or the enum itself. @@ -277,22 +277,18 @@ class DerivedConformance { /// \p indexName The name of the output variable. /// \return A DeclRefExpr of the output variable (of type Int). static DeclRefExpr * - convertEnumToIndex( SmallVectorImpl &stmts, - DeclContext *parentDC, - EnumDecl *enumDecl, - VarDecl *enumVarDecl, - AbstractFunctionDecl *funcDecl, - const char *indexName); - - static Pattern* - enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, - char varPrefix, DeclContext *varContext, - SmallVectorImpl &boundVars); - - static VarDecl * - indexedVarDecl(char prefixChar, int index, Type type, - DeclContext *varContext); + convertEnumToIndex(SmallVectorImpl &stmts, DeclContext *parentDC, + EnumDecl *enumDecl, VarDecl *enumVarDecl, + AbstractFunctionDecl *funcDecl, const char *indexName); + + static Pattern * + enumElementPayloadSubpattern(EnumElementDecl *enumElementDecl, char varPrefix, + DeclContext *varContext, + SmallVectorImpl &boundVars); + + static VarDecl *indexedVarDecl(char prefixChar, int index, Type type, + DeclContext *varContext); }; -} +} // namespace swift #endif From c97a0cf2d639154fd21a426de82cf5adf25fd79a Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 22 Jan 2020 14:10:57 -0800 Subject: [PATCH 016/237] [sil.rst] Clarify documentation around partial_apply closure contexts/closed over parameters. --- docs/SIL.rst | 90 +++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/docs/SIL.rst b/docs/SIL.rst index 4358e2ec12f3a..256b14beb74f0 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -3495,46 +3495,8 @@ partial_apply // %r will be of the substituted thick function type $(Z'...) -> R' Creates a closure by partially applying the function ``%0`` to a partial -sequence of its arguments. In the instruction syntax, the type of the callee is -specified after the argument list; the types of the argument and of the defined -value are derived from the function type of the callee. If the ``partial_apply`` -has an escaping function type (not ``[on_stack]``) the closure context will be -allocated with retain count 1 and initialized to contain the values ``%1``, -``%2``, etc. The closed-over values will not be retained; that must be done -separately before the ``partial_apply``. The closure does however take ownership -of the partially applied arguments (except for ``@inout_aliasable`` parameters); -when the closure reference count reaches zero, the contained values will be -destroyed. If the ``partial_apply`` has a ``@noescape`` function type -(``partial_apply [on_stack]``) the closure context is allocated on the stack and -initialized to contain the closed-over values. The closed-over values are not -retained, lifetime of the closed-over values must be managed separately. The -lifetime of the stack context of a ``partial_apply [on_stack]`` must be -terminated with a ``dealloc_stack``. - -If the callee is generic, all of its generic parameters must be bound by the -given substitution list. The arguments are given with these generic -substitutions applied, and the resulting closure is of concrete function -type with the given substitutions applied. The generic parameters themselves -cannot be partially applied; all of them must be bound. The result is always -a concrete function. - -If an address argument has ``@inout_aliasable`` convention, the closure -obtained from ``partial_apply`` will not own its underlying value. -The ``@inout_aliasable`` parameter convention is used when a ``@noescape`` -closure captures an ``inout`` argument. - -TODO: The instruction, when applied to a generic function, -currently implicitly performs abstraction difference transformations enabled -by the given substitutions, such as promoting address-only arguments and returns -to register arguments. This should be fixed. - -By default, ``partial_apply`` creates a closure whose invocation takes ownership -of the context, meaning that a call implicitly releases the closure. The -``[callee_guaranteed]`` change this to a caller-guaranteed model, where the -caller promises not to release the closure while the function is being called. - -This instruction is used to implement both curry thunks and closures. A -curried function in Swift:: +sequence of its arguments. This instruction is used to implement both curry +thunks and closures. A curried function in Swift:: func foo(_ a:A)(b:B)(c:C)(d:D) -> E { /* body of foo */ } @@ -3607,6 +3569,54 @@ lowers to an uncurried entry point and is curried in the enclosing function:: return %ret : $Int } +**Ownership Semantics of Closure Context during Invocation**: By default, an +escaping ``partial_apply`` (``partial_apply`` without ``[on_stack]]`` creates a +closure whose invocation takes ownership of the context, meaning that a call +implicitly releases the closure. If the ``partial_apply`` is marked with the +flag ``[callee_guaranteed]`` the invocation instead uses a caller-guaranteed +model, where the caller promises not to release the closure while the function +is being called. + +**Captured Value Ownership Semantics**: In the instruction syntax, the type of +the callee is specified after the argument list; the types of the argument and +of the defined value are derived from the function type of the callee. Even so, +the ownership requirements of the partial apply are not the same as that of the +callee function (and thus said signature). Instead: + +1. If the ``partial_apply`` has a ``@noescape`` function type (``partial_apply + [on_stack]``) the closure context is allocated on the stack and is + initialized to contain the closed-over values without taking ownership of + those values. The closed-over values are not retained and the lifetime of the + closed-over values must be managed by other instruction independently of the + ``partial_apply``. The lifetime of the stack context of a ``partial_apply + [on_stack]`` must be terminated with a ``dealloc_stack``. + +2. If the ``partial_apply`` has an escaping function type (not ``[on_stack]``) + then the closure context will be heap allocated with a retain count of 1. Any + closed over parameters (except for ``@inout`` parameters) will be consumed by + the partial_apply. This ensures that no matter when the ``partial_apply`` is + called, the captured arguments are alive. When the closure context's + reference count reaches zero, the contained values are destroyed. If the + callee requires an owned parameter, then the implicit partial_apply forwarder + created by IRGen will copy the underlying argument and pass it to the callee. + +3. If an address argument has ``@inout_aliasable`` convention, the closure + obtained from ``partial_apply`` will not own its underlying value. The + ``@inout_aliasable`` parameter convention is used when a ``@noescape`` + closure captures an ``inout`` argument. + +**NOTE:** If the callee is generic, all of its generic parameters must be bound +by the given substitution list. The arguments are given with these generic +substitutions applied, and the resulting closure is of concrete function type +with the given substitutions applied. The generic parameters themselves cannot +be partially applied; all of them must be bound. The result is always a concrete +function. + +**TODO:** The instruction, when applied to a generic function, currently +implicitly performs abstraction difference transformations enabled by the given +substitutions, such as promoting address-only arguments and returns to register +arguments. This should be fixed. + builtin ``````` :: From d71b3dfec887de32f41f931e5af7a96cde49364a Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Sat, 25 Jan 2020 13:50:28 -0600 Subject: [PATCH 017/237] small vector sizes --- lib/Sema/DerivedConformanceComparable.cpp | 10 +++++----- lib/Sema/DerivedConformances.cpp | 6 +++--- lib/Sema/DerivedConformances.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Sema/DerivedConformanceComparable.cpp b/lib/Sema/DerivedConformanceComparable.cpp index cf5a5c313904d..2345347088060 100644 --- a/lib/Sema/DerivedConformanceComparable.cpp +++ b/lib/Sema/DerivedConformanceComparable.cpp @@ -64,7 +64,7 @@ deriveBodyComparable_enum_noAssociatedValues_lt(AbstractFunctionDecl *ltDecl, auto enumDecl = cast(aParam->getType()->getAnyNominal()); // Generate the conversion from the enums to integer indices. - SmallVector statements; + SmallVector statements; DeclRefExpr *aIndex = DerivedConformance::convertEnumToIndex(statements, parentDC, enumDecl, aParam, ltDecl, "index_a"); DeclRefExpr *bIndex = DerivedConformance::convertEnumToIndex(statements, parentDC, enumDecl, @@ -123,7 +123,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v Type enumType = aParam->getType(); auto enumDecl = cast(aParam->getType()->getAnyNominal()); - SmallVector statements; + SmallVector statements; SmallVector cases; unsigned elementCount = 0; // need this as `getAllElements` returns a generator @@ -134,7 +134,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v elementCount++; // .(let l0, let l1, ...) - SmallVector lhsPayloadVars; + SmallVector lhsPayloadVars; auto lhsSubpattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'l', ltDecl, lhsPayloadVars); auto lhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), @@ -144,7 +144,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v lhsElemPat->setImplicit(); // .(let r0, let r1, ...) - SmallVector rhsPayloadVars; + SmallVector rhsPayloadVars; auto rhsSubpattern = DerivedConformance::enumElementPayloadSubpattern(elt, 'r', ltDecl, rhsPayloadVars); auto rhsElemPat = new (C) EnumElementPattern(TypeLoc::withoutLoc(enumType), @@ -182,7 +182,7 @@ deriveBodyComparable_enum_hasAssociatedValues_lt(AbstractFunctionDecl *ltDecl, v // Generate a guard statement for each associated value in the payload, // breaking out early if any pair is unequal. (same as Equatable synthesis.) // the else statement performs the lexicographic comparison. - SmallVector statementsInCase; + SmallVector statementsInCase; for (size_t varIdx = 0; varIdx < lhsPayloadVars.size(); varIdx++) { auto lhsVar = lhsPayloadVars[varIdx]; auto lhsExpr = new (C) DeclRefExpr(lhsVar, DeclNameLoc(), diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 5d06c23b2da41..fc7c23fe18873 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -554,10 +554,10 @@ DeclRefExpr *DerivedConformance::convertEnumToIndex(SmallVectorImpl &st /// \p theEnum The enum whose elements and associated values should be checked. /// \p protocol The protocol being requested. /// \return The ParamDecl of each associated value whose type does not conform. -SmallVector +SmallVector DerivedConformance::associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol) { - SmallVector nonconformingAssociatedValues; + SmallVector nonconformingAssociatedValues; for (auto elt : theEnum->getAllElements()) { auto PL = elt->getParameterList(); if (!PL) @@ -610,7 +610,7 @@ DerivedConformance::enumElementPayloadSubpattern(EnumElementDecl *enumElementDec // types, and labels. For example: // case a(x: Int) => (x: let a0) // case b(Int, String) => (let a0, let a1) - SmallVector elementPatterns; + SmallVector elementPatterns; int index = 0; for (auto tupleElement : tupleType->getElements()) { auto payloadVar = indexedVarDecl(varPrefix, index++, diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index db93df9c3483d..55d8a380c796b 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -256,7 +256,7 @@ class DerivedConformance { /// associated values should be checked. \p protocol The protocol being /// requested. \return The ParamDecl of each associated value whose type does /// not conform. - static SmallVector + static SmallVector associatedValuesNotConformingToProtocol(DeclContext *DC, EnumDecl *theEnum, ProtocolDecl *protocol); From 42345dabd342fe793e4eb3bf338ba9560fac0725 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Tue, 28 Jan 2020 12:56:59 -0800 Subject: [PATCH 018/237] SymbolGraph: Move pathComponents up and include interfaceLanguage `pathComponents` doesn't help with disambiguation, so it shouldn't be a part of the identifier, but can be moved up one level. Include an interface language in the identifier instead. rdar://problem/58853310 --- lib/SymbolGraphGen/Edge.cpp | 10 +++-- lib/SymbolGraphGen/Symbol.cpp | 24 ++++++++++-- lib/SymbolGraphGen/Symbol.h | 43 ++------------------- lib/SymbolGraphGen/SymbolGraphASTWalker.cpp | 29 +++----------- lib/SymbolGraphGen/SymbolGraphASTWalker.h | 8 +--- 5 files changed, 37 insertions(+), 77 deletions(-) diff --git a/lib/SymbolGraphGen/Edge.cpp b/lib/SymbolGraphGen/Edge.cpp index 296cc0960fee2..22f43eacc5402 100644 --- a/lib/SymbolGraphGen/Edge.cpp +++ b/lib/SymbolGraphGen/Edge.cpp @@ -25,12 +25,14 @@ void Edge::serialize(llvm::json::OStream &OS) const { // In case a dependent module isn't available, serialize a fallback name. auto TargetModuleName = Target->getModuleContext()->getName().str(); + if (TargetModuleName != Walker->M.getName().str()) { - auto TargetSymbolIdentifier = Walker->getSymbolIdentifier(Target); - auto TargetComponents = TargetSymbolIdentifier.SimpleComponents; + SmallVector, 8> TargetPathComponents; + Walker->getPathComponents(Target, TargetPathComponents); + SmallString<128> Scratch(TargetModuleName); - for (auto it = TargetComponents.begin(); - it != TargetComponents.end(); ++it) { + for (auto it = TargetPathComponents.begin(); + it != TargetPathComponents.end(); ++it) { Scratch.push_back('.'); Scratch.append(*it); } diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index c30210d75a1e1..425b98217b921 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -76,15 +76,30 @@ void Symbol::serializeKind(llvm::json::OStream &OS) const { void Symbol::serializeIdentifier(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const { - AttributeRAII A("identifier", OS); - Walker.getSymbolIdentifier(VD).serialize(OS); + OS.attributeObject("identifier", [&](){ + OS.attribute("precise", Walker.getUSR(VD)); + OS.attribute("interfaceLanguage", "swift"); + }); +} + +void Symbol::serializePathComponents(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const { + OS.attributeArray("pathComponents", [&](){ + SmallVector, 8> PathComponents; + Walker.getPathComponents(VD, PathComponents); + for (auto Component : PathComponents) { + OS.value(Component); + } + }); } void Symbol::serializeNames(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const { OS.attributeObject("names", [&](){ - auto Identifier = Walker.getSymbolIdentifier(VD); - OS.attribute("title", Identifier.SimpleComponents.back()); + SmallVector, 8> PathComponents; + Walker.getPathComponents(VD, PathComponents); + + OS.attribute("title", PathComponents.back()); // "navigator": null Walker.serializeSubheadingDeclarationFragments("subheading", VD, OS); // "prose": null @@ -370,6 +385,7 @@ void Symbol::serialize(SymbolGraphASTWalker &Walker, OS.object([&](){ serializeKind(OS); serializeIdentifier(Walker, OS); + serializePathComponents(Walker, OS); serializeNames(Walker, OS); serializeDocComment(Walker, OS); diff --git a/lib/SymbolGraphGen/Symbol.h b/lib/SymbolGraphGen/Symbol.h index 9cdbca5757164..6e22255596b50 100644 --- a/lib/SymbolGraphGen/Symbol.h +++ b/lib/SymbolGraphGen/Symbol.h @@ -24,46 +24,6 @@ namespace symbolgraphgen { struct AvailabilityDomain; struct SymbolGraphASTWalker; -/** - An identifier for a symbol that provides a globally unique identifier suitable for - internal lookups and a locally unique path for human use, such as a URL. - */ -struct SymbolIdentifier { - /** - A string that uniquely identifies a symbol within a module in the event of - ambiguities. A precise identifier need not be human readable. - */ - StringRef PreciseIdentifier; - - /** - The components for a "fully qualified" identifier. - */ - ArrayRef SimpleComponents; - - SymbolIdentifier(llvm::StringRef PreciseIdentifier, - ArrayRef SimpleComponents) - : PreciseIdentifier(PreciseIdentifier), - SimpleComponents(SimpleComponents) { - assert(!PreciseIdentifier.empty()); - } - - void serialize(llvm::json::OStream &OS) const { - OS.object([&](){ - OS.attribute("precise", PreciseIdentifier); - OS.attributeArray("simpleComponents", [&](){ - for (auto Component : SimpleComponents) { - OS.value(Component); - } - }); - }); - } - - bool operator==(const SymbolIdentifier &Other) const { - return PreciseIdentifier == Other.PreciseIdentifier && - SimpleComponents == Other.SimpleComponents; - } -}; - /// A symbol from a module: a node in a graph. struct Symbol { const ValueDecl *VD; @@ -76,6 +36,9 @@ struct Symbol { void serializeIdentifier(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const; + void serializePathComponents(SymbolGraphASTWalker &Walker, + llvm::json::OStream &OS) const; + void serializeNames(SymbolGraphASTWalker &Walker, llvm::json::OStream &OS) const; diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp index 2ce1337d65468..cc370438be29b 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.cpp @@ -149,24 +149,15 @@ StringRef SymbolGraphASTWalker::getUSR(const ValueDecl *VD) { return USR; } -SymbolIdentifier -SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { - // Look in the symbol identifier cache for this declartion. - auto Found = SymbolIdentifierCache.find(VD); - if (Found != SymbolIdentifierCache.end()) { - return Found->getSecond(); - } - - // Not found; need to build a symbol identifier and add it to the cache. - auto PreciseIdentifier = getUSR(VD); - llvm::SmallVector SimpleIdentifierChain; - +void +SymbolGraphASTWalker::getPathComponents(const ValueDecl *VD, + SmallVectorImpl> &Components) { // Collect the spellings of the fully qualified identifier components. auto Decl = VD; while (Decl && !isa(Decl)) { SmallString<32> Scratch; Decl->getFullName().getString(Scratch); - SimpleIdentifierChain.push_back(Ctx.allocateCopy(Scratch.str())); + Components.push_back(Scratch); if (const auto *DC = Decl->getDeclContext()) { if (const auto *Proto = DC->getExtendedProtocolDecl()) { Decl = Proto; @@ -179,17 +170,9 @@ SymbolGraphASTWalker::getSymbolIdentifier(const ValueDecl *VD) { Decl = nullptr; } } - - // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. - std::reverse(SimpleIdentifierChain.begin(), SimpleIdentifierChain.end()); - SymbolIdentifier Identifier { - PreciseIdentifier, - Ctx.allocateCopy(llvm::makeArrayRef(SimpleIdentifierChain)) - }; - - SymbolIdentifierCache.insert({VD, Identifier}); - return Identifier; + // The list is leaf-to-root, but our list is root-to-leaf, so reverse it. + std::reverse(Components.begin(), Components.end()); } PrintOptions SymbolGraphASTWalker::getDeclarationFragmentsPrintOptions() const { diff --git a/lib/SymbolGraphGen/SymbolGraphASTWalker.h b/lib/SymbolGraphGen/SymbolGraphASTWalker.h index fb3e3dc9b6d0d..adf15ec5d46ca 100644 --- a/lib/SymbolGraphGen/SymbolGraphASTWalker.h +++ b/lib/SymbolGraphGen/SymbolGraphASTWalker.h @@ -27,7 +27,6 @@ class ValueDecl; namespace symbolgraphgen { -struct SymbolIdentifier; struct SymbolGraph; struct SymbolGraphOptions; @@ -49,9 +48,6 @@ struct SymbolGraphASTWalker : public SourceEntityWalker { /// A context for allocations. markup::MarkupContext Ctx; - /// A cache of identifiers for declarations that may be seen more than once. - llvm::DenseMap SymbolIdentifierCache; - /// A cache of USRs for declarations. llvm::DenseMap USRCache; @@ -72,8 +68,8 @@ struct SymbolGraphASTWalker : public SourceEntityWalker { /// Get the USR of a declaration and add it to the local allocator. StringRef getUSR(const ValueDecl *VD); - /// Returns a `SymbolIdentifier` for a given declaration. - SymbolIdentifier getSymbolIdentifier(const ValueDecl *VD); + /// Returns an array of path components for a declaration. + void getPathComponents(const ValueDecl *VD, SmallVectorImpl> &Components); // MARK: - Declaration Fragments From 37e403db78c78a39e046b810cd3bd702ec1f598d Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 28 Jan 2020 16:16:04 -0800 Subject: [PATCH 019/237] Compile libraries in testcases with -parse-as-library (NFC) This is in preparation to a change in serialization of global variables where this detail will matter. --- test/DebugInfo/inlinescopes.swift | 2 +- test/SILGen/expressions.swift | 2 +- test/SILGen/global_init_attribute.swift | 2 +- test/SILGen/global_resilience.swift | 2 +- test/Serialization/Recovery/typedefs.swift | 3 +++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/DebugInfo/inlinescopes.swift b/test/DebugInfo/inlinescopes.swift index 769b8a38ce8f0..6c4a3a4fefc3d 100644 --- a/test/DebugInfo/inlinescopes.swift +++ b/test/DebugInfo/inlinescopes.swift @@ -6,7 +6,7 @@ // RUN: %FileCheck %s -check-prefix=TRANSPARENT-CHECK < %t.ll // CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main{{.*}} -// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %4), !dbg ![[CALL:.*]] +// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %{{.*}}), !dbg ![[CALL:.*]] // CHECK-DAG: ![[TOPLEVEL:.*]] = !DIFile(filename: "{{.*}}inlinescopes.swift" import FooBar diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index f642702602900..54654a6ee7b29 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t - +// RUN: echo "public var x = Int()" | %target-swift-frontend -parse-as-library -module-name FooBar -emit-module -o %t - // RUN: %target-swift-emit-silgen -parse-stdlib -module-name expressions %s -I%t -disable-access-control | %FileCheck %s import Swift diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index f47bde0192a73..95167c7127078 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-module -o %t %S/Inputs/def_global.swift +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-module -o %t %S/Inputs/def_global.swift // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -I %t %s | %FileCheck %s // // Test that SILGen uses the "global_init" attribute for all global diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index c05720d0713eb..78b425a9ed684 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift +// RUN: %target-swift-frontend -emit-module -parse-as-library -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift // RUN: %target-swift-emit-silgen -I %t -enable-library-evolution -parse-as-library %s | %FileCheck %s // RUN: %target-swift-emit-sil -I %t -O -enable-library-evolution -parse-as-library %s | %FileCheck --check-prefix=CHECK-OPT %s diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index a9fab839e045b..4b33701fd00e8 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -1,4 +1,7 @@ // RUN: %empty-directory(%t) + +// Cannot use -parse-as-library here because that would compile also the +// #if VERIFY path, which contains top-level code. // RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s From f2c61d8ec58fd10535fc3eb2cab192a47de7e222 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Mon, 27 Jan 2020 21:48:19 -0800 Subject: [PATCH 020/237] Bug fixes for type fingerprints --- ...endenciesSourceFileDepGraphConstructor.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index 41df94b5fab71..a2cb94342861f 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -680,12 +680,10 @@ class SourceFileDepGraphConstructor { for (const auto &contextNameFingerprint : contextNameFingerprints) { auto p = g.findExistingNodePairOrCreateAndAddIfNew( kind, contextNameFingerprint); - // When we don't have a fingerprint yet, must rebuild every provider when - // interfaceHash changes. So when interface (i.e. interface hash) of - // sourceFile changes, every provides is dirty. And since we don't know - // what happened, dirtyness might affect the interface. - if (!p.getInterface()->getFingerprint().hasValue()) - g.addArc(g.getSourceFileNodePair().getInterface(), p.getInterface()); + // Since the current type fingerprints only include tokens in the body, + // when the interface hash changes, it is possible that the type in the + // file has changed. + g.addArc(g.getSourceFileNodePair().getInterface(), p.getInterface()); } } @@ -809,8 +807,15 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( // that may have been there. No error handling -- this is just a nicety, it // doesn't matter if it fails. llvm::sys::fs::rename(outputPath, outputPath + "~"); + // Since, when fingerprints are enabled, + // the parser diverts token hashing into per-body fingerprints + // before it can know if a difference is in a private type, + // in order to be able to test the changed fingerprints + // we force the inclusion of private declarations when fingerprints + // are enabled. const bool includeIntrafileDeps = - SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes; + SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || + SF->getASTContext().LangOpts.EnableTypeFingerprints; const bool hadCompilationError = SF->getASTContext().hadError(); auto gc = SourceFileDepGraphConstructor::forSourceFile( SF, depTracker, outputPath, includeIntrafileDeps, hadCompilationError); From bbffc0959b714a0300d92a2f8549309f841b1fae Mon Sep 17 00:00:00 2001 From: David Ungar Date: Mon, 27 Jan 2020 21:48:51 -0800 Subject: [PATCH 021/237] Tests for type fingerprints are enabled. --- ...ine_grained_swiftdeps_with_fingerprints.sh | 7 ++ .../added_method-type-fingerprints.swift | 55 ++++++++++++ ...s_private_property-type-fingerprints.swift | 53 +++++++++++ ...ate_class_property-type-fingerprints.swift | 53 +++++++++++ ...m_private_property-type-fingerprints.swift | 56 ++++++++++++ ...vate_enum_property-type-fingerprints.swift | 55 ++++++++++++ ...ded_private_method-type-fingerprints.swift | 55 ++++++++++++ ...method_value_types-type-fingerprints.swift | 88 +++++++++++++++++++ ...te_protocol_method-type-fingerprints.swift | 50 +++++++++++ ..._protocol_property-type-fingerprints.swift | 50 +++++++++++ ...t_private_property-type-fingerprints.swift | 56 ++++++++++++ ...te_struct_property-type-fingerprints.swift | 56 ++++++++++++ ...edited_method_body-type-fingerprints.swift | 31 +++++++ test/InterfaceHash/edited_method_body.swift | 4 +- ...ed_property_getter-type-fingerprints.swift | 33 +++++++ .../edited_property_getter.swift | 4 +- .../rdar23148987-type-fingerprints.swift | 61 +++++++++++++ .../Serialization/rdar40839486.swift | 4 +- 18 files changed, 765 insertions(+), 6 deletions(-) create mode 100755 test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh create mode 100644 test/InterfaceHash/added_method-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_class_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_enum_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_method-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/added_private_struct_property-type-fingerprints.swift create mode 100644 test/InterfaceHash/edited_method_body-type-fingerprints.swift create mode 100644 test/InterfaceHash/edited_property_getter-type-fingerprints.swift create mode 100644 validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift diff --git a/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh b/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh new file mode 100755 index 0000000000000..80bb392c22cfb --- /dev/null +++ b/test/Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +# Fine-grained swiftdeps files use multiple lines for each graph node. +# Compress such a file so that each entry is one line of the form: +# +# Also sort for consistency, since the node order can vary. + +awk '/kind:/ {k = $2; f = ""}; /aspect:/ {a = $2}; /context:/ {c = $2}; /name/ {n = $2}; /sequenceNumber/ {s = $2}; /fingerprint:/ {f = $2 }; /isProvides:/ {isP = $2; print k, a, c, n, isP, f}' | sort diff --git a/test/InterfaceHash/added_method-type-fingerprints.swift b/test/InterfaceHash/added_method-type-fingerprints.swift new file mode 100644 index 0000000000000..7b51e1f67b758 --- /dev/null +++ b/test/InterfaceHash/added_method-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..cf3a104cc71b1 --- /dev/null +++ b/test/InterfaceHash/added_private_class_private_property-type-fingerprints.swift @@ -0,0 +1,53 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +private class C { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 +} + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_class_property-type-fingerprints.swift b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift new file mode 100644 index 0000000000000..f4e01619c1b76 --- /dev/null +++ b/test/InterfaceHash/added_private_class_property-type-fingerprints.swift @@ -0,0 +1,53 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 +} + +// Since C is a type or extension, the interface hash ought to not get the +// changed token hash. + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..9ad0b28d56449 --- /dev/null +++ b/test/InterfaceHash/added_private_enum_private_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + + +// BEGIN a.swift +private enum A { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +private enum A { + case x, y + func f2() -> Int { + return 0 + } + + var foo: Int { return 0 } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift new file mode 100644 index 0000000000000..c8220f6f9a54b --- /dev/null +++ b/test/InterfaceHash/added_private_enum_property-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +enum A { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +enum A { + case x, y + func f2() -> Int { + return 0 + } + + private var foo: Int { return 0 } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method-type-fingerprints.swift b/test/InterfaceHash/added_private_method-type-fingerprints.swift new file mode 100644 index 0000000000000..c0c60206d9c92 --- /dev/null +++ b/test/InterfaceHash/added_private_method-type-fingerprints.swift @@ -0,0 +1,55 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' C true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' C true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1C{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1C{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1C{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift new file mode 100644 index 0000000000000..42fee00e31bf0 --- /dev/null +++ b/test/InterfaceHash/added_private_method_value_types-type-fingerprints.swift @@ -0,0 +1,88 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +struct A { + func f2() -> Int { + return 0 + } +} + +enum B { + case x, y + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +struct A { + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +enum B { + case x, y + func f2() -> Int { + return 0 + } + + private func f3() -> Int { + return 1 + } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' A true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' A true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1A{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1A{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' B true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' B true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1B{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1B{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1B{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift new file mode 100644 index 0000000000000..c0ccd54a11771 --- /dev/null +++ b/test/InterfaceHash/added_private_protocol_method-type-fingerprints.swift @@ -0,0 +1,50 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private protocol P { + func f2() -> Int + var y: Int { get set } +} + +// BEGIN b.swift +private protocol P { + func f2() -> Int + func f3() -> Int + var y: Int { get set } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift new file mode 100644 index 0000000000000..c27cee3752b83 --- /dev/null +++ b/test/InterfaceHash/added_private_protocol_property-type-fingerprints.swift @@ -0,0 +1,50 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private protocol P { + func f2() -> Int + var y: Int { get set } +} + +// BEGIN b.swift +private protocol P { + func f2() -> Int + var x: Int { get set } + var y: Int { get set } +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' P true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' P true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1P{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1P{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1P{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift new file mode 100644 index 0000000000000..47e371aff0901 --- /dev/null +++ b/test/InterfaceHash/added_private_struct_private_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +struct S { + func f2() -> Int { + return 0 + } + + var y: Int = 0 +} + +// BEGIN b.swift +struct S { + func f2() -> Int { + return 0 + } + + private var x: Int = 0 + var y: Int = 0 +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift new file mode 100644 index 0000000000000..bd92b5dfbe37f --- /dev/null +++ b/test/InterfaceHash/added_private_struct_property-type-fingerprints.swift @@ -0,0 +1,56 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: not diff %t/{a,b}-processed.swiftdeps >%t/diffs + +// BEGIN a.swift +private struct S { + func f2() -> Int { + return 0 + } + + var y: Int = 0 +} + +// BEGIN b.swift +private struct S { + func f2() -> Int { + return 0 + } + + var x: Int = 0 + var y: Int = 0 +} + +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-SAME-INTERFACE-HASH +// RUN: %FileCheck %s <%t/diffs -check-prefix=CHECK-DIFFERENT-TYPE-FINGERPRINT + +// CHECK-SAME-INTERFACE-HASH-NOT: sourceFileProvides + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < topLevel interface '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel implementation '' S true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > topLevel interface '' S true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < nominal interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > nominal interface 4main1S{{[^ ]+}} '' true + +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: < potentialMember interface 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember implementation 4main1S{{[^ ]+}} '' true +// CHECK-DIFFERENT-TYPE-FINGERPRINT-DAG: > potentialMember interface 4main1S{{[^ ]+}} '' true diff --git a/test/InterfaceHash/edited_method_body-type-fingerprints.swift b/test/InterfaceHash/edited_method_body-type-fingerprints.swift new file mode 100644 index 0000000000000..10ae3e000c09f --- /dev/null +++ b/test/InterfaceHash/edited_method_body-type-fingerprints.swift @@ -0,0 +1,31 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps + +// BEGIN a.swift +class C { + func f2() -> Int { + return 0 + } +} + +// BEGIN b.swift +class C { + func f2() -> Int { + return 1 + } +} diff --git a/test/InterfaceHash/edited_method_body.swift b/test/InterfaceHash/edited_method_body.swift index 2f87dd9f02fef..b0aebce44e500 100644 --- a/test/InterfaceHash/edited_method_body.swift +++ b/test/InterfaceHash/edited_method_body.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/test/InterfaceHash/edited_property_getter-type-fingerprints.swift b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift new file mode 100644 index 0000000000000..0a0b5fdaaff54 --- /dev/null +++ b/test/InterfaceHash/edited_property_getter-type-fingerprints.swift @@ -0,0 +1,33 @@ +// REQUIRES: shell +// Also uses awk: +// XFAIL OS=windows + +// When adding a private protocol method, the interface hash should stay the same +// The per-type fingerprint should change + +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/split_file.py -o %t %s +// RUN: cp %t/{a,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/a-processed.swiftdeps +// RUN: cp %t/{b,x}.swift +// RUN: %target-swift-frontend -typecheck -enable-type-fingerprints -primary-file %t/x.swift -emit-reference-dependencies-path %t/x.swiftdeps -module-name main +// RUN: %S/../Inputs/process_fine_grained_swiftdeps_with_fingerprints.sh <%t/x.swiftdeps >%t/b-processed.swiftdeps + +// RUN: cmp %t/{a,b}-processed.swiftdeps + +// BEGIN a.swift +class C { + var p: Int { + return 0 + } +} + +// BEGIN b.swift +class C { + var p: Int { + let x = 1 + return x + } +} + diff --git a/test/InterfaceHash/edited_property_getter.swift b/test/InterfaceHash/edited_property_getter.swift index 42584e33f6467..deaeb8ccc08dc 100644 --- a/test/InterfaceHash/edited_property_getter.swift +++ b/test/InterfaceHash/edited_property_getter.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/split_file.py -o %t %s -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash -// RUN: %target-swift-frontend -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/a.swift 2> %t/a.hash +// RUN: %target-swift-frontend -disable-type-fingerprints -dump-interface-hash -primary-file %t/b.swift 2> %t/b.hash // RUN: cmp %t/a.hash %t/b.hash // BEGIN a.swift diff --git a/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift new file mode 100644 index 0000000000000..3eefcdd073ba8 --- /dev/null +++ b/validation-test/Driver/Dependencies/rdar23148987-type-fingerprints.swift @@ -0,0 +1,61 @@ +// RUN: %empty-directory(%t) + +// RUN: cp %s %t/main.swift +// RUN: cp %S/Inputs/rdar23148987/helper-1.swift %t/helper.swift +// RUN: touch -t 201401240005 %t/*.swift + +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1 %s + +// CHECK-1-NOT: warning +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/main.swift" +// CHECK-1: {{^}$}} + +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/helper.swift" +// CHECK-1: {{^}$}} + +// RUN: ls %t/ | %FileCheck -check-prefix=CHECK-LS %s + +// CHECK-LS-DAG: main.o +// CHECK-LS-DAG: helper.o + +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | %FileCheck -check-prefix=CHECK-1-SKIPPED %s + +// CHECK-1-SKIPPED-NOT: warning +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/main.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/helper.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// RUN: cp %S/Inputs/rdar23148987/helper-2.swift %t/helper.swift +// RUN: touch -t 201401240006 %t/helper.swift +// RUN: cd %t && %target-build-swift -enable-type-fingerprints -c -incremental -output-file-map %S/Inputs/rdar23148987/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 -driver-show-incremental -driver-show-job-lifecycle | %FileCheck -check-prefix=CHECK-2 %s + +// CHECK-2-NOT: warning +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/helper.swift" +// CHECK-2: {{^}$}} + +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/main.swift" +// CHECK-2: {{^}$}} + +func test(obj: Test) { + obj.foo() +} diff --git a/validation-test/Serialization/rdar40839486.swift b/validation-test/Serialization/rdar40839486.swift index 3704eec44bf09..8c4e1531b0db1 100644 --- a/validation-test/Serialization/rdar40839486.swift +++ b/validation-test/Serialization/rdar40839486.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -disable-type-fingerprints -emit-module-path %t/main4.swiftmodule -swift-version 4 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s -// RUN: %target-build-swift -disable-type-fingerprints -emit-module-path %t/main4_2.swiftmodule -swift-version 4.2 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s +// RUN: %target-build-swift -emit-module-path %t/main4.swiftmodule -swift-version 4 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s +// RUN: %target-build-swift -emit-module-path %t/main4_2.swiftmodule -swift-version 4.2 -Fsystem %sdk/System/Library/PrivateFrameworks/ %s // REQUIRES: OS=macosx || OS=ios From b0786e7e06230a3d1f59271389b56222ea74aca3 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 28 Jan 2020 21:13:22 -0800 Subject: [PATCH 022/237] Fix propagation of guaranteed phi args during DiagnoseUnreachable. Fixes assertion failure - UNREACHABLE executed at swift/include/swift/SIL/OwnershipUtils.h:127! --- .../Mandatory/DiagnoseUnreachable.cpp | 7 ++--- test/SILOptimizer/diagnose_unreachable.sil | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp index 6923725dc55b6..107829132d2a8 100644 --- a/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp +++ b/lib/SILOptimizer/Mandatory/DiagnoseUnreachable.cpp @@ -185,12 +185,9 @@ static void propagateBasicBlockArgs(SILBasicBlock &BB) { // this to CCP and trigger another round of copy propagation. SILArgument *Arg = *AI; - // If this argument is guaranteed and Args[Idx] is a SILFunctionArgument, - // delete the end_borrow. - if (Arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed && - isa(Args[Idx])) { + // If this argument is guaranteed and Args[Idx], delete the end_borrow. + if (Arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed) deleteEndBorrows(Arg); - } // We were able to fold, so all users should use the new folded value. Arg->replaceAllUsesWith(Args[Idx]); diff --git a/test/SILOptimizer/diagnose_unreachable.sil b/test/SILOptimizer/diagnose_unreachable.sil index bbf443dc223ec..7045ca9e368ab 100644 --- a/test/SILOptimizer/diagnose_unreachable.sil +++ b/test/SILOptimizer/diagnose_unreachable.sil @@ -782,3 +782,33 @@ bb1(%2 : @owned $Builtin.NativeObject): bb2: unreachable } + +// Test propagation of guaranteed phi arguments. The nested end_borrow +// must be removed, even with the outer borrow is *not* a function +// argument. + +enum EnumWithB { + case A(B) + func testit() -> Int +} + +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK: bb1([[PHI:%.*]] : @guaranteed $B): +// CHECK: br bb2 +// CHECK: bb2: +// CHECK: end_borrow [[PHI]] : $B +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' +sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +bb0(%0 : @guaranteed $EnumWithB): + switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 + +bb1(%2 : @guaranteed $B): + br bb3(%2 : $B) + +bb3(%4 : @guaranteed $B): + end_borrow %4 : $B + end_borrow %2 : $B + %99 = tuple () + return %99 : $() +} From 1d747bc351521c965c7a80eb2621664df6370517 Mon Sep 17 00:00:00 2001 From: Butta Date: Sun, 26 Jan 2020 12:02:11 +0530 Subject: [PATCH 023/237] [sourcekitd-test] Remove redundant LLVMCore static library This library is already supplied by the LLVM CMake config, so adding it here sometimes causes the CommandLine option parser to fail, as it registers the same option twice. --- tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt index cf69c7b9c6e82..91c28f371c4d6 100644 --- a/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt +++ b/tools/SourceKit/tools/sourcekitd-test/CMakeLists.txt @@ -5,7 +5,7 @@ swift_add_public_tablegen_target(sourcekitdTestOptionsTableGen) add_sourcekit_executable(sourcekitd-test sourcekitd-test.cpp TestOptions.cpp - LLVM_LINK_COMPONENTS core option coverage lto + LLVM_LINK_COMPONENTS option coverage lto ) target_link_libraries(sourcekitd-test PRIVATE SourceKitSupport From 1af49ecb996e66aa0b7de7395ee073973a13ed7c Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 28 Jan 2020 22:54:08 -0800 Subject: [PATCH 024/237] Fix an EscapeAnalysis assert to handle recent changes. setPointsToEdge should assert that its target isn't already merged, but now that we batch up multiple merge requests, it's fine to allow the target to be scheduled-for-merge. Many assertions have been recently added and tightened in order to "discover" unexpected cases. There's nothing incorrect about how these cases were handled, but they lack unit tests. In this case I still haven't been able to reduce a test case. I'm continuing to work on it, but don't want to further delay the fix. --- include/swift/SILOptimizer/Analysis/EscapeAnalysis.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h index c1d405c4d7f42..2d56b97d85695 100644 --- a/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h +++ b/include/swift/SILOptimizer/Analysis/EscapeAnalysis.h @@ -449,7 +449,7 @@ class EscapeAnalysis : public BottomUpIPAnalysis { /// Sets the outgoing points-to edge. The \p To node must be a Content node. void setPointsToEdge(CGNode *To) { - assert(!To->mergeTo); + assert(!To->isMerged); assert(To->Type == NodeType::Content && "Wrong node type for points-to edge"); pointsToIsEdge = true; From b2083db45d88696eec1c34e5e79ce1f9849e290b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 29 Jan 2020 00:06:57 -0800 Subject: [PATCH 025/237] [ConstraintSystem] Add a fix to allow conversion between non-class type and AnyObject --- lib/Sema/CSFix.cpp | 37 +++++++++++++++++++++++++++++++++++++ lib/Sema/CSFix.h | 22 ++++++++++++++++++++-- lib/Sema/CSSimplify.cpp | 13 +++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index ffc4e502509a5..95e78c3a454be 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1180,3 +1180,40 @@ SpecifyObjectLiteralTypeImport::create(ConstraintSystem &cs, ConstraintLocator *locator) { return new (cs.getAllocator()) SpecifyObjectLiteralTypeImport(cs, locator); } + +AllowNonClassTypeToConvertToAnyObject::AllowNonClassTypeToConvertToAnyObject( + ConstraintSystem &cs, Type type, ConstraintLocator *locator) + : ContextualMismatch(cs, FixKind::AllowNonClassTypeToConvertToAnyObject, + type, cs.getASTContext().getAnyObjectType(), locator) { +} + +bool AllowNonClassTypeToConvertToAnyObject::diagnose(bool asNote) const { + auto &cs = getConstraintSystem(); + + auto *locator = getLocator(); + if (locator->getPath().empty()) + return false; + + const auto &last = locator->getPath().back(); + switch (last.getKind()) { + case ConstraintLocator::ContextualType: { + ContextualFailure failure(cs, getFromType(), getToType(), locator); + return failure.diagnose(asNote); + } + + case ConstraintLocator::ApplyArgToParam: { + ArgumentMismatchFailure failure(cs, getFromType(), getToType(), locator); + return failure.diagnose(asNote); + } + + default: + return false; + } +} + +AllowNonClassTypeToConvertToAnyObject * +AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, + ConstraintLocator *locator) { + return new (cs.getAllocator()) + AllowNonClassTypeToConvertToAnyObject(cs, type, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 05959c8ddbaf4..086f9d5e3de89 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -235,12 +235,15 @@ enum class FixKind : uint8_t { /// Closure return type has to be explicitly specified because it can't be /// inferred in current context e.g. because it's a multi-statement closure. SpecifyClosureReturnType, - - /// Object literal type coudn't be infered because the module where + + /// Object literal type coudn't be inferred because the module where /// the default type that implements the associated literal protocol /// is declared was not imported. SpecifyObjectLiteralTypeImport, + /// Allow any type (and not just class or class-constrained type) to + /// be convertible to AnyObject. + AllowNonClassTypeToConvertToAnyObject, }; class ConstraintFix { @@ -1655,6 +1658,21 @@ class SpecifyObjectLiteralTypeImport final : public ConstraintFix { }; +class AllowNonClassTypeToConvertToAnyObject final : public ContextualMismatch { + AllowNonClassTypeToConvertToAnyObject(ConstraintSystem &cs, Type type, + ConstraintLocator *locator); + +public: + std::string getName() const { + return "allow non-class type to convert to 'AnyObject'"; + } + + bool diagnose(bool asNote = false) const; + + static AllowNonClassTypeToConvertToAnyObject * + create(ConstraintSystem &cs, Type type, ConstraintLocator *locator); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 43a00626b0e8d..46b71306844d5 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2095,9 +2095,17 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, // Subtype relation to AnyObject also allows class-bound // existentials that are not @objc and therefore carry // witness tables. - if (!type1->isClassExistentialType() && - !type1->mayHaveSuperclass()) + if (!type1->isClassExistentialType() && !type1->mayHaveSuperclass()) { + if (shouldAttemptFixes()) { + auto *fix = AllowNonClassTypeToConvertToAnyObject::create( + *this, type1, getConstraintLocator(locator)); + + return recordFix(fix) ? getTypeMatchFailure(locator) + : getTypeMatchSuccess(); + } + return getTypeMatchFailure(locator); + } } // Keep going. @@ -8837,6 +8845,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowMutatingMemberOnRValueBase: case FixKind::AllowTupleSplatForSingleParameter: case FixKind::AllowInvalidUseOfTrailingClosure: + case FixKind::AllowNonClassTypeToConvertToAnyObject: case FixKind::SpecifyClosureReturnType: llvm_unreachable("handled elsewhere"); } From ec3b783380a984c2ee1531108e08170a85ace793 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 29 Jan 2020 00:37:39 -0800 Subject: [PATCH 026/237] [Diagnostics] Improve diagnostic for invalid conversion to AnyObject --- include/swift/AST/DiagnosticsSema.def | 17 +++++- lib/Sema/CSDiagnostics.cpp | 56 +++++++++++++------ lib/Sema/CSDiagnostics.h | 2 +- test/ClangImporter/attr-swift_private.swift | 2 +- test/Constraints/bridging.swift | 10 ++-- test/Generics/existential_restrictions.swift | 2 +- test/Interpreter/SDK/misc_osx.swift | 2 +- test/Parse/metatype_object_conversion.swift | 7 +-- ...g_metatype_cast_to_reference_no_objc.swift | 4 +- 9 files changed, 68 insertions(+), 34 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index fef7af3b04473..02438f31c3bd5 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -338,6 +338,10 @@ ERROR(cannot_convert_initializer_value,none, "cannot convert value of type %0 to specified type %1", (Type,Type)) ERROR(cannot_convert_initializer_value_protocol,none, "value of type %0 does not conform to specified type %1", (Type,Type)) +ERROR(cannot_convert_initializer_value_anyobject,none, + "value of type %0 expected to be instance of class or " + "class-constrained type", + (Type, Type)) ERROR(cannot_convert_initializer_value_nil,none, "'nil' cannot initialize specified type %0", (Type)) @@ -346,6 +350,10 @@ ERROR(cannot_convert_to_return_type,none, (Type,Type)) ERROR(cannot_convert_to_return_type_protocol,none, "return expression of type %0 does not conform to %1", (Type,Type)) +ERROR(cannot_convert_return_type_to_anyobject,none, + "return expression of type %0 expected to be an instance of " + "a class or class-constrained type", + (Type, Type)) ERROR(cannot_convert_to_return_type_nil,none, "'nil' is incompatible with return type %0", (Type)) @@ -440,7 +448,10 @@ NOTE(candidate_performs_illegal_ephemeral_conv,none, ERROR(cannot_convert_argument_value_protocol,none, "argument type %0 does not conform to expected type %1", (Type, Type)) - +ERROR(cannot_convert_argument_value_anyobject,none, + "argument type %0 expected to be an instance of " + "a class or class-constrained type", + (Type, Type)) ERROR(cannot_convert_argument_value_nil,none, "'nil' is not compatible with expected argument type %0", (Type)) @@ -536,6 +547,10 @@ NOTE(assign_protocol_conformance_fix_it,none, ERROR(cannot_convert_assign_protocol,none, "value of type %0 does not conform to %1 in assignment", (Type, Type)) +ERROR(cannot_convert_assign_anyobject,none, + "value of type %0 expected to be an instance of " + "a class or class-constrained type in assignment", + (Type, Type)) ERROR(cannot_convert_assign_nil,none, "'nil' cannot be assigned to type %0", (Type)) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b8cf4a95a75bb..b85e37691bb76 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1977,7 +1977,7 @@ bool ContextualFailure::diagnoseAsError() { CTP = CTP_ClosureResult; } - if (auto msg = getDiagnosticFor(CTP, toType->isExistentialType())) { + if (auto msg = getDiagnosticFor(CTP, toType)) { diagnostic = *msg; break; } @@ -2276,11 +2276,9 @@ bool ContextualFailure::diagnoseCoercionToUnrelatedType() const { if (auto *coerceExpr = dyn_cast(anchor)) { auto fromType = getType(coerceExpr->getSubExpr()); auto toType = getType(coerceExpr->getCastTypeLoc()); - - auto diagnostic = - getDiagnosticFor(CTP_CoerceOperand, - /*forProtocol=*/toType->isExistentialType()); - + + auto diagnostic = getDiagnosticFor(CTP_CoerceOperand, toType); + auto diag = emitDiagnostic(anchor->getLoc(), *diagnostic, fromType, toType); diag.highlight(anchor->getSourceRange()); @@ -2844,15 +2842,24 @@ bool ContextualFailure::isIntegerToStringIndexConversion() const { Optional> ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, - bool forProtocol) { + Type contextualType) { + auto forProtocol = contextualType->isExistentialType(); switch (context) { - case CTP_Initialization: + case CTP_Initialization: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_initializer_value_anyobject; + return forProtocol ? diag::cannot_convert_initializer_value_protocol : diag::cannot_convert_initializer_value; + } case CTP_ReturnStmt: - case CTP_ReturnSingleExpr: + case CTP_ReturnSingleExpr: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_return_type_to_anyobject; + return forProtocol ? diag::cannot_convert_to_return_type_protocol : diag::cannot_convert_to_return_type; + } case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value; case CTP_DefaultParameter: @@ -2861,9 +2868,13 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_YieldByValue: return forProtocol ? diag::cannot_convert_yield_value_protocol : diag::cannot_convert_yield_value; - case CTP_CallArgument: + case CTP_CallArgument: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_argument_value_anyobject; + return forProtocol ? diag::cannot_convert_argument_value_protocol : diag::cannot_convert_argument_value; + } case CTP_ClosureResult: return forProtocol ? diag::cannot_convert_closure_result_protocol : diag::cannot_convert_closure_result; @@ -2879,9 +2890,13 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_CoerceOperand: return forProtocol ? diag::cannot_convert_coerce_protocol : diag::cannot_convert_coerce; - case CTP_AssignSource: + case CTP_AssignSource: { + if (contextualType->isAnyObject()) + return diag::cannot_convert_assign_anyobject; + return forProtocol ? diag::cannot_convert_assign_protocol : diag::cannot_convert_assign; + } case CTP_SubscriptAssignSource: return forProtocol ? diag::cannot_convert_subscript_assign_protocol : diag::cannot_convert_subscript_assign; @@ -2908,7 +2923,7 @@ bool TupleContextualFailure::diagnoseAsError() { else if ((purpose == CTP_Initialization) && !cs.getContextualType(getAnchor())) diagnostic = diag::tuple_types_not_convertible; - else if (auto diag = getDiagnosticFor(purpose, /*forProtocol=*/false)) + else if (auto diag = getDiagnosticFor(purpose, getToType())) diagnostic = *diag; else return false; @@ -2919,7 +2934,7 @@ bool TupleContextualFailure::diagnoseAsError() { bool FunctionTypeMismatch::diagnoseAsError() { auto purpose = getContextualTypePurpose(); - auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false); + auto diagnostic = getDiagnosticFor(purpose, getToType()); if (!diagnostic) return false; @@ -4839,17 +4854,16 @@ bool MissingContextualConformanceFailure::diagnoseAsError() { if (path.empty()) { assert(isa(anchor)); if (isa(cast(anchor)->getDest())) { - diagnostic = - getDiagnosticFor(CTP_SubscriptAssignSource, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(CTP_SubscriptAssignSource, getToType()); } else { - diagnostic = getDiagnosticFor(CTP_AssignSource, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(CTP_AssignSource, getToType()); } } else { const auto &last = path.back(); switch (last.getKind()) { case ConstraintLocator::ContextualType: assert(Context != CTP_Unused); - diagnostic = getDiagnosticFor(Context, /*forProtocol=*/true); + diagnostic = getDiagnosticFor(Context, getToType()); break; case ConstraintLocator::SequenceElementType: { @@ -5277,7 +5291,7 @@ bool InOutConversionFailure::diagnoseAsError() { assert(locator->findLast()); auto contextualType = cs.getContextualType(anchor); auto purpose = getContextualTypePurpose(); - auto diagnostic = getDiagnosticFor(purpose, /*forProtocol=*/false); + auto diagnostic = getDiagnosticFor(purpose, contextualType); if (!diagnostic) return false; @@ -5385,6 +5399,12 @@ bool ArgumentMismatchFailure::diagnoseAsError() { auto argType = getFromType(); auto paramType = getToType(); + if (paramType->isAnyObject()) { + emitDiagnostic(getLoc(), diag::cannot_convert_argument_value_anyobject, + argType, paramType); + return true; + } + Diag diagnostic = diag::cannot_convert_argument_value; // If parameter type is a protocol value, let's says that diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 90f64c5175acf..386ea94c8831a 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -653,7 +653,7 @@ class ContextualFailure : public FailureDiagnostic { ContextualTypePurpose getContextualTypePurpose() const { return CTP; } static Optional> - getDiagnosticFor(ContextualTypePurpose context, bool forProtocol); + getDiagnosticFor(ContextualTypePurpose context, Type contextualType); }; /// Diagnose errors related to converting function type which diff --git a/test/ClangImporter/attr-swift_private.swift b/test/ClangImporter/attr-swift_private.swift index 05ddc81835f30..f249454b15d96 100644 --- a/test/ClangImporter/attr-swift_private.swift +++ b/test/ClangImporter/attr-swift_private.swift @@ -121,7 +121,7 @@ func testCF(_ a: __PrivCFType, b: __PrivCFSub, c: __PrivInt) { makeSureAnyObject(a) makeSureAnyObject(b) #if !IRGEN - makeSureAnyObject(c) // expected-error {{argument type '__PrivInt' (aka 'Int32') does not conform to expected type 'AnyObject'}} + makeSureAnyObject(c) // expected-error {{argument type '__PrivInt' (aka 'Int32') expected to be an instance of a class or class-constrained type}} #endif } diff --git a/test/Constraints/bridging.swift b/test/Constraints/bridging.swift index 4cdc52e3e2626..c40fb0bdf181d 100644 --- a/test/Constraints/bridging.swift +++ b/test/Constraints/bridging.swift @@ -89,7 +89,7 @@ func bridgeToObjC(_ s: BridgedStruct) -> BridgedClass { } func bridgeToAnyObject(_ s: BridgedStruct) -> AnyObject { - return s // expected-error{{return expression of type 'BridgedStruct' does not conform to 'AnyObject'}} + return s // expected-error{{return expression of type 'BridgedStruct' expected to be an instance of a class or class-constrained type}} return s as AnyObject } @@ -344,14 +344,14 @@ func forceUniversalBridgeToAnyObject(a: T, b: U, c: An z = g as AnyObject z = h as AnyObject - z = a // expected-error{{does not conform to 'AnyObject'}} + z = a // expected-error{{value of type 'T' expected to be an instance of a class or class-constrained type in assignment}} z = b - z = c // expected-error{{does not conform to 'AnyObject'}} expected-note {{cast 'Any' to 'AnyObject'}} {{8-8= as AnyObject}} - z = d // expected-error{{does not conform to 'AnyObject'}} + z = c // expected-error{{value of type 'Any' expected to be an instance of a class or class-constrained type in assignment}} expected-note {{cast 'Any' to 'AnyObject'}} {{8-8= as AnyObject}} + z = d // expected-error{{value of type 'KnownUnbridged' expected to be an instance of a class or class-constrained type in assignment}} z = e z = f z = g - z = h // expected-error{{does not conform to 'AnyObject'}} + z = h // expected-error{{value of type 'String' expected to be an instance of a class or class-constrained type in assignment}} _ = z } diff --git a/test/Generics/existential_restrictions.swift b/test/Generics/existential_restrictions.swift index ae652220daa2b..fbf2f29375fc5 100644 --- a/test/Generics/existential_restrictions.swift +++ b/test/Generics/existential_restrictions.swift @@ -25,7 +25,7 @@ func fT(_ t: T) { } func testPassExistential(_ p: P, op: OP, opp: OP & P, cp: CP, sp: SP, any: Any, ao: AnyObject) { fP(p) // expected-error{{value of protocol type 'P' cannot conform to 'P'; only struct/enum/class types can conform to protocols}} fAO(p) // expected-error{{global function 'fAO' requires that 'P' be a class type}} - fAOE(p) // expected-error{{argument type 'P' does not conform to expected type 'AnyObject'}} + fAOE(p) // expected-error{{argument type 'P' expected to be an instance of a class or class-constrained type}} fT(p) fOP(op) diff --git a/test/Interpreter/SDK/misc_osx.swift b/test/Interpreter/SDK/misc_osx.swift index fabd2413023c3..15383cf784077 100644 --- a/test/Interpreter/SDK/misc_osx.swift +++ b/test/Interpreter/SDK/misc_osx.swift @@ -13,5 +13,5 @@ func testFSEventStreamRef(stream: FSEventStreamRef) { FSEventStreamRetain(stream) // no-warning FSEventStreamRelease(stream) - let _: AnyObject = stream // expected-error {{value of type 'FSEventStreamRef' (aka 'OpaquePointer') does not conform to specified type 'AnyObject'}} + let _: AnyObject = stream // expected-error {{value of type 'FSEventStreamRef' (aka 'OpaquePointer') expected to be instance of class or class-constrained type}} } diff --git a/test/Parse/metatype_object_conversion.swift b/test/Parse/metatype_object_conversion.swift index 96f27402dc86f..2dc87ff1e87bb 100644 --- a/test/Parse/metatype_object_conversion.swift +++ b/test/Parse/metatype_object_conversion.swift @@ -12,15 +12,14 @@ func takesAnyObject(_ x: AnyObject) {} func concreteTypes() { takesAnyObject(C.self) - // TODO: Better error messages - takesAnyObject(S.self) // expected-error{{argument type 'S.Type' does not conform to expected type 'AnyObject'}} - takesAnyObject(ClassConstrainedProto.self) // expected-error{{argument type 'ClassConstrainedProto.Protocol' does not conform to expected type 'AnyObject'}} + takesAnyObject(S.self) // expected-error{{argument type 'S.Type' expected to be an instance of a class or class-constrained type}} + takesAnyObject(ClassConstrainedProto.self) // expected-error{{argument type 'ClassConstrainedProto.Protocol' expected to be an instance of a class or class-constrained type}} } func existentialMetatypes(nonClass: NonClassProto.Type, classConstrained: ClassConstrainedProto.Type, compo: (NonClassProto & ClassConstrainedProto).Type) { - takesAnyObject(nonClass) // expected-error{{argument type 'NonClassProto.Type' does not conform to expected type 'AnyObject'}} + takesAnyObject(nonClass) // expected-error{{argument type 'NonClassProto.Type' expected to be an instance of a class or class-constrained type}} takesAnyObject(classConstrained) takesAnyObject(compo) } diff --git a/test/Sema/diag_metatype_cast_to_reference_no_objc.swift b/test/Sema/diag_metatype_cast_to_reference_no_objc.swift index 28566417e0fbf..abca5b92aa279 100644 --- a/test/Sema/diag_metatype_cast_to_reference_no_objc.swift +++ b/test/Sema/diag_metatype_cast_to_reference_no_objc.swift @@ -3,6 +3,6 @@ class C {} func test(c: AnyClass) { - let _: AnyObject = c // expected-error {{value of type 'AnyClass' (aka 'AnyObject.Type') does not conform to specified type 'AnyObject'}} - let _: AnyObject = C.self // expected-error {{value of type 'C.Type' does not conform to specified type 'AnyObject'}} + let _: AnyObject = c // expected-error {{value of type 'AnyClass' (aka 'AnyObject.Type') expected to be instance of class or class-constrained type}} + let _: AnyObject = C.self // expected-error {{value of type 'C.Type' expected to be instance of class or class-constrained type}} } From ffa6bf47294960fb6aaf26da47c213bbb5a10c9d Mon Sep 17 00:00:00 2001 From: Chris Amanse <12277586+chrisamanse@users.noreply.github.com> Date: Wed, 29 Jan 2020 08:21:52 -0800 Subject: [PATCH 027/237] Fix memory leaks in ThreadBarriers.swift (#12212) * Fix memory leaks in ThreadBarriers.swift * Fatal error on pthread cond/mutex destroy failure * Rename pthread to thread * Fix pthread init function calls * Fix guard statement --- .../SwiftPrivateThreadExtras.swift | 5 +-- .../ThreadBarriers.swift | 36 +++++++++++-------- .../stdlib/StringSlicesConcurrentAppend.swift | 3 +- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift index 002aef8e74145..bc849536a9429 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/SwiftPrivateThreadExtras.swift @@ -162,10 +162,7 @@ public class _stdlib_Barrier { } deinit { - let ret = _stdlib_thread_barrier_destroy(_threadBarrierPtr) - if ret != 0 { - fatalError("_stdlib_thread_barrier_destroy() failed") - } + _stdlib_thread_barrier_destroy(_threadBarrierPtr) } public func wait() { diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 9f5e183447021..6d79bd124e976 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -69,13 +69,12 @@ public func _stdlib_thread_barrier_init( InitializeConditionVariable(barrier.pointee.cond!) #else barrier.pointee.mutex = UnsafeMutablePointer.allocate(capacity: 1) - if pthread_mutex_init(barrier.pointee.mutex!, nil) != 0 { - // FIXME: leaking memory. - return -1 - } barrier.pointee.cond = UnsafeMutablePointer.allocate(capacity: 1) - if pthread_cond_init(barrier.pointee.cond!, nil) != 0 { - // FIXME: leaking memory, leaking a mutex. + guard _stdlib_thread_barrier_mutex_and_cond_init(barrier) == 0 else { + barrier.pointee.mutex!.deinitialize(count: 1) + barrier.pointee.mutex!.deallocate() + barrier.pointee.cond!.deinitialize(count: 1) + barrier.pointee.cond!.deallocate() return -1 } #endif @@ -83,20 +82,27 @@ public func _stdlib_thread_barrier_init( return 0 } +private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t>) -> CInt { + guard pthread_mutex_init(barrier.pointee.mutex!, nil) == 0 else { + return -1 + } + guard pthread_cond_init(barrier.pointee.cond!, nil) == 0 else { + pthread_mutex_destroy(barrier.pointee.mutex!) + return -1 + } + return 0 +} + public func _stdlib_thread_barrier_destroy( _ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t> -) -> CInt { +) { #if os(Windows) // Condition Variables do not need to be explicitly destroyed // Mutexes do not need to be explicitly destroyed #else - if pthread_cond_destroy(barrier.pointee.cond!) != 0 { - // FIXME: leaking memory, leaking a mutex. - return -1 - } - if pthread_mutex_destroy(barrier.pointee.mutex!) != 0 { - // FIXME: leaking memory. - return -1 + guard pthread_cond_destroy(barrier.pointee.cond!) == 0 && + pthread_mutex_destroy(barrier.pointee.mutex!) == 0 else { + fatalError("_stdlib_thread_barrier_destroy() failed") } #endif barrier.pointee.cond!.deinitialize(count: 1) @@ -105,7 +111,7 @@ public func _stdlib_thread_barrier_destroy( barrier.pointee.mutex!.deinitialize(count: 1) barrier.pointee.mutex!.deallocate() - return 0 + return } public func _stdlib_thread_barrier_wait( diff --git a/validation-test/stdlib/StringSlicesConcurrentAppend.swift b/validation-test/stdlib/StringSlicesConcurrentAppend.swift index 64441e7a184ea..942449e9dfdc8 100644 --- a/validation-test/stdlib/StringSlicesConcurrentAppend.swift +++ b/validation-test/stdlib/StringSlicesConcurrentAppend.swift @@ -111,8 +111,7 @@ StringTestSuite.test("SliceConcurrentAppend") { expectEqual(0, joinRet1) expectEqual(0, joinRet2) - ret = _stdlib_thread_barrier_destroy(barrierVar!) - expectEqual(0, ret) + _stdlib_thread_barrier_destroy(barrierVar!) barrierVar!.deinitialize(count: 1) barrierVar!.deallocate() From 7351bfc86fc18975e97f9a29dddb4471efc73de5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 3 Jan 2020 16:24:11 -0800 Subject: [PATCH 028/237] [CSFix] Add a fix to add a missing qualifier to shadowed top-level name references --- lib/Sema/CSFix.cpp | 10 ++++++++++ lib/Sema/CSFix.h | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 95e78c3a454be..b8aa2f7fa9a7f 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1217,3 +1217,13 @@ AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, return new (cs.getAllocator()) AllowNonClassTypeToConvertToAnyObject(cs, type, locator); } + +bool AddQualifierToAccessTopLevelName::diagnose(bool asNote) const { + return false; +} + +AddQualifierToAccessTopLevelName * +AddQualifierToAccessTopLevelName::create(ConstraintSystem &cs, + ConstraintLocator *locator) { + return new (cs.getAllocator()) AddQualifierToAccessTopLevelName(cs, locator); +} diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h index 086f9d5e3de89..d4c453070228d 100644 --- a/lib/Sema/CSFix.h +++ b/lib/Sema/CSFix.h @@ -244,6 +244,10 @@ enum class FixKind : uint8_t { /// Allow any type (and not just class or class-constrained type) to /// be convertible to AnyObject. AllowNonClassTypeToConvertToAnyObject, + + /// Member shadows a top-level name, such a name could only be accessed by + /// prefixing it with a module name. + AddQualifierToAccessTopLevelName, }; class ConstraintFix { @@ -1655,7 +1659,22 @@ class SpecifyObjectLiteralTypeImport final : public ConstraintFix { static SpecifyObjectLiteralTypeImport *create(ConstraintSystem &cs, ConstraintLocator *locator); +}; + +class AddQualifierToAccessTopLevelName final : public ConstraintFix { + AddQualifierToAccessTopLevelName(ConstraintSystem &cs, + ConstraintLocator *locator) + : ConstraintFix(cs, FixKind::AddQualifierToAccessTopLevelName, locator) {} + +public: + std::string getName() const { + return "qualify reference to access top-level function"; + } + bool diagnose(bool asNote = false) const; + + static AddQualifierToAccessTopLevelName *create(ConstraintSystem &cs, + ConstraintLocator *locator); }; class AllowNonClassTypeToConvertToAnyObject final : public ContextualMismatch { From c9c20afe27893bc7b1c607852abd540ebb5c8205 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 3 Jan 2020 20:31:17 -0800 Subject: [PATCH 029/237] [Diagnostics] Port name shadowing diagnostics Diagnose an attempt to reference a top-level name shadowed by a local member e.g. ```swift extension Sequence { func test() -> Int { return max(1, 2) } } ``` Here `min` refers to a global function `min(_: T, _: T)` in `Swift` module and can only be accessed by adding `Swift.` to it, because `Sequence` has a member named `min` which accepts a single argument. --- include/swift/AST/DiagnosticsSema.def | 12 ++++---- lib/Sema/CSDiagnostics.cpp | 42 +++++++++++++++++++++++++++ lib/Sema/CSDiagnostics.h | 23 +++++++++++++++ lib/Sema/CSFix.cpp | 4 ++- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 02438f31c3bd5..3d46cd38c1d43 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1153,14 +1153,12 @@ NOTE(candidate_expected_different_labels,none, "incorrect labels for candidate (have: '%0', expected: '%1')", (StringRef, StringRef)) +ERROR(member_shadows_function,none, + "use of %0 refers to %1 rather than %2 %3", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName)) ERROR(member_shadows_global_function,none, - "use of %0 refers to %1 %2 rather than %3 %4 in %5 %6", - (DeclNameRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, - DeclName, DescriptiveDeclKind, DeclName)) -ERROR(member_shadows_global_function_near_match,none, - "use of %0 nearly matches %3 %4 in %5 %6 rather than %1 %2", - (DeclNameRef, DescriptiveDeclKind, DeclName, DescriptiveDeclKind, - DeclName, DescriptiveDeclKind, DeclName)) + "use of %0 refers to %1 rather than %2 %3 in module %4", + (DeclNameRef, DescriptiveDeclKind, DescriptiveDeclKind, DeclName, DeclName)) ERROR(instance_member_use_on_type,none, "instance member %1 cannot be used on type %0; " diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b85e37691bb76..f16fb16bdbe17 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6137,3 +6137,45 @@ bool UnableToInferProtocolLiteralType::diagnoseAsError() { return true; } + +bool MissingQuialifierInMemberRefFailure::diagnoseAsError() { + auto selectedOverload = getOverloadChoiceIfAvailable(getLocator()); + if (!selectedOverload) + return false; + + auto *UDE = cast(getRawAnchor()); + + auto baseType = getType(UDE->getBase()); + + auto methodKind = baseType->isAnyExistentialType() + ? DescriptiveDeclKind::StaticMethod + : DescriptiveDeclKind::Method; + + auto choice = selectedOverload->choice.getDeclOrNull(); + if (!choice) + return false; + + auto *DC = choice->getDeclContext(); + if (!(DC->isModuleContext() || DC->isModuleScopeContext())) { + emitDiagnostic(UDE->getLoc(), diag::member_shadows_function, UDE->getName(), + methodKind, choice->getDescriptiveKind(), + choice->getFullName()); + return true; + } + + auto qualifier = DC->getParentModule()->getName(); + + emitDiagnostic(UDE->getLoc(), diag::member_shadows_global_function, + UDE->getName(), methodKind, choice->getDescriptiveKind(), + choice->getFullName(), qualifier); + + SmallString<32> namePlusDot = qualifier.str(); + namePlusDot.push_back('.'); + + emitDiagnostic(UDE->getLoc(), diag::fix_unqualified_access_top_level_multi, + namePlusDot, choice->getDescriptiveKind(), qualifier) + .fixItInsert(UDE->getStartLoc(), namePlusDot); + + emitDiagnostic(choice, diag::decl_declared_here, choice->getFullName()); + return true; +} diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 386ea94c8831a..67783a9fa283d 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1936,6 +1936,29 @@ class UnableToInferProtocolLiteralType final : public FailureDiagnostic { bool diagnoseAsError(); }; +/// Diagnose an attempt to reference a top-level name shadowed by a local +/// member e.g. +/// +/// ```swift +/// extension Sequence { +/// func test() -> Int { +/// return max(1, 2) +/// } +/// } +/// ``` +/// +/// Here `min` refers to a global function `min(_: T, _: T)` in `Swift` +/// module and can only be accessed by adding `Swift.` to it, because `Sequence` +/// has a member named `min` which accepts a single argument. +class MissingQuialifierInMemberRefFailure final : public FailureDiagnostic { +public: + MissingQuialifierInMemberRefFailure(ConstraintSystem &cs, + ConstraintLocator *locator) + : FailureDiagnostic(cs, locator) {} + + bool diagnoseAsError(); +}; + } // end namespace constraints } // end namespace swift diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index b8aa2f7fa9a7f..2eecf54297e86 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -1219,7 +1219,9 @@ AllowNonClassTypeToConvertToAnyObject::create(ConstraintSystem &cs, Type type, } bool AddQualifierToAccessTopLevelName::diagnose(bool asNote) const { - return false; + auto &cs = getConstraintSystem(); + MissingQuialifierInMemberRefFailure failure(cs, getLocator()); + return failure.diagnose(asNote); } AddQualifierToAccessTopLevelName * From 78fda9ed98830a235ecfe93ff14e949ae3d86200 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 08:57:01 -0800 Subject: [PATCH 030/237] [ConstraintSystem] Use new fix/diagnostic for name shadowing Stop filtering outer overload choices while trying to pre-check expression, instead have it always fetch those and use new fix to only attempt them in diagnostic mode (unless it's min/max situation with conditional conformances). --- lib/Sema/CSSimplify.cpp | 55 ++++++++++++- lib/Sema/TypeCheckConstraints.cpp | 77 +++---------------- test/APINotes/versioned-objc.swift | 2 +- test/Constraints/members.swift | 10 +-- ...okup_min_max_conditional_conformance.swift | 14 ++-- test/Sema/circular_decl_checking.swift | 5 +- 6 files changed, 77 insertions(+), 86 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 870eae283b2f5..ed92d05fa9fed 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3273,6 +3273,30 @@ bool ConstraintSystem::repairFailures( locator)) break; + { + auto *calleeLocator = getCalleeLocator(loc); + if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) { + if (auto overload = findSelectedOverloadFor(calleeLocator)) { + if (auto choice = overload->choice.getDeclOrNull()) { + // If this is an argument of a symetric function/operator let's + // not fix any position rather than first because we'd just end + // up with ambiguity instead of reporting an actual problem with + // mismatched type since each argument can have district bindings. + if (auto *AFD = dyn_cast(choice)) { + auto *paramList = AFD->getParameters(); + auto firstParamType = paramList->get(0)->getInterfaceType(); + if (elt.castTo().getParamIdx() > + 0 && + llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool { + return param->getInterfaceType()->isEqual(firstParamType); + })) + return true; + } + } + } + } + } + conversionsOrFixes.push_back( AllowArgumentMismatch::create(*this, lhs, rhs, loc)); break; @@ -6201,8 +6225,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint( if (candidates.size() == 1) candidates.front()->setFavored(); - generateConstraints(candidates, memberTy, outerAlternatives, - useDC, locator); + // We *might* include any non-members that we found in outer contexts in + // some special cases, for backwards compatibility: first, we have to be + // looking for one of the special names ('min' or 'max'), and second, all + // of the inner (viable) results need to come from conditional + // conformances. The second condition is how the problem here was + // encountered: a type ('Range') was made to conditionally conform to a + // new protocol ('Sequence'), which introduced some extra methods + // ('min' and 'max') that shadowed global functions that people regularly + // called within extensions to that type (usually adding 'clamp'). + bool treatAsViable = + (member.isSimpleName("min") || member.isSimpleName("max")) && + allFromConditionalConformances(DC, baseTy, result.ViableCandidates); + + generateConstraints( + candidates, memberTy, outerAlternatives, useDC, locator, None, + /*requiresFix=*/!treatAsViable, + [&](unsigned, const OverloadChoice &) { + return treatAsViable ? nullptr + : AddQualifierToAccessTopLevelName::create( + *this, locator); + }); } } @@ -8852,6 +8895,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( case FixKind::AllowInvalidUseOfTrailingClosure: case FixKind::AllowNonClassTypeToConvertToAnyObject: case FixKind::SpecifyClosureReturnType: + case FixKind::AddQualifierToAccessTopLevelName: llvm_unreachable("handled elsewhere"); } @@ -9315,7 +9359,12 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { case ConstraintKind::BindOverload: if (auto *fix = constraint.getFix()) { - if (recordFix(fix)) + // TODO(diagnostics): Impact should be associated with a fix unless + // it's a contextual problem, then only solver can decide what the impact + // would be in each particular situation. + auto impact = + fix->getKind() == FixKind::AddQualifierToAccessTopLevelName ? 10 : 1; + if (recordFix(fix, impact)) return SolutionKind::Error; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index de6d54af3adce..7e5b6714d6de0 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -452,20 +452,6 @@ static bool findNonMembers(ArrayRef lookupResults, return AllDeclRefs; } -/// Whether we should be looking at the outer results for a function called \c -/// name. -/// -/// This is very restrictive because it's a source compatibility issue (see the -/// if (AllConditionalConformances) { (void)findNonMembers(...); } below). -static bool shouldConsiderOuterResultsFor(DeclNameRef name) { - const StringRef specialNames[] = {"min", "max"}; - for (auto specialName : specialNames) - if (name.isSimpleName(specialName)) - return true; - - return false; -} - /// Bind an UnresolvedDeclRefExpr by performing name lookup and /// returning the resultant expression. Context is the DeclContext used /// for the lookup. @@ -479,8 +465,11 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, NameLookupOptions lookupOptions = defaultUnqualifiedLookupOptions; if (isa(DC)) lookupOptions |= NameLookupFlags::KnownPrivate; - if (shouldConsiderOuterResultsFor(Name)) - lookupOptions |= NameLookupFlags::IncludeOuterResults; + + // TODO: Include all of the possible members to give a solver a + // chance to diagnose name shadowing which requires explicit + // name/module qualifier to access top-level name. + lookupOptions |= NameLookupFlags::IncludeOuterResults; auto Lookup = TypeChecker::lookupUnqualified(DC, Name, Loc, lookupOptions); @@ -625,14 +614,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, // better matching candidates. if (localDeclAfterUse) { auto innerDecl = localDeclAfterUse; - - // Perform a thorough lookup if outer results was not included before. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - auto option = lookupOptions; - option |= NameLookupFlags::IncludeOuterResults; - Lookup = lookupUnqualified(DC, Name, Loc, option); - } - while (localDeclAfterUse) { if (Lookup.outerResults().empty()) { Context.Diags.diagnose(Loc, diag::use_local_before_declaration, Name); @@ -649,13 +630,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, findNonMembers(Lookup.innerResults(), UDRE->getRefKind(), /*breakOnMember=*/true, ResultValues, isValid); } - - // Drop outer results if they are not supposed to be included. - if (!lookupOptions.contains(NameLookupFlags::IncludeOuterResults)) { - Lookup.filter([&](LookupResultEntry Result, bool isOuter) { - return !isOuter; - }); - } } // If we have an unambiguous reference to a type decl, form a TypeExpr. @@ -715,7 +689,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, ResultValues.clear(); bool AllMemberRefs = true; - bool AllConditionalConformances = true; ValueDecl *Base = nullptr; DeclContext *BaseDC = nullptr; for (auto Result : Lookup) { @@ -732,26 +705,6 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, Base = ThisBase; BaseDC = Result.getDeclContext(); - - // Check if this result is derived through a conditional conformance, - // meaning it comes from a protocol (or extension) where there's a - // conditional conformance for the type with the method in question - // (NB. that type may not be the type associated with DC, for tested types - // with static methods). - if (auto Proto = Value->getDeclContext()->getSelfProtocolDecl()) { - auto contextSelfType = - BaseDC->getInnermostTypeContext()->getDeclaredInterfaceType(); - auto conformance = conformsToProtocol( - contextSelfType, Proto, DC, - ConformanceCheckFlags::InExpression | - ConformanceCheckFlags::SkipConditionalRequirements); - - if (conformance.isInvalid() || - conformance.getConditionalRequirements().empty()) { - AllConditionalConformances = false; - } - } - continue; } @@ -774,22 +727,12 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, /*Implicit=*/true); } - // We *might* include any non-members that we found in outer contexts in - // some special cases, for backwards compatibility: first, we have to be - // looking for one of the special names - // ('shouldConsiderOuterResultsFor(Name)'), and second, all of the inner - // results need to come from conditional conformances. The second condition - // is how the problem here was encountered: a type ('Range') was made to - // conditionally conform to a new protocol ('Sequence'), which introduced - // some extra methods ('min' and 'max') that shadowed global functions that - // people regularly called within extensions to that type (usually adding - // 'clamp'). llvm::SmallVector outerAlternatives; - if (AllConditionalConformances) { - (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), - /*breakOnMember=*/false, outerAlternatives, - /*isValid=*/[&](ValueDecl *) { return true; }); - } + (void)findNonMembers(Lookup.outerResults(), UDRE->getRefKind(), + /*breakOnMember=*/false, outerAlternatives, + /*isValid=*/[](ValueDecl *choice) -> bool { + return !choice->isInvalid(); + }); // Otherwise, form an UnresolvedDotExpr and sema will resolve it based on // type information. diff --git a/test/APINotes/versioned-objc.swift b/test/APINotes/versioned-objc.swift index b2b5cc76ea6c9..9f9b2b64611cd 100644 --- a/test/APINotes/versioned-objc.swift +++ b/test/APINotes/versioned-objc.swift @@ -166,7 +166,7 @@ extension PrintingInterference { func testDroppingRenamedPrints() { // CHECK-DIAGS-4: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method print(self) - // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: missing argument for parameter 'extra' in call + // CHECK-DIAGS-5: [[@LINE-1]]:{{[0-9]+}}: error: use of 'print' refers to instance method rather than global function 'print(_:separator:terminator:)' in module 'Swift' // CHECK-DIAGS-4-NOT: [[@LINE+1]]:{{[0-9]+}}: print(self, extra: self) diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index c2de930b8b443..1720948609d3c 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -355,7 +355,7 @@ do { // rdar://problem/25341015 extension Sequence { func r25341015_1() -> Int { - return max(1, 2) // expected-error {{use of 'max' refers to instance method 'max(by:)' rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} + return max(1, 2) // expected-error {{use of 'max' refers to instance method rather than global function 'max' in module 'Swift'}} expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} } } @@ -381,7 +381,7 @@ func r25341015() { class Bar { func baz() {} func qux() { - baz(1, 2) // expected-error {{argument passed to call that takes no arguments}} + baz(1, 2) // expected-error {{use of 'baz' refers to instance method rather than local function 'baz'}} } } } @@ -405,17 +405,17 @@ func bar_32854314() -> Int { extension Array where Element == Int { func foo() { let _ = min(foo_32854314(), bar_32854314()) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func foo(_ x: Int, _ y: Double) { let _ = min(x, y) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } func bar() { let _ = min(1.0, 2) // expected-note {{use 'Swift.' to reference the global function in module 'Swift'}} {{13-13=Swift.}} - // expected-error@-1 {{use of 'min' nearly matches global function 'min' in module 'Swift' rather than instance method 'min(by:)'}} + // expected-error@-1 {{use of 'min' refers to instance method rather than global function 'min' in module 'Swift'}} } } diff --git a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift index 81d2e59582cfa..e79212e6aa318 100644 --- a/test/NameBinding/name_lookup_min_max_conditional_conformance.swift +++ b/test/NameBinding/name_lookup_min_max_conditional_conformance.swift @@ -16,7 +16,7 @@ extension ContainsMinMax { func min() {} } -func foo(_: Int, _: Int) {} +func foo(_: Int, _: Int) {} // expected-note 2 {{'foo' declared here}} protocol ContainsFoo {} extension ContainsFoo { @@ -34,15 +34,14 @@ extension NonConditional { // expected-error@-1{{use of 'min' refers to instance method}} // expected-note@-2{{use 'Swift.' to reference the global function}} - // FIXME(diagnostics): Better diagnostic in this case would be to suggest to add `name_lookup_min_max_conditional_conformance.` - // to call because name `foo` is shadowed by instance method without arguments. Would be fixed by `resolveDeclRefExpr` refactoring. - _ = foo(5, 6) // expected-error {{argument passed to call that takes no arguments}} + _ = foo(5, 6) // expected-error {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-1 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } struct Conditional {} extension Conditional: ContainsMinMax where T: ContainsMinMax {} -extension Conditional: ContainsFoo where T: ContainsFoo {} // expected-note {{requirement from conditional conformance of 'Conditional' to 'ContainsFoo'}} +extension Conditional: ContainsFoo where T: ContainsFoo {} extension Conditional { func f() { @@ -53,9 +52,8 @@ extension Conditional { // expected-warning@-1{{use of 'min' as reference to global function in module 'Swift' will change in future versions of Swift to reference instance method in generic struct 'Conditional' which comes via a conditional conformance}} // expected-note@-2{{use 'Swift.' to continue to reference the global function}} - // FIXME(diagnostics): Same as line 39, there should be only one error here about shadowing. _ = foo(5, 6) - // expected-error@-1 {{referencing instance method 'foo()' on 'Conditional' requires that 'T' conform to 'ContainsFoo'}} - // expected-error@-2 {{argument passed to call that takes no arguments}} + // expected-error@-1 {{use of 'foo' refers to instance method rather than global function 'foo' in module 'name_lookup_min_max_conditional_conformance'}} + // expected-note@-2 {{use 'name_lookup_min_max_conditional_conformance.' to reference the global function in module 'name_lookup_min_max_conditional_conformance'}} {{13-13=name_lookup_min_max_conditional_conformance.}} } } diff --git a/test/Sema/circular_decl_checking.swift b/test/Sema/circular_decl_checking.swift index acb78f0520803..3e8d6e2a9a542 100644 --- a/test/Sema/circular_decl_checking.swift +++ b/test/Sema/circular_decl_checking.swift @@ -23,9 +23,10 @@ class HasGenericFunc { } } -class HasProp { +class HasProp { // expected-note {{'HasProp' declared here}} var HasProp: HasProp { - return HasProp() // expected-error {{cannot call value of non-function type 'HasProp'}}{{19-21=}} + return HasProp() // expected-error {{use of 'HasProp' refers to instance method rather than class 'HasProp' in module 'circular_decl_checking'}} + // expected-note@-1 {{use 'circular_decl_checking.' to reference the class in module 'circular_decl_checking'}} {{12-12=circular_decl_checking.}} } var SomethingElse: SomethingElse? { // expected-error {{use of undeclared type 'SomethingElse'}} return nil From ec95397a76341957cf190e6fd877420cc4c62eb5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 10:02:44 -0800 Subject: [PATCH 031/237] [CSDiag] NFC: Remove obsolete name shadowing diagnostics --- lib/Sema/CSDiag.cpp | 225 -------------------------------------------- 1 file changed, 225 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 1910e242ce091..5e755aad3e72a 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -201,10 +201,6 @@ class FailureDiagnosis :public ASTVisitor{ ContextualTypePurpose CTP, Type suggestedType = Type()); - bool diagnoseImplicitSelfErrors(Expr *fnExpr, Expr *argExpr, - CalleeCandidateInfo &CCI, - ArrayRef argLabels); - private: /// Validate potential contextual type for type-checking one of the /// sub-expressions, usually correct/valid types are the ones which @@ -964,223 +960,6 @@ decomposeArgType(Type argType, ArrayRef argLabels) { return result; } -bool FailureDiagnosis::diagnoseImplicitSelfErrors( - Expr *fnExpr, Expr *argExpr, CalleeCandidateInfo &CCI, - ArrayRef argLabels) { - // If candidate list is empty it means that problem is somewhere else, - // since we need to have candidates which might be shadowing other funcs. - if (CCI.empty() || !CCI[0].getDecl()) - return false; - - auto &ctx = CS.getASTContext(); - // Call expression is formed as 'foo.bar' where 'foo' might be an - // implicit "Self" reference, such use wouldn't provide good diagnostics - // for situations where instance members have equal names to functions in - // Swift Standard Library e.g. min/max. - auto UDE = dyn_cast(fnExpr); - if (!UDE) - return false; - - auto baseExpr = dyn_cast(UDE->getBase()); - if (!baseExpr) - return false; - - auto baseDecl = baseExpr->getDecl(); - if (!baseExpr->isImplicit() || baseDecl->getFullName() != ctx.Id_self) - return false; - - // Our base expression is an implicit 'self.' reference e.g. - // - // extension Sequence { - // func test() -> Int { - // return max(1, 2) - // } - // } - // - // In this example the Sequence class already has two methods named 'max' - // none of which accept two arguments, but there is a function in - // Swift Standard Library called 'max' which does accept two arguments, - // so user might have called that by mistake without realizing that - // compiler would add implicit 'self.' prefix to the call of 'max'. - auto argType = CS.getType(argExpr); - // If argument wasn't properly type-checked, let's retry without changing AST. - if (!argType || argType->hasUnresolvedType() || argType->hasTypeVariable() || - argType->hasTypeParameter()) { - auto *argTuple = dyn_cast(argExpr); - if (!argTuple) { - // Bail out if we don't have a well-formed argument list. - return false; - } - - // Let's type check individual argument expressions without any - // contextual information to try to recover an argument type that - // matches what the user actually wrote instead of what the typechecker - // expects. - SmallVector elts; - for (unsigned i = 0, e = argTuple->getNumElements(); i < e; ++i) { - ConcreteDeclRef ref = nullptr; - auto *el = argTuple->getElement(i); - auto typeResult = - TypeChecker::getTypeOfExpressionWithoutApplying(el, CS.DC, ref); - if (!typeResult) - return false; - auto flags = ParameterTypeFlags().withInOut(typeResult->is()); - elts.push_back(TupleTypeElt(typeResult->getInOutObjectType(), - argTuple->getElementName(i), - flags)); - } - - argType = TupleType::get(elts, CS.getASTContext()); - } - - auto typeKind = argType->getKind(); - if (typeKind != TypeKind::Tuple && typeKind != TypeKind::Paren) - return false; - - // If argument type couldn't be properly resolved or has errors, - // we can't diagnose anything in here, it points to the different problem. - if (isUnresolvedOrTypeVarType(argType) || argType->hasError()) - return false; - - auto context = CS.DC; - using CandidateMap = - llvm::SmallDenseMap>; - - auto getBaseKind = [](ValueDecl *base) -> DescriptiveDeclKind { - DescriptiveDeclKind kind = DescriptiveDeclKind::Module; - if (!base) - return kind; - - auto context = base->getDeclContext(); - do { - if (isa(context)) - return DescriptiveDeclKind::Extension; - - if (auto nominal = dyn_cast(context)) { - kind = nominal->getDescriptiveKind(); - break; - } - - context = context->getParent(); - } while (context); - - return kind; - }; - - auto diagnoseShadowing = [&](ValueDecl *base, - ArrayRef candidates) -> bool { - CalleeCandidateInfo calleeInfo(base ? base->getInterfaceType() : nullptr, - candidates, CCI.hasTrailingClosure, CS, - base); - - calleeInfo.filterListArgs(decomposeArgType(argType, argLabels)); - - auto diagnostic = diag::member_shadows_global_function_near_match; - switch (calleeInfo.closeness) { - case CC_Unavailable: - case CC_Inaccessible: - case CC_SelfMismatch: - case CC_ArgumentLabelMismatch: - case CC_ArgumentCountMismatch: - case CC_GeneralMismatch: - return false; - - case CC_NonLValueInOut: - case CC_OneArgumentNearMismatch: - case CC_OneArgumentMismatch: - case CC_OneGenericArgumentNearMismatch: - case CC_OneGenericArgumentMismatch: - case CC_ArgumentNearMismatch: - case CC_ArgumentMismatch: - case CC_GenericNonsubstitutableMismatch: - break; // Near match cases - - case CC_ExactMatch: - diagnostic = diag::member_shadows_global_function; - break; - } - - auto choice = calleeInfo.candidates[0].getDecl(); - auto baseKind = getBaseKind(base); - auto baseName = getBaseName(choice->getDeclContext()); - - auto origCandidate = CCI[0].getDecl(); - ctx.Diags.diagnose(UDE->getLoc(), diagnostic, UDE->getName(), - origCandidate->getDescriptiveKind(), - origCandidate->getFullName(), - choice->getDescriptiveKind(), - choice->getFullName(), baseKind, baseName); - - auto topLevelDiag = diag::fix_unqualified_access_top_level; - if (baseKind == DescriptiveDeclKind::Module) - topLevelDiag = diag::fix_unqualified_access_top_level_multi; - - emitFixItForExplicitlyQualifiedReference(ctx.Diags, UDE, topLevelDiag, - baseName, - choice->getDescriptiveKind()); - - for (auto &candidate : calleeInfo.candidates) { - if (auto decl = candidate.getDecl()) - ctx.Diags.diagnose(decl, diag::decl_declared_here, decl->getFullName()); - } - - return true; - }; - - // For each of the parent contexts, let's try to find any candidates - // which have the same name and the same number of arguments as callee. - while (context->getParent()) { - auto result = - TypeChecker::lookupUnqualified(context, UDE->getName(), UDE->getLoc()); - context = context->getParent(); - - if (!result || result.empty()) - continue; - - CandidateMap candidates; - for (const auto &candidate : result) { - auto base = candidate.getBaseDecl(); - auto decl = candidate.getValueDecl(); - if ((base && base->isInvalid()) || decl->isInvalid()) - continue; - - // If base is present but it doesn't represent a valid nominal, - // we can't use current candidate as one of the choices. - if (base && !base->getInterfaceType()->getNominalOrBoundGenericNominal()) - continue; - - auto context = decl->getDeclContext(); - // We are only interested in static or global functions, because - // there is no way to call anything else properly. - if (!decl->isStatic() && !context->isModuleScopeContext()) - continue; - - OverloadChoice choice(base ? base->getInterfaceType() : nullptr, - decl, UDE->getFunctionRefKind()); - - if (base) { // Let's group all of the candidates have a common base. - candidates[base].push_back(choice); - continue; - } - - // If there is no base, it means this is one of the global functions, - // let's try to diagnose its shadowing inline. - if (diagnoseShadowing(base, choice)) - return true; - } - - if (candidates.empty()) - continue; - - for (const auto &candidate : candidates) { - if (diagnoseShadowing(candidate.getFirst(), candidate.getSecond())) - return true; - } - } - - return false; -} - // Extract expression for failed argument number static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { if (auto *TE = dyn_cast(argExpr)) @@ -1202,10 +981,6 @@ static Expr *getFailedArgumentExpr(CalleeCandidateInfo CCI, Expr *argExpr) { bool FailureDiagnosis::diagnoseParameterErrors(CalleeCandidateInfo &CCI, Expr *fnExpr, Expr *argExpr, ArrayRef argLabels) { - // Try to diagnose errors related to the use of implicit self reference. - if (diagnoseImplicitSelfErrors(fnExpr, argExpr, CCI, argLabels)) - return true; - // If we have a failure where the candidate set differs on exactly one // argument, and where we have a consistent mismatch across the candidate set // (often because there is only one candidate in the set), then diagnose this From 217c343eb6dd11809beb56f49a91c39dfd9de4b5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 11:54:11 -0800 Subject: [PATCH 032/237] [Diagnostics] NFC: Fix name shadowing diagnostic comment to refer to `max` --- lib/Sema/CSDiagnostics.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 67783a9fa283d..2f3c878a40777 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -1947,9 +1947,9 @@ class UnableToInferProtocolLiteralType final : public FailureDiagnostic { /// } /// ``` /// -/// Here `min` refers to a global function `min(_: T, _: T)` in `Swift` +/// Here `max` refers to a global function `max(_: T, _: T)` in `Swift` /// module and can only be accessed by adding `Swift.` to it, because `Sequence` -/// has a member named `min` which accepts a single argument. +/// has a member named `max` which accepts a single argument. class MissingQuialifierInMemberRefFailure final : public FailureDiagnostic { public: MissingQuialifierInMemberRefFailure(ConstraintSystem &cs, From 93c23211062cdee54cf0471ca5c04f74724e06c4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 6 Jan 2020 19:49:57 -0800 Subject: [PATCH 033/237] [TypeChecker] Add types nested in protocol to lookup results Although such functionality is not yet supported we have to mirror AST lookup and add such members into results, otherwise there is a risk of inner/outer results mismatch. --- lib/Sema/TypeCheckNameLookup.cpp | 6 ++++++ test/decl/nested/protocol.swift | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 7ad524f45df3f..707d454122c5e 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -195,6 +195,12 @@ namespace { addResult(found); return; } + } else if (isa(found)) { + // Declaring nested types inside other types is currently + // not supported by lookup would still return such members + // so we have to account for that here as well. + addResult(found); + return; } // FIXME: the "isa()" check will be wrong for diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index ea5c973467b10..6d25970753ba7 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -19,7 +19,7 @@ class OuterGenericClass { } } -protocol OuterProtocol { // expected-note{{'OuterProtocol' declared here}} +protocol OuterProtocol { associatedtype Hen protocol InnerProtocol { // expected-error{{protocol 'InnerProtocol' cannot be nested inside another declaration}} associatedtype Rooster @@ -32,7 +32,7 @@ struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int func f() { let _ = InnerProtocol.self } - // expected-error@-1 {{use of unresolved identifier 'InnerProtocol'}} + // expected-error@-1 {{protocol 'InnerProtocol' can only be used as a generic constraint because it has Self or associated type requirements}} } protocol Racoon { From 4bd1ffc67f36a14254dc4b3fdb472f766d264f7e Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 27 Jan 2020 16:27:14 -0800 Subject: [PATCH 034/237] Serialize whether a VarDecl is a top-level global. The current way that VarDecl::isLazilyInitializedGlobal() is implemented does not work in the debugger, since the DeclContext of all VarDecls are deserialized Swift modules. By adding a bit to the VarDecl we can recover the fact that a VarDecl was in fact a global even in the debugger. --- include/swift/AST/Decl.h | 11 +++++++++-- lib/AST/Decl.cpp | 7 ++----- lib/Parse/ParseDecl.cpp | 1 + lib/Serialization/Deserialization.cpp | 3 +++ lib/Serialization/ModuleFormat.h | 1 + lib/Serialization/Serialization.cpp | 1 + 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 789de8f2df163..b2727cd5572fb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -335,7 +335,7 @@ class alignas(1 << DeclAlignInBits) Decl { IsStatic : 1 ); - SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1, + SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1+1+1, /// Encodes whether this is a 'let' binding. Introducer : 1, @@ -358,7 +358,10 @@ class alignas(1 << DeclAlignInBits) Decl { IsLazyStorageProperty : 1, /// Whether this is the backing storage for a property wrapper. - IsPropertyWrapperBackingProperty : 1 + IsPropertyWrapperBackingProperty : 1, + + /// Whether this is a lazily top-level global variable from the main file. + IsTopLevelGlobal : 1 ); SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits, @@ -5084,6 +5087,10 @@ class VarDecl : public AbstractStorageDecl { Bits.VarDecl.IsLazyStorageProperty = IsLazyStorage; } + /// True if this is a top-level global variable from the main source file. + bool isTopLevelGlobal() const { return Bits.VarDecl.IsTopLevelGlobal; } + void setTopLevelGlobal(bool b) { Bits.VarDecl.IsTopLevelGlobal = b; } + /// Retrieve the custom attributes that attach property wrappers to this /// property. The returned list contains all of the attached property wrapper attributes in source order, /// which means the outermost wrapper attribute is provided first. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 25f90c3d45002..8bdb94abff1c9 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5312,6 +5312,7 @@ VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer, Bits.VarDecl.IsLazyStorageProperty = false; Bits.VarDecl.HasNonPatternBindingInit = false; Bits.VarDecl.IsPropertyWrapperBackingProperty = false; + Bits.VarDecl.IsTopLevelGlobal = false; } Type VarDecl::getType() const { @@ -5410,11 +5411,7 @@ bool VarDecl::isLazilyInitializedGlobal() const { // Top-level global variables in the main source file and in the REPL are not // lazily initialized. - auto sourceFileContext = dyn_cast(getDeclContext()); - if (!sourceFileContext) - return true; - - return !sourceFileContext->isScriptMode(); + return !isTopLevelGlobal(); } SourceRange VarDecl::getSourceRange() const { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 15b3c1b33d293..6edbf335b8d78 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -5840,6 +5840,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, VD->setStatic(StaticLoc.isValid()); VD->getAttrs() = Attributes; setLocalDiscriminator(VD); + VD->setTopLevelGlobal(topLevelDecl); // Set original declaration in `@differentiable` attributes. setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4c62bb3728b1a..e7ae80813ba74 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2657,6 +2657,7 @@ class DeclDeserializer { uint8_t rawIntroducer; bool isGetterMutating, isSetterMutating; bool isLazyStorageProperty; + bool isTopLevelGlobal; DeclID lazyStorageID; unsigned numAccessors, numBackingProperties; uint8_t readImpl, writeImpl, readWriteImpl, opaqueReadOwnership; @@ -2673,6 +2674,7 @@ class DeclDeserializer { hasNonPatternBindingInit, isGetterMutating, isSetterMutating, isLazyStorageProperty, + isTopLevelGlobal, lazyStorageID, opaqueReadOwnership, readImpl, writeImpl, readWriteImpl, @@ -2804,6 +2806,7 @@ class DeclDeserializer { } var->setLazyStorageProperty(isLazyStorageProperty); + var->setTopLevelGlobal(isTopLevelGlobal); // If there are any backing properties, record them. if (numBackingProperties > 0) { diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 265ea709a7c89..9c32d255477b4 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -1191,6 +1191,7 @@ namespace decls_block { BCFixed<1>, // is getter mutating? BCFixed<1>, // is setter mutating? BCFixed<1>, // is this the backing storage for a lazy property? + BCFixed<1>, // top level global? DeclIDField, // if this is a lazy property, this is the backing storage OpaqueReadOwnershipField, // opaque read ownership ReadImplKindField, // read implementation diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index ce49f72c4bccc..18abc58342f53 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3255,6 +3255,7 @@ class Serializer::DeclSerializer : public DeclVisitor { var->isGetterMutating(), var->isSetterMutating(), var->isLazyStorageProperty(), + var->isTopLevelGlobal(), S.addDeclRef(lazyStorage), accessors.OpaqueReadOwnership, accessors.ReadImpl, From 15c1b4eca7a665aa64082fc01d33d1d52f9889f5 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 28 Jan 2020 16:16:04 -0800 Subject: [PATCH 035/237] Compile libraries in testcases with -parse-as-library (NFC) This is in preparation to a change in serialization of global variables where this detail will matter. --- test/DebugInfo/inlinescopes.swift | 2 +- test/SILGen/expressions.swift | 2 +- test/SILGen/global_init_attribute.swift | 2 +- test/SILGen/global_resilience.swift | 2 +- test/Serialization/Recovery/typedefs.swift | 7 +++++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/test/DebugInfo/inlinescopes.swift b/test/DebugInfo/inlinescopes.swift index 769b8a38ce8f0..6c4a3a4fefc3d 100644 --- a/test/DebugInfo/inlinescopes.swift +++ b/test/DebugInfo/inlinescopes.swift @@ -6,7 +6,7 @@ // RUN: %FileCheck %s -check-prefix=TRANSPARENT-CHECK < %t.ll // CHECK: define{{( dllexport)?}}{{( protected)?( signext)?}} i32 @main{{.*}} -// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %4), !dbg ![[CALL:.*]] +// CHECK: call swiftcc i64 @"$s4main8noinlineys5Int64VADF"(i64 %{{.*}}), !dbg ![[CALL:.*]] // CHECK-DAG: ![[TOPLEVEL:.*]] = !DIFile(filename: "{{.*}}inlinescopes.swift" import FooBar diff --git a/test/SILGen/expressions.swift b/test/SILGen/expressions.swift index f642702602900..54654a6ee7b29 100644 --- a/test/SILGen/expressions.swift +++ b/test/SILGen/expressions.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: echo "public var x = Int()" | %target-swift-frontend -module-name FooBar -emit-module -o %t - +// RUN: echo "public var x = Int()" | %target-swift-frontend -parse-as-library -module-name FooBar -emit-module -o %t - // RUN: %target-swift-emit-silgen -parse-stdlib -module-name expressions %s -I%t -disable-access-control | %FileCheck %s import Swift diff --git a/test/SILGen/global_init_attribute.swift b/test/SILGen/global_init_attribute.swift index f47bde0192a73..95167c7127078 100644 --- a/test/SILGen/global_init_attribute.swift +++ b/test/SILGen/global_init_attribute.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -emit-module -o %t %S/Inputs/def_global.swift +// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -parse-as-library -emit-module -o %t %S/Inputs/def_global.swift // RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -I %t %s | %FileCheck %s // // Test that SILGen uses the "global_init" attribute for all global diff --git a/test/SILGen/global_resilience.swift b/test/SILGen/global_resilience.swift index c05720d0713eb..78b425a9ed684 100644 --- a/test/SILGen/global_resilience.swift +++ b/test/SILGen/global_resilience.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift +// RUN: %target-swift-frontend -emit-module -parse-as-library -enable-library-evolution -emit-module-path=%t/resilient_global.swiftmodule -module-name=resilient_global %S/../Inputs/resilient_global.swift // RUN: %target-swift-emit-silgen -I %t -enable-library-evolution -parse-as-library %s | %FileCheck %s // RUN: %target-swift-emit-sil -I %t -O -enable-library-evolution -parse-as-library %s | %FileCheck --check-prefix=CHECK-OPT %s diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index a9fab839e045b..60d0eda010d56 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -1,4 +1,7 @@ // RUN: %empty-directory(%t) + +// Cannot use -parse-as-library here because that would compile also the +// #if VERIFY path, which contains top-level code. // RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s @@ -23,9 +26,9 @@ import Lib // CHECK-SIL-LABEL: sil hidden [ossa] @$s8typedefs11testSymbolsyyF func testSymbols() { // Check that the symbols are not using 'Bool'. - // CHECK-SIL: function_ref @$s3Lib1xs5Int32Vvau + // CHECK-SIL: global_addr @$s3Lib9usesAssocs5Int32VSgvp _ = Lib.x - // CHECK-SIL: function_ref @$s3Lib9usesAssocs5Int32VSgvau + // CHECK-SIL: global_addr @$s3Lib1xs5Int32Vvp _ = Lib.usesAssoc } // CHECK-SIL: end sil function '$s8typedefs11testSymbolsyyF' From 3fff5dd064fd99eb905cbbec37cc8e6e9c19762c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 29 Jan 2020 09:40:38 -0800 Subject: [PATCH 036/237] [ConstraintSystem] Extend metatype instance type mismatch coverage Originally type mismatches associated with metatypes were only covered for coercions but fix coverage grew since then and now it makes sense to remove special case and let `repairFailures` take care of it. --- lib/Sema/CSSimplify.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 870eae283b2f5..2a24f5f95d981 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4068,16 +4068,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, auto result = matchTypes(instanceType1, instanceType2, subKind, subflags, locator.withPathElement(ConstraintLocator::InstanceType)); - if (shouldAttemptFixes() && result.isFailure()) { - auto *anchor = locator.getAnchor(); - if (anchor && isa(anchor)) { - auto *fix = - ContextualMismatch::create(*this, instanceType1, instanceType2, - getConstraintLocator(locator)); - conversionsOrFixes.push_back(fix); - break; - } - } + + // If matching of the instance types resulted in the failure make sure + // to give `repairFailure` a chance to run to attempt to fix the issue. + if (shouldAttemptFixes() && result.isFailure()) + break; + return result; } From 0bd1e61a20652577b00017c5cd087c2dbe73d2f9 Mon Sep 17 00:00:00 2001 From: Eric Miotto <1094986+edymtt@users.noreply.github.com> Date: Wed, 29 Jan 2020 10:09:32 -0800 Subject: [PATCH 037/237] [build][gardening] adjust one more framework path (#29529) This is a follow to #29507 addressing one more place in which we need to collate -F with the framework path Addresses rdar://problem/58934566 --- cmake/modules/AddSwift.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index 931d751a03459..a89442f485d85 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -2133,7 +2133,10 @@ function(add_swift_target_library name) list(APPEND swiftlib_swift_compile_flags_all "-Fsystem" "${ios_support_frameworks_path}") list(APPEND swiftlib_c_compile_flags_all "-iframework" "${ios_support_frameworks_path}") - list(APPEND swiftlib_link_flags_all "-F" "${ios_support_frameworks_path}") + # We collate -F with the framework path to avoid unwanted deduplication + # of options by target_compile_options -- this way no undesired + # side effects are introduced should a new search path be added. + list(APPEND swiftlib_link_flags_all "-F${ios_support_frameworks_path}") endif() if(sdk IN_LIST SWIFT_APPLE_PLATFORMS AND SWIFTLIB_IS_SDK_OVERLAY) From 17d9d20275aac858104e92b3d33e0b5bdcfe0344 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 29 Jan 2020 10:23:18 -0800 Subject: [PATCH 038/237] Improve one of the comments in COWArrayOpts. --- lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp index c48c161cb28ed..b4265a2690aa3 100644 --- a/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp @@ -849,11 +849,16 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable, // Check whether we can hoist make_mutable based on the operations that are // in the loop. - // Note that in this case we don't verify that the array buffer is not aliased - // and therefore we must be conservative if the make_mutable is executed - // conditionally (i.e. doesn't dominate all exit blocks). - // The test SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally - // shows the problem. + // + // Hoisting make_mutable releases the original array storage. If an alias of + // that storage is accessed on any path reachable from the loop header that + // doesn't already pass through the make_mutable, then hoisting is + // illegal. hasLoopOnlyDestructorSafeArrayOperations checks that the array + // storage is not accessed within the loop. However, this does not include + // paths exiting the loop. Rather than analyzing code outside the loop, simply + // check that the original make_mutable dominates all exits. The test + // SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally shows + // the problem. if (hasLoopOnlyDestructorSafeArrayOperations() && dominatesExits) { // Done. We can hoist the make_mutable. // We still need the array uses later to check if we can add loads to From 0440e914519f1212e00e30e22192ced00a21cdef Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 29 Jan 2020 10:54:18 -0800 Subject: [PATCH 039/237] Try setting mod times explicitly to fix unreproducible ci failure. --- test/Frontend/type-fingerprint.swift | 29 ++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/test/Frontend/type-fingerprint.swift b/test/Frontend/type-fingerprint.swift index 8d9188e8362c4..aa08da31bedf2 100644 --- a/test/Frontend/type-fingerprint.swift +++ b/test/Frontend/type-fingerprint.swift @@ -12,16 +12,27 @@ // RUN: cp %S/Inputs/type-fingerprint/{main,a}.swift %t // RUN: cp %S/Inputs/type-fingerprint/ofm.json %t // RUN: cp %S/Inputs/type-fingerprint/b0.swift %t/b.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + // RUN: cd %t && %swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output1 -// RUN: cp %t/b.swiftdeps %t/b1.swiftdeps +// only-run-for-debugging: cp %t/b.swiftdeps %t/b1.swiftdeps // Change one type, but uses of all types get recompiled // RUN: cp %S/Inputs/type-fingerprint/b1.swift %t/b.swift + +// Seeing weird failure on CI, so ensure that b.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/b.swift + // RUN: cd %t && %swiftc_driver -disable-type-fingerprints -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output2 -// RUN: cp %t/b.swiftdeps %t/b2.swiftdeps +// Save for debugging: +// only-run-for-debugging: cp %t/b.swiftdeps %t/b2.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINAB-RECOMPILED %s < %t/output2 @@ -40,16 +51,26 @@ // RUN: cp %S/Inputs/type-fingerprint/{main,a}.swift %t // RUN: cp %S/Inputs/type-fingerprint/ofm.json %t // RUN: cp %S/Inputs/type-fingerprint/b0.swift %t/b.swift + +// Seeing weird failure on CI, so set the mod times +// RUN: touch -t 200101010101 %t/*.swift + // RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output3 -// RUN: cp %t/b.swiftdeps %t/b3.swiftdeps +// only-run-for-debugging: cp %t/b.swiftdeps %t/b3.swiftdeps // Change one type, only uses of that type get recompiled // RUN: cp %S/Inputs/type-fingerprint/b1.swift %t/b.swift + +// Seeing weird failure on CI, so ensure that b.swift is newer +// RUN: touch -t 200201010101 %t/* +// RUN: touch -t 200101010101 %t/*.swift +// RUN: touch -t 200301010101 %t/b.swift + // RUN: cd %t && %swiftc_driver -enable-batch-mode -j2 -incremental -driver-show-incremental ./main.swift ./a.swift ./b.swift -module-name main -output-file-map ofm.json >&output4 -// RUN: cp %t/b.swiftdeps %t/b4.swiftdeps +// only-run-for-debugging: cp %t/b.swiftdeps %t/b4.swiftdeps // RUN: %FileCheck -check-prefix=CHECK-MAINB-RECOMPILED %s < %t/output4 From 4028ac245a8556353b5ba27ffe1c8f3d8f17fe03 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Wed, 29 Jan 2020 11:15:53 -0800 Subject: [PATCH 040/237] EscapeAnalysis: add support for access markers. This will be critical when OSSA relies on access markers everywhere and more passes are converted to OSSA. --- lib/SILOptimizer/Analysis/EscapeAnalysis.cpp | 1 + test/SILOptimizer/escape_analysis.sil | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index 62e0059646990..e5b1eec09a578 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -122,6 +122,7 @@ SILValue EscapeAnalysis::getPointerBase(SILValue value) { case ValueKind::StructElementAddrInst: case ValueKind::StructExtractInst: case ValueKind::TupleElementAddrInst: + case ValueKind::BeginAccessInst: case ValueKind::UncheckedTakeEnumDataAddrInst: case ValueKind::UncheckedEnumDataInst: case ValueKind::MarkDependenceInst: diff --git a/test/SILOptimizer/escape_analysis.sil b/test/SILOptimizer/escape_analysis.sil index eddfac11b7f09..e510fbadcda74 100644 --- a/test/SILOptimizer/escape_analysis.sil +++ b/test/SILOptimizer/escape_analysis.sil @@ -1888,3 +1888,25 @@ bb0(%0 : @guaranteed $@sil_unowned Builtin.NativeObject): %1 = strong_copy_unowned_value %0 : $@sil_unowned Builtin.NativeObject return %1 : $Builtin.NativeObject } + +// Test begin_access. It should look like a derived pointer. +// CHECK-LABEL: CG of testAccessMarkerHelper +sil hidden @testAccessMarkerHelper : $@convention(thin) (@inout SomeData) -> () { +bb0(%0 : $*SomeData): + %1 = tuple () + return %1 : $() +} + +// CHECK-LABEL: CG of testAccessMarker +// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1) +// CHECK-NEXT: Con [ref] %0.1 Esc: G, Succ: +// CHECK-LABEL: End +sil hidden @testAccessMarker : $@convention(thin) (@inout SomeData) -> () { +bb0(%0 : $*SomeData): + %1 = begin_access [modify] [static] %0 : $*SomeData + %2 = function_ref @testAccessMarkerHelper : $@convention(thin) (@inout SomeData) -> () + %3 = apply %2(%1) : $@convention(thin) (@inout SomeData) -> () + end_access %1 : $*SomeData + %5 = tuple () + return %5 : $() +} From b8960133ff57cade7f1d1254585c79502b820b8b Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 29 Jan 2020 11:45:58 -0800 Subject: [PATCH 041/237] Add source annotation to SILPrinter under flag -sil-print-sourceinfo (#29444) * Add source annotation to SILPrinter under flag -sil-print-sourceinfo rdar://58365252 --- include/swift/Basic/SourceManager.h | 3 +++ lib/Basic/SourceLoc.cpp | 15 ++++++++++++++- lib/SIL/SILPrinter.cpp | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/swift/Basic/SourceManager.h b/include/swift/Basic/SourceManager.h index ed4f5dc34126e..7cf754731c1ab 100644 --- a/include/swift/Basic/SourceManager.h +++ b/include/swift/Basic/SourceManager.h @@ -243,6 +243,9 @@ class SourceManager { llvm::Optional resolveOffsetForEndOfLine(unsigned BufferId, unsigned Line) const; + /// Get the length of the line + llvm::Optional getLineLength(unsigned BufferId, unsigned Line) const; + SourceLoc getLocForLineCol(unsigned BufferId, unsigned Line, unsigned Col) const { auto Offset = resolveFromLineCol(BufferId, Line, Col); return Offset.hasValue() ? getLocForOffset(BufferId, Offset.getValue()) : diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index 7c66731e41fe1..76547ff2e91f6 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -331,10 +331,20 @@ SourceManager::resolveOffsetForEndOfLine(unsigned BufferId, return resolveFromLineCol(BufferId, Line, ~0u); } +llvm::Optional +SourceManager::getLineLength(unsigned BufferId, unsigned Line) const { + auto BegOffset = resolveFromLineCol(BufferId, Line, 0); + auto EndOffset = resolveFromLineCol(BufferId, Line, ~0u); + if (BegOffset && EndOffset) { + return EndOffset.getValue() - BegOffset.getValue(); + } + return None; +} + llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, unsigned Line, unsigned Col) const { - if (Line == 0 || Col == 0) { + if (Line == 0) { return None; } const bool LineEnd = Col == ~0u; @@ -353,6 +363,9 @@ llvm::Optional SourceManager::resolveFromLineCol(unsigned BufferId, return None; } Ptr = LineStart; + if (Col == 0) { + return Ptr - InputBuf->getBufferStart(); + } // The <= here is to allow for non-inclusive range end positions at EOF for (; ; ++Ptr) { --Col; diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 4900797283f88..976b9c790185c 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -64,6 +64,10 @@ llvm::cl::opt SILPrintDebugInfo("sil-print-debuginfo", llvm::cl::init(false), llvm::cl::desc("Include debug info in SIL output")); +llvm::cl::opt +SILPrintSourceInfo("sil-print-sourceinfo", llvm::cl::init(false), + llvm::cl::desc("Include source annotation in SIL output")); + llvm::cl::opt SILPrintGenericSpecializationInfo( "sil-print-generic-specialization-info", llvm::cl::init(false), llvm::cl::desc("Include generic specialization" @@ -618,7 +622,22 @@ class SILPrinter : public SILInstructionVisitor { } *this << '\n'; + const auto &SM = BB->getModule().getASTContext().SourceMgr; + Optional PrevLoc; for (const SILInstruction &I : *BB) { + if (SILPrintSourceInfo) { + auto CurSourceLoc = I.getLoc().getSourceLoc(); + if (CurSourceLoc.isValid()) { + if (!PrevLoc || SM.getLineNumber(CurSourceLoc) > SM.getLineNumber(PrevLoc->getSourceLoc())) { + auto Buffer = SM.findBufferContainingLoc(CurSourceLoc); + auto Line = SM.getLineNumber(CurSourceLoc); + auto LineLength = SM.getLineLength(Buffer, Line); + PrintState.OS << " // " << SM.extractText({SM.getLocForLineCol(Buffer, Line, 0), LineLength.getValueOr(0)}) << + "\tSourceLoc: " << SM.getDisplayNameForLoc(CurSourceLoc) << ":" << Line << "\n"; + PrevLoc = I.getLoc(); + } + } + } Ctx.printInstructionCallBack(&I); if (SILPrintGenericSpecializationInfo) { if (auto AI = ApplySite::isa(const_cast(&I))) From 3c86061de8a2a6474860ae209b3e70ce6dc17866 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 28 Jan 2020 17:11:15 -0800 Subject: [PATCH 042/237] Sema: Fix module interface printing of inherited generic initializers Make sure we use the sugared form of GenericTypeParamType and not the canonical type. Fixes . --- lib/Sema/CodeSynthesis.cpp | 6 ++---- test/ModuleInterface/inherited-generic-parameters.swift | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index a087a878ba1d5..85e8249f4c9bd 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -411,10 +411,8 @@ configureGenericDesignatedInitOverride(ASTContext &ctx, auto *gp = cast(type); if (gp->getDepth() < superclassDepth) return Type(gp).subst(subMap); - return CanGenericTypeParamType::get( - gp->getDepth() - superclassDepth + depth, - gp->getIndex(), - ctx); + return genericParams->getParams()[gp->getIndex()] + ->getDeclaredInterfaceType(); }; auto lookupConformanceFn = diff --git a/test/ModuleInterface/inherited-generic-parameters.swift b/test/ModuleInterface/inherited-generic-parameters.swift index 4a538a33942b7..1be75a69d7311 100644 --- a/test/ModuleInterface/inherited-generic-parameters.swift +++ b/test/ModuleInterface/inherited-generic-parameters.swift @@ -15,6 +15,10 @@ public class Base { // CHECK-NEXT: public init(x: @escaping (In) -> Out) public init(x: @escaping (In) -> Out) {} + +// CHECK-NEXT: public init(_: A, _: A) + public init(_: A, _: A) {} + // CHECK: } } @@ -22,6 +26,7 @@ public class Base { public class Derived : Base { // CHECK-NEXT: {{(@objc )?}}deinit // CHECK-NEXT: override public init(x: @escaping (T) -> T) +// CHECK-NEXT: override public init(_ argument: A, _ argument: A) // CHECK-NEXT: } } From 2e1e4284ac3ebc64a0cedee59aa88b1c7ec5b3be Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 29 Jan 2020 13:31:50 -0800 Subject: [PATCH 043/237] Added ability to add fingerprint via # --- include/swift/AST/FineGrainedDependencies.h | 1 + ...endenciesSourceFileDepGraphConstructor.cpp | 37 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 50cba234d0e46..49f0b755f9e39 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -799,6 +799,7 @@ class SourceFileDepGraph { compoundNamesByRDK); static constexpr char noncascadingOrPrivatePrefix = '#'; + static constexpr char nameFingerprintSeparator = ','; static std::string noncascading(std::string name); diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp index a2cb94342861f..eba5fe33192bb 100644 --- a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp +++ b/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp @@ -848,12 +848,29 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( static StringRef stripPrefix(const StringRef name) { return name.ltrim(SourceFileDepGraph::noncascadingOrPrivatePrefix); } +static StringRef stripFingerprint(const StringRef nameAndFingerprint) { + return nameAndFingerprint.split(SourceFileDepGraph::nameFingerprintSeparator) + .first; +} +static StringRef stripName(const StringRef nameAndFingerprint) { + return nameAndFingerprint.split(SourceFileDepGraph::nameFingerprintSeparator) + .second; +} +static std::string extractName(const StringRef prefixNameFingerprint) { + return stripFingerprint(stripPrefix(prefixNameFingerprint)).str(); +} +static Optional extractFingerprint( + const StringRef prefixNameFingerprint) { + const auto fp = stripName(stripPrefix(prefixNameFingerprint)); + return fp.empty() ? None : Optional(fp.str()); +} static std::vector getBaseNameProvides(ArrayRef simpleNames) { std::vector result; for (StringRef n : simpleNames) - result.push_back(ContextNameFingerprint("", stripPrefix(n).str(), None)); + result.push_back(ContextNameFingerprint("", extractName(n), + extractFingerprint(n))); return result; } @@ -861,7 +878,8 @@ static std::vector getMangledHolderProvides(ArrayRef simpleNames) { std::vector result; for (StringRef n : simpleNames) - result.push_back(ContextNameFingerprint(stripPrefix(n).str(), "", None)); + result.push_back(ContextNameFingerprint(extractName(n), "", + extractFingerprint(n))); return result; } @@ -869,12 +887,15 @@ static std::vector getCompoundProvides( ArrayRef> compoundNames) { std::vector result; for (const auto &p : compoundNames) - result.push_back(ContextNameFingerprint(stripPrefix(p.first), - stripPrefix(p.second), None)); + result.push_back(ContextNameFingerprint(extractName(p.first), + extractName(p.second), + extractFingerprint(p.second))); return result; } -static bool cascades(const std::string &s) { return s.empty() || s[0] != SourceFileDepGraph::noncascadingOrPrivatePrefix; } +static bool cascades(const std::string &s) { + return s.empty() || s[0] != SourceFileDepGraph::noncascadingOrPrivatePrefix; +} // Use '_' as a prefix for a file-private member static bool isPrivate(const std::string &s) { @@ -904,7 +925,8 @@ getCompoundDepends( // (On Linux, the compiler needs more verbosity than: // result.push_back({{n, "", false}, cascades(n)}); result.push_back( - std::make_pair(std::make_tuple(stripPrefix(n), std::string(), false), cascades(n))); + std::make_pair(std::make_tuple(stripPrefix(n), std::string(), false), + cascades(n))); } for (auto &p : compoundNames) { // Likewise, for Linux expand the following out: @@ -932,7 +954,8 @@ SourceFileDepGraph SourceFileDepGraph::simulateLoad( SourceFileDepGraphConstructor c( swiftDepsFilename, includePrivateDeps, hadCompilationError, interfaceHash, getSimpleDepends(simpleNamesByRDK[dependsTopLevel]), - getCompoundDepends(simpleNamesByRDK[dependsNominal], compoundNamesByRDK[dependsMember]), + getCompoundDepends(simpleNamesByRDK[dependsNominal], + compoundNamesByRDK[dependsMember]), getSimpleDepends(simpleNamesByRDK[dependsDynamicLookup]), getExternalDepends(simpleNamesByRDK[dependsExternal]), {}, // precedence groups From e13ea48dcdbee74a5641d98b463737411a1473a7 Mon Sep 17 00:00:00 2001 From: Chris Amanse Date: Wed, 29 Jan 2020 14:14:12 -0800 Subject: [PATCH 044/237] Private method should be for non-Windows only --- stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift index 6d79bd124e976..54517a901b117 100644 --- a/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift +++ b/stdlib/private/SwiftPrivateThreadExtras/ThreadBarriers.swift @@ -82,6 +82,7 @@ public func _stdlib_thread_barrier_init( return 0 } +#if !os(Windows) private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t>) -> CInt { guard pthread_mutex_init(barrier.pointee.mutex!, nil) == 0 else { return -1 @@ -92,6 +93,7 @@ private func _stdlib_thread_barrier_mutex_and_cond_init(_ barrier: UnsafeMutable } return 0 } +#endif public func _stdlib_thread_barrier_destroy( _ barrier: UnsafeMutablePointer<_stdlib_thread_barrier_t> From d2ad3dc3259c9a8d3eadcf58a4ac7977d040ca8b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 29 Jan 2020 15:29:13 -0800 Subject: [PATCH 045/237] [NFC] Commit a regression test for rdar://58960414 --- .../Headers/CategoryOverrides.h | 13 ++++++++ .../PrivateHeaders/Private.h | 4 +++ ...bjc_redeclared_properties_categories.swift | 30 +++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h index 03c13252ac497..4c66878d60f8a 100644 --- a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h @@ -19,3 +19,16 @@ typedef struct SomeStruct_s { @interface MyDerivedClass : MyBaseClass @property (nonatomic, strong, nullable) Base *derivedMember; @end + +typedef enum { + Caster, + Grantulated, + Confectioners, + Cane, + Demerara, + Turbinado, +} RefinedSugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private)); + +@interface Refinery : Base +@property (nonatomic, readonly) RefinedSugar sugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private)); +@end diff --git a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h index d52dd6c550ec9..0c4ed45702bca 100644 --- a/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h +++ b/test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h @@ -14,3 +14,7 @@ @interface MyBaseClass () @end + +@interface Refinery () +@property (nonatomic, readwrite) RefinedSugar sugar; +@end diff --git a/test/ClangImporter/objc_redeclared_properties_categories.swift b/test/ClangImporter/objc_redeclared_properties_categories.swift index 0281251b87a75..7cf1ae534e37b 100644 --- a/test/ClangImporter/objc_redeclared_properties_categories.swift +++ b/test/ClangImporter/objc_redeclared_properties_categories.swift @@ -1,11 +1,11 @@ // RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s // RUN: echo '#import ' > %t.h -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s --allow-empty +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s --allow-empty // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -F %S/Inputs/frameworks -o %t.pch %t.h -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s +// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s import CategoryOverrides @@ -54,3 +54,27 @@ func takesABaseClass(_ x: MyBaseClass) { // CHECK-PRIVATE-NOT: has no member 'derivedMember' x.derivedMember = Base() } + +// A category declared in a (private) header can introduce overrides of a +// property that has mismatched Swift naming conventions. If we see a +// non-__attribute__((swift_private)) decl, sometimes it comes in too. + +extension Refinery { + public enum RefinedSugar { + case caster + case grantulated + case confectioners + case cane + case demerara + case turbinado + } + + public var sugar: Refinery.RefinedSugar { + return .caster // RefinedSugar(self.__sugar) + } +} + +func takesARefinery(_ x: Refinery) { + // CHECK: cannot assign to property: 'sugar' is a get-only property + x.sugar = .caster +} From 286781fc3e8325456278755ebc334cd2e9b7ab49 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Wed, 29 Jan 2020 16:48:42 -0800 Subject: [PATCH 046/237] Add a unit test for an individual node change, & add ability to set fingerprints in unit tests. --- .../Driver/FineGrainedDependencyDriverGraph.h | 2 +- .../FineGrainedDependencyDriverGraph.cpp | 7 ++++++- .../FineGrainedDependencyGraphTests.cpp | 16 ++++++++++++++ ...peBodyFingerprintsDependencyGraphTests.cpp | 21 +++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index af7b2f5116c06..9d1e9954d2c15 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -437,7 +437,7 @@ class ModuleDepGraph { /// Call \p fn for each node whose key matches \p key. void forEachMatchingNode(const DependencyKey &key, - function_ref) const; + function_ref) const; void forEachNodeInJob(StringRef swiftDeps, function_ref) const; diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index dd997a283f1ca..5bda1956f9b4c 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -171,6 +171,11 @@ ModuleDepGraph::findJobsToRecompileWhenNodesChange< std::unordered_set>( const std::unordered_set &); +template std::vector +ModuleDepGraph::findJobsToRecompileWhenNodesChange< + std::vector>( + const std::vector &); + std::vector ModuleDepGraph::computeSwiftDepsFromNodes( ArrayRef nodes) const { llvm::StringSet<> swiftDepsOfNodes; @@ -441,7 +446,7 @@ void ModuleDepGraph::forEachNode( void ModuleDepGraph::forEachMatchingNode( const DependencyKey &key, - function_ref fn) const { + function_ref fn) const { nodeMap.forEachValueMatching( key, [&](const std::string &, ModuleDepGraphNode *n) { fn(n); }); } diff --git a/unittests/Driver/FineGrainedDependencyGraphTests.cpp b/unittests/Driver/FineGrainedDependencyGraphTests.cpp index b8af2dddc03a7..28e8286866d9e 100644 --- a/unittests/Driver/FineGrainedDependencyGraphTests.cpp +++ b/unittests/Driver/FineGrainedDependencyGraphTests.cpp @@ -808,3 +808,19 @@ TEST(ModuleDepGraph, MutualInterfaceHash) { const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); EXPECT_TRUE(contains(jobs, &job1)); } + +TEST(ModuleDepGraph, DisabledTypeBodyFingerprints) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ false); + + graph.simulateLoad(&job0, {{dependsNominal, {"B2"}}}); + graph.simulateLoad(&job1, {{providesNominal, {"B1", "B2"}}}); + graph.simulateLoad(&job2, {{dependsNominal, {"B1"}}}); + + { + const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job1); + EXPECT_EQ(3u, jobs.size()); + EXPECT_TRUE(contains(jobs, &job0)); + EXPECT_TRUE(contains(jobs, &job1)); + EXPECT_TRUE(contains(jobs, &job2)); + } +} diff --git a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp index ff06b0344c252..041cc9c2ee416 100644 --- a/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp +++ b/unittests/Driver/TypeBodyFingerprintsDependencyGraphTests.cpp @@ -808,3 +808,24 @@ TEST(ModuleDepGraphWithTypeBodyFingerprints, MutualInterfaceHash) { const auto jobs = graph.findJobsToRecompileWhenWholeJobChanges(&job0); EXPECT_TRUE(contains(jobs, &job1)); } + +TEST(ModuleDepGraph, EnabledTypeBodyFingerprints) { + ModuleDepGraph graph(/*EnableTypeFingerprints=*/ true); + + graph.simulateLoad(&job0, {{dependsNominal, {"B2"}}}); + graph.simulateLoad(&job1, {{providesNominal, {"B1", "B2"}}}); + graph.simulateLoad(&job2, {{dependsNominal, {"B1"}}}); + + + const DependencyKey k = DependencyKey(NodeKind::nominal, + DeclAspect::interface, "B1", ""); + std::vector changedNodes; + graph.forEachMatchingNode( + k, + [&](ModuleDepGraphNode* n) {changedNodes.push_back(n);}); + { + const auto jobs = graph.findJobsToRecompileWhenNodesChange(changedNodes); + EXPECT_TRUE(contains(jobs, &job2)); + EXPECT_FALSE(contains(jobs, &job0)); + } +} From 9bd638579de91b68c77219623ae987008fa4e6dd Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 29 Jan 2020 14:51:28 -0800 Subject: [PATCH 047/237] Sema: Fix duplicate diagnostic spam with 'Self' in properties Fixes / . --- lib/Sema/TypeCheckDeclPrimary.cpp | 11 +++++++---- test/type/self.swift | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index cb4f75303721a..5659c6f8912aa 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -1336,13 +1336,13 @@ class DeclChecker : public DeclVisitor { } if (VD->getDeclContext()->getSelfClassDecl()) { - checkDynamicSelfType(VD, VD->getValueInterfaceType()); - if (VD->getValueInterfaceType()->hasDynamicSelfType()) { if (VD->hasStorage()) VD->diagnose(diag::dynamic_self_in_stored_property); else if (VD->isSettable(nullptr)) VD->diagnose(diag::dynamic_self_in_mutable_property); + else + checkDynamicSelfType(VD, VD->getValueInterfaceType()); } } @@ -2153,8 +2153,11 @@ class DeclChecker : public DeclVisitor { checkExplicitAvailability(FD); - if (FD->getDeclContext()->getSelfClassDecl()) - checkDynamicSelfType(FD, FD->getResultInterfaceType()); + // Skip this for accessors, since we should have diagnosed the + // storage itself. + if (!isa(FD)) + if (FD->getDeclContext()->getSelfClassDecl()) + checkDynamicSelfType(FD, FD->getResultInterfaceType()); checkDefaultArguments(FD->getParameters()); diff --git a/test/type/self.swift b/test/type/self.swift index bc2fa859cb96b..10e27b93a4e6e 100644 --- a/test/type/self.swift +++ b/test/type/self.swift @@ -270,3 +270,20 @@ class Foo { Self.value * 2 }() } + +// https://bugs.swift.org/browse/SR-11681 - duplicate diagnostics +struct Box { + let boxed: T +} + +class Boxer { + lazy var s = Box(boxed: self as! Self) + // expected-error@-1 {{stored property cannot have covariant 'Self' type}} + // expected-error@-2 {{mutable property cannot have covariant 'Self' type}} + + var t = Box(boxed: Self()) + // expected-error@-1 {{stored property cannot have covariant 'Self' type}} + // expected-error@-2 {{covariant 'Self' type cannot be referenced from a stored property initializer}} + + required init() {} +} From f4a5301bfd6c71e4673442f7314d74a1d4ed3db8 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 29 Jan 2020 22:36:06 -0800 Subject: [PATCH 048/237] Sema: Fix crash when property type references type alias with unsatisfied requirements The fix for did not handle properties because those types are resolved using the archetype resolver. We would bail out early because of a bogus early return that can just be removed. Fixes . --- lib/Sema/TypeCheckType.cpp | 4 ---- test/Constraints/conditionally_defined_types.swift | 12 ++++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index ad3ab2f99e947..483c9db23cb4d 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -1463,10 +1463,6 @@ static Type resolveNestedIdentTypeComponent( inferredAssocType); } - // At this point, we need to have resolved the type of the member. - if (memberType->hasError()) - return memberType; - // If there are generic arguments, apply them now. return applyGenericArguments(memberType, resolution, comp, options); }; diff --git a/test/Constraints/conditionally_defined_types.swift b/test/Constraints/conditionally_defined_types.swift index 2107fa6e6faca..193d40a7f766d 100644 --- a/test/Constraints/conditionally_defined_types.swift +++ b/test/Constraints/conditionally_defined_types.swift @@ -222,3 +222,15 @@ let _ = Conforms.Decl4.Decl2.self // expected-error {{type 'Z2.T' (aka 'Y let _ = Conforms.Decl4.Decl3.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl4.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} let _ = Conforms.Decl4.Decl5.self // expected-error {{type 'Z2.T' (aka 'Y') does not conform to protocol 'P'}} + +// rdar://problem/45271663 +protocol PP { associatedtype V } +struct SS {} +enum EE {} +extension EE : PP where A : PP, B : PP { typealias V = EE } + +protocol P2 { associatedtype U } +func f(s: SS) -> SS> where PI.U : PP, PI.V : P2 { + let t: EE.V + // expected-error@-1 {{type 'PI.V.U' does not conform to protocol 'PP'}} +} From be37141b0fc5ad82dafbaaed2936a5a62c4de1e0 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Wed, 29 Jan 2020 23:56:09 -0800 Subject: [PATCH 049/237] Revert "build: simplify version tracking logic" --- lib/Basic/CMakeLists.txt | 58 ++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index fd60da8ff5379..6cd7577ce3f93 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -11,32 +11,59 @@ else() set(UUID_INCLUDE "${UUID_INCLUDE_DIRS}") endif() -function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file("${dir}" ${name}_vc) +# Figure out if we can track VC revisions. +# FIXME: Copied from Clang. +function(find_first_existing_file out_var) + foreach(file ${ARGN}) + if(EXISTS "${file}") + set(${out_var} "${file}" PARENT_SCOPE) + return() + endif() + endforeach() +endfunction() - # Create custom target to generate the VC revision include. - set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") +macro(find_first_existing_vc_file out_var path) + find_first_existing_file(${out_var} + "${path}/.git/logs/HEAD" # Git + "${path}/.svn/wc.db" # SVN 1.7 + "${path}/.svn/entries" # SVN 1.6 + ) +endmacro() - set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") +set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") - add_custom_command(OUTPUT "${version_inc}" - DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" - COMMAND ${CMAKE_COMMAND} "-DNAMES=$" - "-D$_SOURCE_DIR=${dir}" - "-DHEADER_FILE=${version_inc}" - -P "${generate_vcs_version_script}") +function(generate_revision_inc revision_inc_var name dir) + find_first_existing_vc_file(dep_file "${dir}") + # Create custom target to generate the VC revision include. + set(revision_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") + string(TOUPPER ${name} upper_name) + if(DEFINED dep_file) + add_custom_command(OUTPUT "${revision_inc}" + DEPENDS "${dep_file}" "${generate_vcs_version_script}" + COMMAND + ${CMAKE_COMMAND} "-DNAMES=${upper_name}" + "-D${upper_name}_SOURCE_DIR=${dir}" + "-DHEADER_FILE=${revision_inc}" + -P "${generate_vcs_version_script}") + else() + # Generate an empty Revision.inc file if we are not using git or SVN. + file(WRITE "${revision_inc}" "") + endif() # Mark the generated header as being generated. - set_source_files_properties("${version_inc}" + set_source_files_properties("${revision_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) - set(${revision_inc_var} ${version_inc} PARENT_SCOPE) + set(${revision_inc_var} ${revision_inc} PARENT_SCOPE) endfunction() generate_revision_inc(llvm_revision_inc LLVM "${LLVM_MAIN_SRC_DIR}") generate_revision_inc(clang_revision_inc Clang "${CLANG_MAIN_SRC_DIR}") generate_revision_inc(swift_revision_inc Swift "${SWIFT_SOURCE_DIR}") +set(version_inc_files + ${llvm_revision_inc} ${clang_revision_inc} ${swift_revision_inc}) + add_swift_host_library(swiftBasic STATIC AnyValue.cpp Cache.cpp @@ -68,10 +95,7 @@ add_swift_host_library(swiftBasic STATIC Unicode.cpp UUID.cpp Version.cpp - - ${llvm_revision_inc} - ${clang_revision_inc} - ${swift_revision_inc} + ${version_inc_files} # Platform-specific TaskQueue implementations Unix/TaskQueue.inc From dcaa950f7b42893bec920f73223cc3d3bdabf7f9 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Thu, 30 Jan 2020 12:28:32 -0300 Subject: [PATCH 050/237] [CSDiag] Removing unused FailureDiagnosis::visitTryExpr --- lib/Sema/CSDiag.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 1910e242ce091..ddb0d6d86c42d 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -221,7 +221,6 @@ class FailureDiagnosis :public ASTVisitor{ validateContextualType(Type contextualType, ContextualTypePurpose CTP); bool visitExpr(Expr *E); - bool visitTryExpr(TryExpr *E); bool visitApplyExpr(ApplyExpr *AE); bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E); @@ -1425,12 +1424,6 @@ visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) { return false; } -/// A TryExpr doesn't change it's argument, nor does it change the contextual -/// type. -bool FailureDiagnosis::visitTryExpr(TryExpr *E) { - return visit(E->getSubExpr()); -} - bool FailureDiagnosis::visitExpr(Expr *E) { // Check each of our immediate children to see if any of them are // independently invalid. From 2d10a8a9a0c5ac7f0de5cf8bda980dbad0b5e4e2 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 30 Jan 2020 09:45:29 -0800 Subject: [PATCH 051/237] [ConstraintSystem] Extend function type conversion mismatch coverage Originally type mismatches associated with conversions between function types were only covered for coercions and assignments but fix coverage grew since then and now it makes sense to remove special case and let `repairFailures` take care of it. --- lib/Sema/CSSimplify.cpp | 17 ++--------------- test/Constraints/enum_cases.swift | 8 ++++---- test/Constraints/tuple_arguments.swift | 3 ++- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 2a24f5f95d981..fabf1338b2a97 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4083,21 +4083,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, auto result = matchFunctionTypes(func1, func2, kind, flags, locator); - if (shouldAttemptFixes() && result.isFailure()) { - // If this is a contextual type mismatch failure - // let's give the solver a chance to "fix" it. - if (auto last = locator.last()) { - if (last->is()) - break; - } - - // If this is a type mismatch in assignment, we don't really care - // (yet) was it argument or result type mismatch, let's produce a - // diagnostic which mentions both function types. - auto *anchor = locator.getAnchor(); - if (anchor && isa(anchor)) - break; - } + if (shouldAttemptFixes() && result.isFailure()) + break; return result; } diff --git a/test/Constraints/enum_cases.swift b/test/Constraints/enum_cases.swift index 5a918dacd6fa1..dbfe34ec5ca59 100644 --- a/test/Constraints/enum_cases.swift +++ b/test/Constraints/enum_cases.swift @@ -22,14 +22,14 @@ let _ = arr.map(E.bar) // Ok let _ = arr.map(E.two) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (Int, Int) -> E)'}} // expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} -let _ = arr.map(E.tuple) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping ((x: Int, y: Int)) -> E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} +let _ = arr.map(E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> E' to expected argument type '(String) throws -> T'}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} let _ = arr.map(G_E.foo) // Ok let _ = arr.map(G_E.bar) // Ok let _ = arr.map(G_E.two) // expected-error {{cannot convert value of type '(String, String) -> G_E' to expected argument type '(String) throws -> G_E'}} -let _ = arr.map(G_E.tuple) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping ((x: Int, y: Int)) -> G_E)'}} -// expected-note@-1{{expected an argument list of type '((Self.Element) throws -> T)'}} +let _ = arr.map(G_E.tuple) // expected-error {{cannot convert value of type '((x: Int, y: Int)) -> G_E' to expected argument type '(String) throws -> T'}} +// expected-error@-1 {{generic parameter 'T' could not be inferred}} let _ = E.foo("hello") // expected-error {{missing argument label 'bar:' in call}} let _ = E.bar("hello") // Ok diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index 3011132d48d78..0b1852ea221ed 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -1702,7 +1702,8 @@ _ = x.map { (_: ()) in () } // https://bugs.swift.org/browse/SR-9470 do { func f(_: Int...) {} - let _ = [(1, 2, 3)].map(f) // expected-error {{cannot invoke 'map' with an argument list of type '(@escaping (Int...) -> ())'}} + let _ = [(1, 2, 3)].map(f) // expected-error {{cannot convert value of type '(Int...) -> ()' to expected argument type '((Int, Int, Int)) throws -> T'}} + // expected-error@-1 {{generic parameter 'T' could not be inferred}} } // rdar://problem/48443263 - cannot convert value of type '() -> Void' to expected argument type '(_) -> Void' From d171b30d097752bf72958f1f28496961e70e5fe6 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Thu, 30 Jan 2020 09:48:49 -0800 Subject: [PATCH 052/237] Allow fine-grained-dependency-include-intrafile in tests for compatibility with enabled type fingerprints. --- include/swift/Option/Options.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index dd2d0c0d026f4..dbadf83aa6318 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -191,7 +191,7 @@ HelpText<"Emit dot files every time driver imports an fine-grained swiftdeps fil def fine_grained_dependency_include_intrafile : Flag<["-"], "fine-grained-dependency-include-intrafile">, -InternalDebugOpt, +Flags<[FrontendOption, HelpHidden]>, HelpText<"Include within-file dependencies.">; def emit_fine_grained_dependency_sourcefile_dot_files : From 458ae7b6f94dbc47c2412508df7128b79cb7f901 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Thu, 30 Jan 2020 09:49:18 -0800 Subject: [PATCH 053/237] Use -fine-grained-dependency-include-intrafile for compatibility with type fingerprints. --- .../NameBinding/reference-dependencies-fine.swift | 11 +++++++---- .../reference-dependencies-members-fine.swift | 15 +++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/test/NameBinding/reference-dependencies-fine.swift b/test/NameBinding/reference-dependencies-fine.swift index f9e4b38ab5f65..e15fbcf24e500 100644 --- a/test/NameBinding/reference-dependencies-fine.swift +++ b/test/NameBinding/reference-dependencies-fine.swift @@ -4,10 +4,11 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps +// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps // Check that the output is deterministic. -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // Merge each entry onto one line and sort to overcome order differences // RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps @@ -339,7 +340,8 @@ struct Outer { } } -// CHECK-TOPLEVEL-DAG: topLevel interface '' privateFunc false +// CHECK-TOPLEVEL-DAG: topLevel interface '' privateFunc true +// CHECK-TOPLEVEL-DAG: topLevel implementation '' privateFunc true private func privateFunc() {} // CHECK-TOPLEVEL-DAG: topLevel interface '' topLevel1 false @@ -498,7 +500,8 @@ struct Sentinel2 {} // CHECK-POTENTIALMEMBER-DAG: potentialMember interface 4main13PrivateProto3P '' false // CHECK-NOMINAL-2-DAG: nominal interface Sa '' false -// CHECK-NOMINAL-2-DAG: nominal interface Sb '' false +// CHECK-NOMINAL-2-DAG: nominal interface Sb '' true +// CHECK-NOMINAL-2-DAG: nominal implementation Sb '' true // CHECK-NOMINAL-2-DAG: nominal interface 4main18ClassFromOtherFileC '' false // CHECK-NOMINAL-2-DAG: nominal interface SL '' false // CHECK-NOMINAL-2-DAG: nominal interface s25ExpressibleByFloatLiteralP '' false diff --git a/test/NameBinding/reference-dependencies-members-fine.swift b/test/NameBinding/reference-dependencies-members-fine.swift index c1c9ba58dff74..bbb99fbfb3e98 100644 --- a/test/NameBinding/reference-dependencies-members-fine.swift +++ b/test/NameBinding/reference-dependencies-members-fine.swift @@ -4,9 +4,11 @@ // RUN: %empty-directory(%t) // RUN: cp %s %t/main.swift -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps -// RUN: %target-swift-frontend -enable-fine-grained-dependencies -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps +// Need -fine-grained-dependency-include-intrafile to be invarient wrt type-body-fingerprints enabled/disabled +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t.swiftdeps + +// RUN: %target-swift-frontend -enable-fine-grained-dependencies -fine-grained-dependency-include-intrafile -typecheck -primary-file %t/main.swift %S/Inputs/reference-dependencies-members-helper.swift -emit-reference-dependencies-path - > %t-2.swiftdeps // RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t.swiftdeps >%t-processed.swiftdeps // RUN: %S/../Inputs/process_fine_grained_swiftdeps.sh <%t-2.swiftdeps >%t-2-processed.swiftdeps @@ -14,7 +16,6 @@ // RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL-2 %s < %t-processed.swiftdeps -// RUN: %FileCheck -check-prefix=PROVIDES-NOMINAL-NEGATIVE %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-MEMBER %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=PROVIDES-MEMBER-NEGATIVE %s < %t-processed.swiftdeps // RUN: %FileCheck -check-prefix=DEPENDS-NOMINAL %s < %t-processed.swiftdeps @@ -54,15 +55,13 @@ protocol SomeProto {} // DEPENDS-MEMBER-DAG: member interface 4main10OtherClassC deinit false extension OtherClass : SomeProto {} -// PROVIDES-NOMINAL-NEGATIVE-NOT: nominal implementation 4main11OtherStructV '' true -// PROVIDES-NOMINAL-NEGATIVE-NOT: nominal interface 4main11OtherStructV '' true -// DEPENDS-NOMINAL-DAG: nominal interface 4main11OtherStructV '' false +// PROVIDES-NOMINAL-DAG: nominal implementation 4main11OtherStructV '' true +// PROVIDES-NOMINAL-DAG: nominal interface 4main11OtherStructV '' true extension OtherStruct { // PROVIDES-MEMBER-DAG: potentialMember interface 4main11OtherStructV '' true // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV foo true // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV bar true - // PROVIDES-MEMBER-NEGATIVE-NOT: member interface 4main11OtherStructV baz true - // DEPENDS-MEMBER-DAG: member interface 4main11OtherStructV baz false + // PROVIDES-MEMBER-DAG: member interface 4main11OtherStructV baz true // DEPENDS-MEMBER-NEGATIVE-NOT:: potentialMember interface 4main11OtherStructV baz false func foo() {} var bar: () { return () } From 6debe3d3b500420b474e9d86a5e5ec25bc63ed23 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 30 Jan 2020 09:54:24 -0800 Subject: [PATCH 054/237] [ConstraintSystem] Don't produce conformance fixes for mismatches associated with function result type If there are any requirement failures associated with result types which are part of a function type conversion, let's record general conversion mismatch in order for it to capture and display complete function types. --- lib/Sema/CSSimplify.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index fabf1338b2a97..09a1c7eda4973 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -2160,6 +2160,17 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, break; } + + // TODO(diagnostics): If there are any requirement failures associated + // with result types which are part of a function type conversion, + // let's record general conversion mismatch in order for it to capture + // and display complete function types. + // + // Once either reacher locators or better diagnostic presentation for + // nested type failures is available this check could be removed. + if (last->is()) + return getTypeMatchFailure(locator); + } else { // There are no elements in the path auto *anchor = locator.getAnchor(); if (!(anchor && From e49dd639fd039bb35c3c661a667b08e86535cff6 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 29 Jan 2020 18:55:07 -0800 Subject: [PATCH 055/237] [metadata prespecialization] Enumerate arguments as usual. Previously, when emitting the metadata accessor, the generic arguments of the type were enumerated one after the next. That was fine in most cases but is incorrect in cases where the actual number of generic arguments is less than apparent number as when two arguments are required to be equal. Now, the arguments are enumerated according to the requirements vended by the GenericTypeRequirements struct. --- lib/IRGen/MetadataRequest.cpp | 27 ++-- ...ension-equal_arguments-1distinct_use.swift | 125 ++++++++++++++++++ 2 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 3d8385d7db6e6..c9270a2ee1b67 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1753,28 +1753,33 @@ static void emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( SmallVector, 4> specializationBlocks; auto switchDestination = llvm::BasicBlock::Create(IGM.getLLVMContext()); - unsigned long index = 0; + unsigned long blockIndex = 0; for (auto specialization : specializations) { - auto conditionBlock = conditionBlocks[index]; + auto conditionBlock = conditionBlocks[blockIndex]; IGF.Builder.emitBlock(conditionBlock); - auto successorBlock = index < conditionBlocks.size() - 1 - ? conditionBlocks[index + 1] + auto successorBlock = blockIndex < conditionBlocks.size() - 1 + ? conditionBlocks[blockIndex + 1] : switchDestination; auto specializationBlock = llvm::BasicBlock::Create(IGM.getLLVMContext()); auto substitutions = specialization->getContextSubstitutionMap( IGM.getSwiftModule(), nominal); llvm::Value *condition = llvm::ConstantInt::get(IGM.Int1Ty, 1); - auto generic = specialization->getAnyGeneric(); - auto parameters = generic->getGenericEnvironment()->getGenericParams(); - for (size_t index = 0; index < parameters.size(); ++index) { - auto parameter = parameters[index]; - auto argument = ((Type *)parameter)->subst(substitutions); + auto nominal = specialization->getAnyNominal(); + auto requirements = GenericTypeRequirements(IGF.IGM, nominal); + int requirementIndex = 0; + for (auto requirement : requirements.getRequirements()) { + if (requirement.Protocol) { + continue; + } + auto parameter = requirement.TypeParameter; + auto argument = parameter.subst(substitutions); llvm::Constant *addr = IGM.getAddrOfTypeMetadata(argument->getCanonicalType()); auto addrInt = IGF.Builder.CreateBitCast(addr, IGM.Int8PtrTy); condition = IGF.Builder.CreateAnd( - condition, IGF.Builder.CreateICmpEQ(addrInt, valueAtIndex(index))); + condition, IGF.Builder.CreateICmpEQ(addrInt, valueAtIndex(requirementIndex))); + ++requirementIndex; } IGF.Builder.CreateCondBr(condition, specializationBlock, successorBlock); @@ -1793,7 +1798,7 @@ static void emitCanonicalSpecializationsForGenericTypeMetadataAccessFunction( response = IGF.Builder.CreateInsertValue( response, state, 1, "insert metadata state into response"); specializationBlocks.push_back({specializationBlock, response}); - ++index; + ++blockIndex; } for (auto pair : specializationBlocks) { diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift new file mode 100644 index 0000000000000..9b7ff2a4cf9f9 --- /dev/null +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift @@ -0,0 +1,125 @@ +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// CHECK: @"$s4main9NamespaceVAAq_RszrlE5ValueVyS2i_SSGMf" = internal constant <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }> <{ +// i8** getelementptr inbounds ( +// %swift.vwtable, +// %swift.vwtable* @"$s4main9NamespaceVAAq_RszrlE5ValueVyS2i_SSGWV", +// i32 0, +// i32 0 +// ), +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: %swift.type_descriptor* bitcast ( +// CHECK-SAME: <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* +// CHECK-SAME: @"$s4main9NamespaceVAAq_RszrlE5ValueVMn" +// CHECK-SAME: to %swift.type_descriptor* +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* @"$sSiN", +// CHECK-SAME: %swift.type* @"$sSSN", +// CHECK-SAME: i32 0, +// CHECK-SAME: {{(\[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: i64 3 +// CHECK-SAME: }>, +// CHECK-SAME: align [[ALIGNMENT]] + +struct Namespace { +} +extension Namespace where First == Second { + struct Value { + let value: (First, Third) + } +} + +@inline(never) +func consume(_ t: T) { + withExtendedLifetime(t) { t in + } +} + +// CHECK: define hidden swiftcc void @"$s4main4doityyF"() #{{[0-9]+}} { +// CHECK: call swiftcc void @"$s4main7consumeyyxlF"( +// CHECK-SAME: %swift.opaque* noalias nocapture %{{[0-9]+}}, +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s4main9NamespaceVAAq_RszrlE5ValueVyS2i_SSGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) +// CHECK-SAME: ) +// CHECK: } +func doit() { + consume( Namespace.Value(value: (1, "two")) ) +} +doit() + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main9NamespaceVAAq_RszrlE5ValueVMa"([[INT]], %swift.type*, %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE_1:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: [[ERASED_TYPE_2:%[0-9]+]] = bitcast %swift.type* %2 to i8* +// CHECK: br label %[[TYPE_COMPARISON_1:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_1]]: +// CHECK: [[EQUAL_TYPE_1_1:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE_1]] +// CHECK: [[EQUAL_TYPES_1_1:%[0-9]+]] = and i1 true, [[EQUAL_TYPE_1_1]] +// CHECK: [[EQUAL_TYPE_1_2:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSSN" to i8*), [[ERASED_TYPE_2]] +// CHECK: [[EQUAL_TYPES_1_2:%[0-9]+]] = and i1 [[EQUAL_TYPES_1_1]], [[EQUAL_TYPE_1_2]] +// CHECK: br i1 [[EQUAL_TYPES_1_2]], label %[[EXIT_PRESPECIALIZED_1:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED_1]]: +// CHECK: ret %swift.metadata_response { +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\])?}}, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s4main9NamespaceVAAq_RszrlE5ValueVyS2i_SSGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: [[INT]] 0 +// CHECK-SAME: } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata( +// CHECK-SAME: [[INT]] %0, +// CHECK-SAME: i8* [[ERASED_TYPE_1]], +// CHECK-SAME: i8* [[ERASED_TYPE_2]], +// CHECK-SAME: i8* undef, +// CHECK-SAME: %swift.type_descriptor* bitcast ( +// CHECK-SAME: <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8, i32, i32, i32 }>* +// CHECK-SAME: @"$s4main9NamespaceVAAq_RszrlE5ValueVMn" +// CHECK-SAME: to %swift.type_descriptor* +// CHECK-SAME: ) +// CHECK-SAME: ) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } From 38f599dcb78777a4632d7a6865acdd248713ad90 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Thu, 30 Jan 2020 11:48:13 -0800 Subject: [PATCH 056/237] Turn type-body-fingerprints on-by-default --- include/swift/Basic/LangOptions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index ae4e11c5f563d..c125f046281c9 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -304,7 +304,7 @@ namespace swift { /// the interface hash, hash them into per-iterable-decl-context /// fingerprints. Fine-grained dependency types won't dirty every provides /// in a file when the user adds a member to, e.g., a struct. - bool EnableTypeFingerprints = false; + bool EnableTypeFingerprints = true; /// When using fine-grained dependencies, emit dot files for every swiftdeps /// file. From 3375b362ebe6b4677deef859ee7a272f44317fc6 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 30 Jan 2020 14:15:38 -0800 Subject: [PATCH 057/237] Search nested types tables in parent modules Add another cross-cutting module configuration to the nested types table search path. A module can have no overlay but also contain a nested types table suitable for finding a given member name. UIKit is the sharpest example of this state of affairs. UIKit currently defines an overlay in Swift where we find some nested types. But it also defines an inner module that has some other nested types tables. Resolves rdar://58940989 --- lib/Serialization/Deserialization.cpp | 4 ++++ test/Serialization/multi-file-nested-type-extension.swift | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 4c62bb3728b1a..8e9d098532b23 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -1468,6 +1468,10 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) { if (auto overlayModule = LF->getOverlayModule()) { nestedType = findNestedTypeDeclInModule(getFile(), overlayModule, memberName, baseType); + } else if (LF->getParentModule() != extensionModule) { + nestedType = findNestedTypeDeclInModule(getFile(), + LF->getParentModule(), + memberName, baseType); } } } diff --git a/test/Serialization/multi-file-nested-type-extension.swift b/test/Serialization/multi-file-nested-type-extension.swift index fd2d139a25009..181fc38b72c52 100644 --- a/test/Serialization/multi-file-nested-type-extension.swift +++ b/test/Serialization/multi-file-nested-type-extension.swift @@ -10,7 +10,7 @@ // REQUIRES: asserts // CHECK: Statistics -// CHECK: 1 Serialization - # of nested types resolved without full lookup +// CHECK: 2 Serialization - # of nested types resolved without full lookup // Note the Optional here and below; this was once necessary to produce a crash. // Without it, the type of the parameter is initialized "early" enough to not @@ -21,3 +21,9 @@ extension Outer { public func useTypes(_: Outer.Callback?) {} } + +extension OuterClass.Inner { + public static var instance: OuterClass.Inner { + return OuterClass.Inner() + } +} From e121cb09cdab86c1479cc0ebf8ac063f2823e0a8 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 30 Jan 2020 15:04:07 -0800 Subject: [PATCH 058/237] Revert "[SourceKit] Disable module system headers validation" This reverts commit 951b85359a8b4781bae9e0735a893aee07b3dd1e. --- lib/IDE/CompletionInstance.cpp | 5 +++++ tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp | 5 ----- tools/swift-ide-test/swift-ide-test.cpp | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 446d80601a9c3..7d326b96bf116 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -362,6 +362,11 @@ bool swift::ide::CompletionInstance::performOperation( // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; + // This validation may call stat(2) many times. Disable it to prevent + // performance regression. + Invocation.getSearchPathOptions().DisableModulesValidateSystemDependencies = + true; + // Since caching uses the interface hash, and since per type fingerprints // weaken that hash, disable them here: Invocation.getLangOptions().EnableTypeFingerprints = false; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp index 1ebbcfeb25441..1e2b1fd10a211 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp @@ -539,11 +539,6 @@ bool SwiftASTManager::initCompilerInvocation( // We don't care about LLVMArgs FrontendOpts.LLVMArgs.clear(); - // This validation may call stat(2) many times. Disable it to prevent - // performance issues. - Invocation.getSearchPathOptions().DisableModulesValidateSystemDependencies = - true; - // SwiftSourceInfo files provide source location information for decls coming // from loaded modules. For most IDE use cases it either has an undesirable // impact on performance with no benefit (code completion), results in stale diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index fb2b67b7225fb..995eaaaab5eef 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -3436,8 +3436,6 @@ int main(int argc, char *argv[]) { options::DebugForbidTypecheckPrefix; InitInvok.getTypeCheckerOptions().DebugConstraintSolver = options::DebugConstraintSolver; - InitInvok.getSearchPathOptions().DisableModulesValidateSystemDependencies = - true; for (auto ConfigName : options::BuildConfigs) InitInvok.getLangOptions().addCustomConditionalCompilationFlag(ConfigName); From cb20d0ebfa9623a61f581edaba7e805cd0c357fd Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 30 Jan 2020 15:04:56 -0800 Subject: [PATCH 059/237] Revert "[CodeCompletion] Disable module system headers validation" This reverts commit ec70a40205396a9bd9e0310b3b81b4b41838976a. --- lib/IDE/CompletionInstance.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/IDE/CompletionInstance.cpp b/lib/IDE/CompletionInstance.cpp index 7d326b96bf116..446d80601a9c3 100644 --- a/lib/IDE/CompletionInstance.cpp +++ b/lib/IDE/CompletionInstance.cpp @@ -362,11 +362,6 @@ bool swift::ide::CompletionInstance::performOperation( // source text. That breaks an invariant of syntax tree building. Invocation.getLangOptions().BuildSyntaxTree = false; - // This validation may call stat(2) many times. Disable it to prevent - // performance regression. - Invocation.getSearchPathOptions().DisableModulesValidateSystemDependencies = - true; - // Since caching uses the interface hash, and since per type fingerprints // weaken that hash, disable them here: Invocation.getLangOptions().EnableTypeFingerprints = false; From 82ec36df4f33ce59c14969229937c124d9baff07 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 30 Jan 2020 15:42:32 -0800 Subject: [PATCH 060/237] Update tsan-emptyarraystorage.swift to run on remote_run --- test/Sanitizers/tsan-emptyarraystorage.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/Sanitizers/tsan-emptyarraystorage.swift b/test/Sanitizers/tsan-emptyarraystorage.swift index 193a48514bb02..af9deb347e0f8 100644 --- a/test/Sanitizers/tsan-emptyarraystorage.swift +++ b/test/Sanitizers/tsan-emptyarraystorage.swift @@ -6,10 +6,6 @@ // REQUIRES: foundation // UNSUPPORTED: OS=tvos -// FIXME: This should be covered by "tsan_runtime"; older versions of Apple OSs -// don't support TSan. -// UNSUPPORTED: remote_run - import Foundation let sem = DispatchSemaphore(value: 0) From ad1ad43c6e4c6862870f6a095630f2e1ffba5da7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 27 Jan 2020 21:08:36 -0800 Subject: [PATCH 061/237] Generalize SolutionApplicationTarget and use it more widely. Make this type suitable for representing the result of solution application, and do so. --- lib/Sema/CSApply.cpp | 53 ++++++++++++++++++++----------------- lib/Sema/ConstraintSystem.h | 48 +++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 770b79c72a9c2..4195ef7b474e6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7231,14 +7231,14 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -llvm::PointerUnion ConstraintSystem::applySolutionImpl( +Optional ConstraintSystem::applySolutionImpl( Solution &solution, SolutionApplicationTarget target, Type convertType, bool discardedExpr, bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { if (shouldSuppressDiagnostics()) - return nullptr; + return None; bool diagnosedErrorsViaFixes = applySolutionFixes(solution); // If all of the available fixes would result in a warning, @@ -7248,12 +7248,12 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( })) { // If we already diagnosed any errors via fixes, that's it. if (diagnosedErrorsViaFixes) - return nullptr; + return None; // If we didn't manage to diagnose anything well, so fall back to // diagnosing mining the system to construct a reasonable error message. diagnoseFailureFor(target); - return nullptr; + return None; } } @@ -7261,9 +7261,13 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( ExprWalker walker(rewriter); // Apply the solution to the target. - llvm::PointerUnion result; + SolutionApplicationTarget result = target; if (auto expr = target.getAsExpr()) { - result = expr->walk(walker); + Expr *rewrittenExpr = expr->walk(walker); + if (!rewrittenExpr) + return None; + + result.setExpr(rewrittenExpr); } else { auto fn = *target.getAsFunction(); @@ -7284,14 +7288,11 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( }); if (!newBody) - return result; + return None; - result = newBody; + result.setFunctionBody(newBody); } - if (result.isNull()) - return result; - // If we're re-typechecking an expression for diagnostics, don't // visit closures that have non-single expression bodies. if (!performingDiagnostics) { @@ -7309,10 +7310,10 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( // If any of them failed to type check, bail. if (hadError) - return nullptr; + return None; } - if (auto resultExpr = result.dyn_cast()) { + if (auto resultExpr = result.getAsExpr()) { Expr *expr = target.getAsExpr(); assert(expr && "Can't have expression result without expression target"); // We are supposed to use contextual type only if it is present and @@ -7328,19 +7329,20 @@ llvm::PointerUnion ConstraintSystem::applySolutionImpl( // If we're supposed to convert the expression to some particular type, // do so now. if (shouldCoerceToContextualType()) { - result = rewriter.coerceToType(resultExpr, convertType, - getConstraintLocator(expr)); - if (!result) - return nullptr; + resultExpr = rewriter.coerceToType(resultExpr, convertType, + getConstraintLocator(expr)); } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { // We referenced an lvalue. Load it. - result = rewriter.coerceToType(resultExpr, - getType(resultExpr)->getRValueType(), - getConstraintLocator(expr)); + resultExpr = rewriter.coerceToType(resultExpr, + getType(resultExpr)->getRValueType(), + getConstraintLocator(expr)); } - if (resultExpr) - solution.setExprTypes(resultExpr); + if (!resultExpr) + return None; + + solution.setExprTypes(resultExpr); + result.setExpr(resultExpr); } rewriter.finalize(); @@ -7512,13 +7514,14 @@ MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { return MutableArrayRef(solutions, numSolutions); } -llvm::PointerUnion SolutionApplicationTarget::walk( - ASTWalker &walker) { +SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { switch (kind) { case Kind::expression: return getAsExpr()->walk(walker); case Kind::function: - return getAsFunction()->getBody()->walk(walker); + return SolutionApplicationTarget( + *getAsFunction(), + cast_or_null(getFunctionBody()->walk(walker))); } } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 472174e6de469..6b4e14edc5ad2 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1147,7 +1147,11 @@ class SolutionApplicationTarget { union { Expr *expression; - AnyFunctionRef function; + + struct { + AnyFunctionRef function; + BraceStmt *body; + } function; }; public: @@ -1156,9 +1160,13 @@ class SolutionApplicationTarget { expression = expr; } - SolutionApplicationTarget(AnyFunctionRef fn) { + SolutionApplicationTarget(AnyFunctionRef fn) + : SolutionApplicationTarget(fn, fn.getBody()) { } + + SolutionApplicationTarget(AnyFunctionRef fn, BraceStmt *body) { kind = Kind::function; - function = fn; + function.function = fn; + function.body = body; } Expr *getAsExpr() const { @@ -1171,18 +1179,32 @@ class SolutionApplicationTarget { } } + void setExpr(Expr *expr) { + assert(kind == Kind::expression); + expression = expr; + } + Optional getAsFunction() const { switch (kind) { case Kind::expression: return None; case Kind::function: - return function; + return function.function; } } + BraceStmt *getFunctionBody() const { + assert(kind == Kind::function); + return function.body; + } + + void setFunctionBody(BraceStmt *stmt) { + assert(kind == Kind::function); + function.body = stmt; + } /// Walk the contents of the application target. - llvm::PointerUnion walk(ASTWalker &walker); + SolutionApplicationTarget walk(ASTWalker &walker); }; enum class ConstraintSystemPhase { @@ -4147,7 +4169,7 @@ class ConstraintSystem { bool minimize); private: - llvm::PointerUnion applySolutionImpl( + Optional applySolutionImpl( Solution &solution, SolutionApplicationTarget target, Type convertType, bool discardedExpr, bool performingDiagnostics); @@ -4165,15 +4187,19 @@ class ConstraintSystem { Type convertType, bool discardedExpr, bool performingDiagnostics) { - return applySolutionImpl(solution, expr, convertType, discardedExpr, - performingDiagnostics).get(); + auto result = applySolutionImpl( + solution, expr, convertType, discardedExpr, performingDiagnostics); + if (result) + return result->getAsExpr(); + return nullptr; } /// Apply a given solution to the body of the given function. BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - return cast_or_null( - applySolutionImpl(solution, fn, Type(), false, false) - .dyn_cast()); + auto result = applySolutionImpl(solution, fn, Type(), false, false); + if (result) + return result->getFunctionBody(); + return nullptr; } /// Reorder the disjunctive clauses for a given expression to From 98db6e642262ea04b3d34b4903c3daaf15c109c0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 27 Jan 2020 21:08:53 -0800 Subject: [PATCH 062/237] =?UTF-8?q?[Constraint=20solver]=20Don=E2=80=99t?= =?UTF-8?q?=20mark=20a=20moved-from=20instance=20as=20=E2=80=9Cdiagnosed?= =?UTF-8?q?=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Sema/CSApply.cpp | 1 + lib/Sema/CSSolver.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 4195ef7b474e6..1f13de2f7e0b1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7511,6 +7511,7 @@ ArrayRef SolutionResult::getAmbiguousSolutions() const { MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { assert(getKind() == Ambiguous); + markAsDiagnosed(); return MutableArrayRef(solutions, numSolutions); } diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index fbae75e344bc2..20699a855fd48 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1172,7 +1172,6 @@ bool ConstraintSystem::solve(Expr *&expr, solutions.assign(std::make_move_iterator(ambiguousSolutions.begin()), std::make_move_iterator(ambiguousSolutions.end())); dumpSolutions(); - solution.markAsDiagnosed(); return false; } From e3124dcb5f41a9b908adf3d9360a97203c55adee Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 09:32:54 -0800 Subject: [PATCH 063/237] [Constraint system] Expand SolutionApplicationTarget for expressions. Add the final conversion type and the flag indicating whether a given expression is discarded to SolutionApplicationTarget, rather than separating the arguments to the solver implementation. --- lib/Sema/CSApply.cpp | 16 +++++++++++----- lib/Sema/CSSolver.cpp | 4 +++- lib/Sema/ConstraintSystem.h | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1f13de2f7e0b1..8fdcc55d12af1 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7232,8 +7232,8 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. Optional ConstraintSystem::applySolutionImpl( - Solution &solution, SolutionApplicationTarget target, Type convertType, - bool discardedExpr, bool performingDiagnostics) { + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve // them to specific expressions. if (!solution.Fixes.empty()) { @@ -7316,9 +7316,11 @@ Optional ConstraintSystem::applySolutionImpl( if (auto resultExpr = result.getAsExpr()) { Expr *expr = target.getAsExpr(); assert(expr && "Can't have expression result without expression target"); + // We are supposed to use contextual type only if it is present and // this expression doesn't represent the implicit return of the single // expression function which got deduced to be `Never`. + Type convertType = target.getExprConversionType(); auto shouldCoerceToContextualType = [&]() { return convertType && !(getType(resultExpr)->isUninhabited() && @@ -7331,7 +7333,8 @@ Optional ConstraintSystem::applySolutionImpl( if (shouldCoerceToContextualType()) { resultExpr = rewriter.coerceToType(resultExpr, convertType, getConstraintLocator(expr)); - } else if (getType(resultExpr)->hasLValueType() && !discardedExpr) { + } else if (getType(resultExpr)->hasLValueType() && + !target.isDiscardedExpr()) { // We referenced an lvalue. Load it. resultExpr = rewriter.coerceToType(resultExpr, getType(resultExpr)->getRValueType(), @@ -7517,8 +7520,11 @@ MutableArrayRef SolutionResult::takeAmbiguousSolutions() && { SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { switch (kind) { - case Kind::expression: - return getAsExpr()->walk(walker); + case Kind::expression: { + SolutionApplicationTarget result = *this; + result.setExpr(getAsExpr()->walk(walker)); + return result; + } case Kind::function: return SolutionApplicationTarget( diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 20699a855fd48..9be997703876d 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1184,7 +1184,9 @@ bool ConstraintSystem::solve(Expr *&expr, } if (stage == 1) { - diagnoseFailureFor(expr); + diagnoseFailureFor( + SolutionApplicationTarget(expr, convertType, + /*isDiscarded=*/false)); solution.markAsDiagnosed(); return true; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 6b4e14edc5ad2..c88c8b3541478 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1146,7 +1146,15 @@ class SolutionApplicationTarget { } kind; union { - Expr *expression; + struct { + Expr *expression; + + /// The type to which the expression should be converted. + Type convertType; + + /// Whether the expression result will be discarded at the end. + bool isDiscarded; + } expression; struct { AnyFunctionRef function; @@ -1155,9 +1163,11 @@ class SolutionApplicationTarget { }; public: - SolutionApplicationTarget(Expr *expr) { + SolutionApplicationTarget(Expr *expr, Type convertType, bool isDiscarded) { kind = Kind::expression; - expression = expr; + expression.expression = expr; + expression.convertType = convertType; + expression.isDiscarded = isDiscarded; } SolutionApplicationTarget(AnyFunctionRef fn) @@ -1172,16 +1182,26 @@ class SolutionApplicationTarget { Expr *getAsExpr() const { switch (kind) { case Kind::expression: - return expression; + return expression.expression; case Kind::function: return nullptr; } } + Type getExprConversionType() const { + assert(kind == Kind::expression); + return expression.convertType; + } + + bool isDiscardedExpr() const { + assert(kind == Kind::expression); + return expression.isDiscarded; + } + void setExpr(Expr *expr) { assert(kind == Kind::expression); - expression = expr; + expression.expression = expr; } Optional getAsFunction() const { @@ -4171,7 +4191,7 @@ class ConstraintSystem { private: Optional applySolutionImpl( Solution &solution, SolutionApplicationTarget target, - Type convertType, bool discardedExpr, bool performingDiagnostics); + bool performingDiagnostics); public: /// Apply a given solution to the expression, producing a fully @@ -4188,7 +4208,8 @@ class ConstraintSystem { bool discardedExpr, bool performingDiagnostics) { auto result = applySolutionImpl( - solution, expr, convertType, discardedExpr, performingDiagnostics); + solution, SolutionApplicationTarget(expr, convertType, discardedExpr), + performingDiagnostics); if (result) return result->getAsExpr(); return nullptr; @@ -4196,7 +4217,7 @@ class ConstraintSystem { /// Apply a given solution to the body of the given function. BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - auto result = applySolutionImpl(solution, fn, Type(), false, false); + auto result = applySolutionImpl(solution, fn, false); if (result) return result->getFunctionBody(); return nullptr; From eb2862ec1e0899c86aa7a01e767e993552d03e96 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 09:54:39 -0800 Subject: [PATCH 064/237] [Constraint system] Collapse applySolutionImpl() into applySolution(). Now that we have a unified notion of a SolutionApplicationTarget, use it to collapse 3 applySolution-ish functions into one. --- lib/Sema/BuilderTransform.cpp | 10 +++++++-- lib/Sema/CSApply.cpp | 2 +- lib/Sema/ConstraintSystem.h | 36 ++++++------------------------- lib/Sema/TypeCheckConstraints.cpp | 15 +++++++------ 4 files changed, 24 insertions(+), 39 deletions(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index d2f21354ab52e..0dff01b97cc29 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1107,8 +1107,14 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( } // Apply the solution to the function body. - return cast_or_null( - cs.applySolutionToBody(solutions.front(), func)); + if (auto result = cs.applySolution( + solutions.front(), + SolutionApplicationTarget(func), + /*performingDiagnostics=*/false)) { + return result->getFunctionBody(); + } + + return nullptr; } ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 8fdcc55d12af1..fa7d40e7b28c6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7231,7 +7231,7 @@ bool ConstraintSystem::applySolutionFixes(const Solution &solution) { /// Apply a given solution to the expression, producing a fully /// type-checked expression. -Optional ConstraintSystem::applySolutionImpl( +Optional ConstraintSystem::applySolution( Solution &solution, SolutionApplicationTarget target, bool performingDiagnostics) { // If any fixes needed to be applied to arrive at this solution, resolve diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c88c8b3541478..4ea147deb4d39 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -4188,40 +4188,16 @@ class ConstraintSystem { findBestSolution(SmallVectorImpl &solutions, bool minimize); -private: - Optional applySolutionImpl( - Solution &solution, SolutionApplicationTarget target, - bool performingDiagnostics); - public: - /// Apply a given solution to the expression, producing a fully - /// type-checked expression. + /// Apply a given solution to the target, producing a fully + /// type-checked target or \c None if an error occurred. /// - /// \param convertType the contextual type to which the - /// expression should be converted, if any. - /// \param discardedExpr if true, the result of the expression - /// is contextually ignored. + /// \param target the target to which the solution will be applied. /// \param performingDiagnostics if true, don't descend into bodies of /// non-single expression closures, or build curry thunks. - Expr *applySolution(Solution &solution, Expr *expr, - Type convertType, - bool discardedExpr, - bool performingDiagnostics) { - auto result = applySolutionImpl( - solution, SolutionApplicationTarget(expr, convertType, discardedExpr), - performingDiagnostics); - if (result) - return result->getAsExpr(); - return nullptr; - } - - /// Apply a given solution to the body of the given function. - BraceStmt *applySolutionToBody(Solution &solution, AnyFunctionRef fn) { - auto result = applySolutionImpl(solution, fn, false); - if (result) - return result->getFunctionBody(); - return nullptr; - } + Optional applySolution( + Solution &solution, SolutionApplicationTarget target, + bool performingDiagnostics); /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index de6d54af3adce..19d1d18153064 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2267,16 +2267,19 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, cs.applySolution(solution); // Apply the solution to the expression. - result = cs.applySolution( - solution, result, convertType.getType(), - options.contains(TypeCheckExprFlags::IsDiscarded), - options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)); - - if (!result) { + SolutionApplicationTarget target( + result, convertType.getType(), + options.contains(TypeCheckExprFlags::IsDiscarded)); + bool performingDiagnostics = + options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); + auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); + if (!resultTarget) { listener.applySolutionFailed(solution, expr); + // Failure already diagnosed, above, as part of applying the solution. return Type(); } + result = resultTarget->getAsExpr(); // Notify listener that we've applied the solution. result = listener.appliedSolution(solution, result); From 56aeac5d0a80935d170a08ed8f3b3e8c5675f3b3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:40:45 -0800 Subject: [PATCH 065/237] [Constraint system] Push SolutionApplicationTarget into the solveImpl Baby steps toward generalizing the expression-centric solver core. --- lib/Sema/CSSolver.cpp | 15 +++++++++------ lib/Sema/ConstraintSystem.h | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 9be997703876d..961dce47a94ad 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1136,9 +1136,10 @@ bool ConstraintSystem::solve(Expr *&expr, // Take up to two attempts at solving the system. The first attempts to // solve a system that is expected to be well-formed, the second kicks in // when there is an error and attempts to salvage an ill-formed expression. + SolutionApplicationTarget target(expr, convertType, /*isDiscarded=*/false); for (unsigned stage = 0; stage != 2; ++stage) { auto solution = (stage == 0) - ? solveImpl(expr, convertType, listener, allowFreeTypeVariables) + ? solveImpl(target, listener, allowFreeTypeVariables) : salvage(); switch (solution.getKind()) { @@ -1201,14 +1202,13 @@ bool ConstraintSystem::solve(Expr *&expr, } SolutionResult -ConstraintSystem::solveImpl(Expr *&expr, - Type convertType, +ConstraintSystem::solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, FreeTypeVariableBinding allowFreeTypeVariables) { if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - log << "---Constraint solving for the expression at "; - auto R = expr->getSourceRange(); + log << "---Constraint solving at "; + auto R = target.getSourceRange(); if (R.isValid()) { R.print(log, getASTContext().SourceMgr, /*PrintText=*/ false); } else { @@ -1220,6 +1220,7 @@ ConstraintSystem::solveImpl(Expr *&expr, assert(!solverState && "cannot be used directly"); // Set up the expression type checker timer. + Expr *expr = target.getAsExpr(); Timer.emplace(expr, *this); Expr *origExpr = expr; @@ -1240,7 +1241,7 @@ ConstraintSystem::solveImpl(Expr *&expr, // If there is a type that we're expected to convert to, add the conversion // constraint. - if (convertType) { + if (Type convertType = target.getExprConversionType()) { // Determine whether we know more about the contextual type. ContextualTypePurpose ctp = CTP_Unused; bool isOpaqueReturnType = false; @@ -1287,6 +1288,8 @@ ConstraintSystem::solveImpl(Expr *&expr, if (getExpressionTooComplex(solutions)) return SolutionResult::forTooComplex(); + target.setExpr(expr); + switch (solutions.size()) { case 0: return SolutionResult::forUndiagnosedError(); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 4ea147deb4d39..7d3846e646f7a 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1223,6 +1223,17 @@ class SolutionApplicationTarget { assert(kind == Kind::function); function.body = stmt; } + + /// Retrieve the source range of the target. + SourceRange getSourceRange() const { + switch (kind) { + case Kind::expression: + return expression.expression->getSourceRange(); + + case Kind::function: + return function.body->getSourceRange(); + } + } /// Walk the contents of the application target. SolutionApplicationTarget walk(ASTWalker &walker); }; @@ -4078,13 +4089,11 @@ class ConstraintSystem { /// Solve the system of constraints generated from provided expression. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target to generate constraints from. /// \param listener The callback to check solving progress. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. - SolutionResult solveImpl(Expr *&expr, - Type convertType, + SolutionResult solveImpl(SolutionApplicationTarget &target, ExprTypeCheckListener *listener, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); From 54ac78d28f4e9fd39148a3dcc2722d6661ac085a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:48:40 -0800 Subject: [PATCH 066/237] [Constrsint solver] Remove ExprTypeCheckListener::constraintGenerationFailed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function isn’t needed, because we can report the failure to the caller directly rather than go through a callback. --- lib/Sema/CSSolver.cpp | 2 -- lib/Sema/TypeCheckConstraints.cpp | 7 ------- lib/Sema/TypeChecker.h | 4 ---- 3 files changed, 13 deletions(-) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 961dce47a94ad..223fd0e3d6793 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1234,8 +1234,6 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, if (auto generatedExpr = generateConstraints(expr, DC)) expr = generatedExpr; else { - if (listener) - listener->constraintGenerationFailed(expr); return SolutionResult::forError(); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 19d1d18153064..4f8bbace048f8 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2036,7 +2036,6 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { } void ExprTypeCheckListener::preCheckFailed(Expr *expr) {} -void ExprTypeCheckListener::constraintGenerationFailed(Expr *expr) {} void ExprTypeCheckListener::applySolutionFailed(Solution &solution, Expr *expr) {} @@ -2104,12 +2103,6 @@ class FallbackDiagnosticListener : public ExprTypeCheckListener { maybeProduceFallbackDiagnostic(expr); } - void constraintGenerationFailed(Expr *expr) override { - if (BaseListener) - BaseListener->constraintGenerationFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - void applySolutionFailed(Solution &solution, Expr *expr) override { if (BaseListener) BaseListener->applySolutionFailed(solution, expr); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c4df6c61bb254..09068a8443e18 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -325,10 +325,6 @@ class ExprTypeCheckListener { /// be correctly processed by the constraint solver. virtual void preCheckFailed(Expr *expr); - /// Callback invoked if constraint system failed to generate - /// constraints for a given expression. - virtual void constraintGenerationFailed(Expr *expr); - /// Callback invoked if application of chosen solution to /// expression has failed. virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); From 754afff034df6f5cbcf9c4a4dfc8f40268f83f01 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:53:45 -0800 Subject: [PATCH 067/237] [Constraint solver] Remove ExprTypeCheckListener::preCheckFailed(). Failures will be reported to clients; this callback is unnecessary. --- lib/Sema/TypeCheckConstraints.cpp | 8 -------- lib/Sema/TypeChecker.h | 4 ---- 2 files changed, 12 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 4f8bbace048f8..834e00b6fcf80 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2035,7 +2035,6 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } -void ExprTypeCheckListener::preCheckFailed(Expr *expr) {} void ExprTypeCheckListener::applySolutionFailed(Solution &solution, Expr *expr) {} @@ -2097,12 +2096,6 @@ class FallbackDiagnosticListener : public ExprTypeCheckListener { return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; } - void preCheckFailed(Expr *expr) override { - if (BaseListener) - BaseListener->preCheckFailed(expr); - maybeProduceFallbackDiagnostic(expr); - } - void applySolutionFailed(Solution &solution, Expr *expr) override { if (BaseListener) BaseListener->applySolutionFailed(solution, expr); @@ -2169,7 +2162,6 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) { - listener.preCheckFailed(expr); return Type(); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 09068a8443e18..27fc91a4570fe 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -321,10 +321,6 @@ class ExprTypeCheckListener { virtual Expr *appliedSolution(constraints::Solution &solution, Expr *expr); - /// Callback invoked if expression is structurally unsound and can't - /// be correctly processed by the constraint solver. - virtual void preCheckFailed(Expr *expr); - /// Callback invoked if application of chosen solution to /// expression has failed. virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); From f8fd19729528f33f59ca17909c33725d4be41910 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 10:59:45 -0800 Subject: [PATCH 068/237] [Constraint solver] Remove ExprTypeCheckListener::applySolutionFailed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t need this fallback path. --- lib/Sema/TypeCheckConstraints.cpp | 44 ------------------------------- lib/Sema/TypeChecker.h | 4 --- 2 files changed, 48 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 834e00b6fcf80..29d7dd96c3066 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2035,9 +2035,6 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } -void ExprTypeCheckListener::applySolutionFailed(Solution &solution, - Expr *expr) {} - void ParentConditionalConformance::diagnoseConformanceStack( DiagnosticEngine &diags, SourceLoc loc, ArrayRef conformances) { @@ -2095,45 +2092,6 @@ class FallbackDiagnosticListener : public ExprTypeCheckListener { Expr *appliedSolution(Solution &solution, Expr *expr) override { return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; } - - void applySolutionFailed(Solution &solution, Expr *expr) override { - if (BaseListener) - BaseListener->applySolutionFailed(solution, expr); - - if (hadAnyErrors()) - return; - - // If solution involves invalid or incomplete conformances that's - // a probable cause of failure to apply it without producing an error, - // which is going to be diagnosed later, so let's not produce - // fallback diagnostic in this case. - if (llvm::any_of( - solution.Conformances, - [](const std::pair - &conformance) -> bool { - auto &ref = conformance.second; - return ref.isConcrete() && ref.getConcrete()->isInvalid(); - })) - return; - - maybeProduceFallbackDiagnostic(expr); - } - -private: - bool hadAnyErrors() const { return Context.Diags.hadAnyError(); } - - void maybeProduceFallbackDiagnostic(Expr *expr) const { - if (Options.contains(TypeCheckExprFlags::SubExpressionDiagnostics) || - DiagnosticSuppression::isEnabled(Context.Diags)) - return; - - // Before producing fatal error here, let's check if there are any "error" - // diagnostics already emitted or waiting to be emitted. Because they are - // a better indication of the problem. - if (!(hadAnyErrors() || Context.hasDelayedConformanceErrors())) - Context.Diags.diagnose(expr->getLoc(), - diag::failed_to_produce_diagnostic); - } }; #pragma mark High-level entry points @@ -2259,8 +2217,6 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); if (!resultTarget) { - listener.applySolutionFailed(solution, expr); - // Failure already diagnosed, above, as part of applying the solution. return Type(); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 27fc91a4570fe..71dab9dc6f570 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -320,10 +320,6 @@ class ExprTypeCheckListener { /// failure. virtual Expr *appliedSolution(constraints::Solution &solution, Expr *expr); - - /// Callback invoked if application of chosen solution to - /// expression has failed. - virtual void applySolutionFailed(constraints::Solution &solution, Expr *expr); }; /// A conditional conformance that implied some other requirements. That is, \c From 5c6608f5ca1b0d7faef732a7350ddc4c68cdccf1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 11:05:00 -0800 Subject: [PATCH 069/237] [Constraint solver] Remove the FallbackDiagnosticListener. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn’t do anything any more, because we’re handling error diagnostics locally without callbacks. Also collapse typeCheckExpressionImpl() into typeCheckExpression(). --- lib/Sema/TypeCheckConstraints.cpp | 52 +++---------------------------- lib/Sema/TypeChecker.h | 9 ------ 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 29d7dd96c3066..74b6424a0da22 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2062,38 +2062,6 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } -/// Sometimes constraint solver fails without producing any diagnostics, -/// that leads to crashes down the line in AST Verifier or SILGen -/// which, as a result, are much harder to figure out. -/// -/// This class is intended to guard against situations like that by -/// keeping track of failures of different type-check phases, and -/// emitting fallback fatal error if any of them fail without producing -/// error diagnostic, and there were no errors emitted or scheduled to be -/// emitted previously. -class FallbackDiagnosticListener : public ExprTypeCheckListener { - ASTContext &Context; - TypeCheckExprOptions Options; - ExprTypeCheckListener *BaseListener; - -public: - FallbackDiagnosticListener(ASTContext &ctx, TypeCheckExprOptions options, - ExprTypeCheckListener *base) - : Context(ctx), Options(options), BaseListener(base) {} - - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - return BaseListener ? BaseListener->builtConstraints(cs, expr) : false; - } - - Expr *foundSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->foundSolution(solution, expr) : expr; - } - - Expr *appliedSolution(Solution &solution, Expr *expr) override { - return BaseListener ? BaseListener->appliedSolution(solution, expr) : expr; - } -}; - #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType, @@ -2102,18 +2070,6 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { auto &Context = dc->getASTContext(); - FallbackDiagnosticListener diagListener(Context, options, listener); - return typeCheckExpressionImpl(expr, dc, convertType, convertTypePurpose, - options, diagListener, baseCS); -} - -Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - ConstraintSystem *baseCS) { - auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); @@ -2188,7 +2144,7 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, SmallVector viable; // Attempt to solve the constraint system. - if (cs.solve(expr, convertTo, &listener, viable, allowFreeTypeVariables)) + if (cs.solve(expr, convertTo, listener, viable, allowFreeTypeVariables)) return Type(); // If the client allows the solution to have unresolved type expressions, @@ -2202,7 +2158,8 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, auto result = expr; auto &solution = viable[0]; - result = listener.foundSolution(solution, result); + if (listener) + result = listener->foundSolution(solution, result); if (!result) return Type(); @@ -2223,7 +2180,8 @@ Type TypeChecker::typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, result = resultTarget->getAsExpr(); // Notify listener that we've applied the solution. - result = listener.appliedSolution(solution, result); + if (listener) + result = listener->appliedSolution(solution, result); if (!result) return Type(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 71dab9dc6f570..c82063b17044a 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -856,15 +856,6 @@ class TypeChecker final { TypeCheckExprOptions(), listener); } -private: - static Type typeCheckExpressionImpl(Expr *&expr, DeclContext *dc, - TypeLoc convertType, - ContextualTypePurpose convertTypePurpose, - TypeCheckExprOptions options, - ExprTypeCheckListener &listener, - constraints::ConstraintSystem *baseCS); - -public: /// Type check the given expression and return its type without /// applying the solution. /// From 40ca1ef1647d72de6ff6bf91c8ab5e83e280e12e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Jan 2020 11:13:55 -0800 Subject: [PATCH 070/237] [Constraint solver] Eliminate ExprTypeCheckListener::foundSolution() Only one client was using this, and its logic is trivially inlined into appliedSolution(). --- lib/Sema/TypeCheckConstraints.cpp | 21 ++++++--------------- lib/Sema/TypeChecker.h | 7 ------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 74b6424a0da22..a204332a66607 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2027,10 +2027,6 @@ bool ExprTypeCheckListener::builtConstraints(ConstraintSystem &cs, Expr *expr) { return false; } -Expr *ExprTypeCheckListener::foundSolution(Solution &solution, Expr *expr) { - return expr; -} - Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) { return expr; } @@ -2158,8 +2154,6 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, auto result = expr; auto &solution = viable[0]; - if (listener) - result = listener->foundSolution(solution, result); if (!result) return Type(); @@ -2578,16 +2572,13 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, return false; } - Expr *foundSolution(Solution &solution, Expr *expr) override { - // Figure out what type the constraints decided on. - auto ty = solution.simplifyType(initType); - initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); - - // Just keep going. - return expr; - } - Expr *appliedSolution(Solution &solution, Expr *expr) override { + { + // Figure out what type the constraints decided on. + auto ty = solution.simplifyType(initType); + initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false); + } + // Convert the initializer to the type of the pattern. expr = solution.coerceToType(expr, initType, Locator); if (!expr) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index c82063b17044a..3c643d706e25e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -305,13 +305,6 @@ class ExprTypeCheckListener { /// constraint system, or false otherwise. virtual bool builtConstraints(constraints::ConstraintSystem &cs, Expr *expr); - /// Callback invoked once a solution has been found. - /// - /// The callback may further alter the expression, returning either a - /// new expression (to replace the result) or a null pointer to indicate - /// failure. - virtual Expr *foundSolution(constraints::Solution &solution, Expr *expr); - /// Callback invokes once the chosen solution has been applied to the /// expression. /// From e0702d9f2e333ad1c9f0a5a88dec67caca99cc62 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 Jan 2020 21:51:57 -0800 Subject: [PATCH 071/237] [Constraint solver] Generalize solve() for arbitrary solution targets. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Start cleaning up the main “solve” entry point for solving an expression and applying the solution, so it handles arbitrary solution targets. This is another small step that doesn’t do much on its own, but will help with unifying the various places in the code base where we run the solver. --- lib/Sema/CSApply.cpp | 3 +- lib/Sema/CSSolver.cpp | 81 ++++++++++++++++--------------- lib/Sema/ConstraintSystem.h | 42 ++++++++++------ lib/Sema/TypeCheckConstraints.cpp | 44 +++++++++-------- 4 files changed, 93 insertions(+), 77 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index fa7d40e7b28c6..443f67d35fd29 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7331,7 +7331,8 @@ Optional ConstraintSystem::applySolution( // If we're supposed to convert the expression to some particular type, // do so now. if (shouldCoerceToContextualType()) { - resultExpr = rewriter.coerceToType(resultExpr, convertType, + resultExpr = rewriter.coerceToType(resultExpr, + simplifyType(convertType), getConstraintLocator(expr)); } else if (getType(resultExpr)->hasLValueType() && !target.isDiscardedExpr()) { diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 223fd0e3d6793..2d07b9245ba8b 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1074,7 +1074,8 @@ void ConstraintSystem::shrink(Expr *expr) { } } -static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { +static bool debugConstraintSolverForTarget( + ASTContext &C, SolutionApplicationTarget target) { if (C.TypeCheckerOpts.DebugConstraintSolver) return true; @@ -1082,14 +1083,13 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { // No need to compute the line number to find out it's not present. return false; - // Get the lines on which the expression starts and ends. + // Get the lines on which the target starts and ends. unsigned startLine = 0, endLine = 0; - if (expr->getSourceRange().isValid()) { - auto range = - Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, - expr->getSourceRange()); - startLine = C.SourceMgr.getLineNumber(range.getStart()); - endLine = C.SourceMgr.getLineNumber(range.getEnd()); + SourceRange range = target.getSourceRange(); + if (range.isValid()) { + auto charRange = Lexer::getCharSourceRangeFromSourceRange(C.SourceMgr, range); + startLine = C.SourceMgr.getLineNumber(charRange.getStart()); + endLine = C.SourceMgr.getLineNumber(charRange.getEnd()); } assert(startLine <= endLine && "expr ends before it starts?"); @@ -1107,25 +1107,26 @@ static bool debugConstraintSolverForExpr(ASTContext &C, Expr *expr) { return startBound != endBound; } -bool ConstraintSystem::solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables) { +Optional> ConstraintSystem::solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables +) { llvm::SaveAndRestore debugForExpr( getASTContext().TypeCheckerOpts.DebugConstraintSolver, - debugConstraintSolverForExpr(getASTContext(), expr)); + debugConstraintSolverForTarget(getASTContext(), target)); /// Dump solutions for debugging purposes. - auto dumpSolutions = [&] { + auto dumpSolutions = [&](const SolutionResult &result) { // Debug-print the set of solutions. if (getASTContext().TypeCheckerOpts.DebugConstraintSolver) { auto &log = getASTContext().TypeCheckerDebug->getStream(); - if (solutions.size() == 1) { + if (result.getKind() == SolutionResult::Success) { log << "---Solution---\n"; - solutions[0].dump(log); - } else { - for (unsigned i = 0, e = solutions.size(); i != e; ++i) { + result.getSolution().dump(log); + } else if (result.getKind() == SolutionResult::Ambiguous) { + auto solutions = result.getAmbiguousSolutions(); + for (unsigned i : indices(solutions)) { log << "--- Solution #" << i << " ---\n"; solutions[i].dump(log); } @@ -1135,45 +1136,47 @@ bool ConstraintSystem::solve(Expr *&expr, // Take up to two attempts at solving the system. The first attempts to // solve a system that is expected to be well-formed, the second kicks in - // when there is an error and attempts to salvage an ill-formed expression. - SolutionApplicationTarget target(expr, convertType, /*isDiscarded=*/false); + // when there is an error and attempts to salvage an ill-formed program. for (unsigned stage = 0; stage != 2; ++stage) { auto solution = (stage == 0) ? solveImpl(target, listener, allowFreeTypeVariables) : salvage(); switch (solution.getKind()) { - case SolutionResult::Success: + case SolutionResult::Success: { // Return the successful solution. - solutions.clear(); - solutions.push_back(std::move(solution).takeSolution()); - dumpSolutions(); - return false; + dumpSolutions(solution); + std::vector result; + result.push_back(std::move(solution).takeSolution()); + return std::move(result); + } case SolutionResult::Error: - return true; + return None; case SolutionResult::TooComplex: - getASTContext().Diags.diagnose(expr->getLoc(), diag::expression_too_complex) - .highlight(expr->getSourceRange()); + getASTContext().Diags.diagnose( + target.getLoc(), diag::expression_too_complex) + .highlight(target.getSourceRange()); solution.markAsDiagnosed(); - return true; + return None; case SolutionResult::Ambiguous: // If salvaging produced an ambiguous result, it has already been // diagnosed. if (stage == 1) { solution.markAsDiagnosed(); - return true; + return None; } if (Options.contains( ConstraintSystemFlags::AllowUnresolvedTypeVariables)) { + dumpSolutions(solution); auto ambiguousSolutions = std::move(solution).takeAmbiguousSolutions(); - solutions.assign(std::make_move_iterator(ambiguousSolutions.begin()), - std::make_move_iterator(ambiguousSolutions.end())); - dumpSolutions(); - return false; + std::vector result( + std::make_move_iterator(ambiguousSolutions.begin()), + std::make_move_iterator(ambiguousSolutions.end())); + return std::move(result); } LLVM_FALLTHROUGH; @@ -1181,15 +1184,13 @@ bool ConstraintSystem::solve(Expr *&expr, case SolutionResult::UndiagnosedError: if (shouldSuppressDiagnostics()) { solution.markAsDiagnosed(); - return true; + return None; } if (stage == 1) { - diagnoseFailureFor( - SolutionApplicationTarget(expr, convertType, - /*isDiscarded=*/false)); + diagnoseFailureFor(target); solution.markAsDiagnosed(); - return true; + return None; } // Loop again to try to salvage. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 7d3846e646f7a..e5383f0a7e518 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1194,6 +1194,11 @@ class SolutionApplicationTarget { return expression.convertType; } + void setExprConversionType(Type type) { + assert(kind == Kind::expression); + expression.convertType = type; + } + bool isDiscardedExpr() const { assert(kind == Kind::expression); return expression.isDiscarded; @@ -1234,6 +1239,18 @@ class SolutionApplicationTarget { return function.body->getSourceRange(); } } + + /// Retrieve the source location for the target. + SourceLoc getLoc() const { + switch (kind) { + case Kind::expression: + return expression.expression->getLoc(); + + case Kind::function: + return function.function.getLoc(); + } + } + /// Walk the contents of the application target. SolutionApplicationTarget walk(ASTWalker &walker); }; @@ -4104,26 +4121,21 @@ class ConstraintSystem { static bool preCheckExpression(Expr *&expr, DeclContext *dc, ConstraintSystem *baseCS = nullptr); - /// Solve the system of constraints generated from provided expression. - /// - /// The expression should have already been pre-checked with - /// preCheckExpression(). + /// Solve the system of constraints generated from provided target. /// - /// \param expr The expression to generate constraints from. - /// \param convertType The expected type of the expression. + /// \param target The target that we'll generate constraints from, which + /// may be updated by the solving process. /// \param listener The callback to check solving progress. - /// \param solutions The set of solutions to the system of constraints. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// - /// \returns true is an error occurred, false is system is consistent - /// and solutions were found. - bool solve(Expr *&expr, - Type convertType, - ExprTypeCheckListener *listener, - SmallVectorImpl &solutions, - FreeTypeVariableBinding allowFreeTypeVariables - = FreeTypeVariableBinding::Disallow); + /// \returns the set of solutions, if any were found, or \c None if an + /// error occurred. When \c None, an error has been emitted. + Optional> solve( + SolutionApplicationTarget &target, + ExprTypeCheckListener *listener, + FreeTypeVariableBinding allowFreeTypeVariables + = FreeTypeVariableBinding::Disallow); /// Solve the system of constraints. /// diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index a204332a66607..0a9f90509187a 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2138,22 +2138,25 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, convertTo = getOptionalType(expr->getLoc(), var); } - SmallVector viable; // Attempt to solve the constraint system. - if (cs.solve(expr, convertTo, listener, viable, allowFreeTypeVariables)) + SolutionApplicationTarget target( + expr, convertTo, + options.contains(TypeCheckExprFlags::IsDiscarded)); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) return Type(); // If the client allows the solution to have unresolved type expressions, // check for them now. We cannot apply the solution with unresolved TypeVars, // because they will leak out into arbitrary places in the resultant AST. if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && - (viable.size() != 1 || + (viable->size() != 1 || (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { return ErrorType::get(Context); } - auto result = expr; - auto &solution = viable[0]; + auto result = target.getAsExpr(); + auto &solution = (*viable)[0]; if (!result) return Type(); @@ -2161,11 +2164,10 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, cs.applySolution(solution); // Apply the solution to the expression. - SolutionApplicationTarget target( - result, convertType.getType(), - options.contains(TypeCheckExprFlags::IsDiscarded)); bool performingDiagnostics = options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); + // FIXME: HACK! + target.setExprConversionType(convertType.getType()); auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); if (!resultTarget) { // Failure already diagnosed, above, as part of applying the solution. @@ -2244,7 +2246,6 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics); // Attempt to solve the constraint system. - SmallVector viable; const Type originalType = expr->getType(); const bool needClearType = originalType && originalType->hasError(); const auto recoverOriginalType = [&] () { @@ -2256,14 +2257,16 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // re-check. if (needClearType) expr->setType(Type()); - if (cs.solve(expr, /*convertType*/Type(), listener, viable, - allowFreeTypeVariables)) { + SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); + if (!viable) { recoverOriginalType(); return Type(); } // Get the expression's simplified type. - auto &solution = viable[0]; + expr = target.getAsExpr(); + auto &solution = (*viable)[0]; auto &solutionCS = solution.getConstraintSystem(); Type exprType = solution.simplifyType(solutionCS.getType(expr)); @@ -2330,19 +2333,18 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( ConstraintSystem cs(dc, options); // Attempt to solve the constraint system. - SmallVector viable; - const Type originalType = expr->getType(); if (originalType && originalType->hasError()) expr->setType(Type()); - cs.solve(expr, /*convertType*/ Type(), listener, viable, - allowFreeTypeVariables); - - for (auto &solution : viable) { - auto exprType = solution.simplifyType(cs.getType(expr)); - assert(exprType && !exprType->hasTypeVariable()); - types.insert(exprType.getPointer()); + SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { + expr = target.getAsExpr(); + for (auto &solution : *viable) { + auto exprType = solution.simplifyType(cs.getType(expr)); + assert(exprType && !exprType->hasTypeVariable()); + types.insert(exprType.getPointer()); + } } } From ce357312b6b8164e2e9d8015d17215f9a5d0b6fd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 Jan 2020 21:59:39 -0800 Subject: [PATCH 072/237] [Constraint solver] Reinstate the fallback diagnostic, just in case. --- lib/Sema/CSSolver.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 2d07b9245ba8b..9e8c3678a7f2f 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1107,6 +1107,24 @@ static bool debugConstraintSolverForTarget( return startBound != endBound; } +/// If we aren't certain that we've emitted a diagnostic, emit a fallback +/// diagnostic. +static void maybeProduceFallbackDiagnostic( + ConstraintSystem &cs, SolutionApplicationTarget target) { + if (cs.Options.contains(ConstraintSystemFlags::SubExpressionDiagnostics) || + cs.Options.contains(ConstraintSystemFlags::SuppressDiagnostics)) + return; + + // Before producing fatal error here, let's check if there are any "error" + // diagnostics already emitted or waiting to be emitted. Because they are + // a better indication of the problem. + ASTContext &ctx = cs.getASTContext(); + if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors()) + return; + + ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic); +} + Optional> ConstraintSystem::solve( SolutionApplicationTarget &target, ExprTypeCheckListener *listener, @@ -1152,6 +1170,7 @@ Optional> ConstraintSystem::solve( } case SolutionResult::Error: + maybeProduceFallbackDiagnostic(*this, target); return None; case SolutionResult::TooComplex: From 5c956ba2e147e309da7c4e619587adade1584b7c Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 30 Jan 2020 22:09:14 -0800 Subject: [PATCH 073/237] [AST/ASTDumper] Fix linker error for `SWIFT_BUILD_ONLY_SYNTAXPARSERLIB` build With such a build we avoid linking the `clangAST` library. --- lib/AST/ASTDumper.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index ae20364d0bb82..dd93348812558 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -3762,6 +3762,10 @@ void Type::dump() const { } void Type::dump(raw_ostream &os, unsigned indent) const { + #if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB + return; // not needed for the parser library. + #endif + PrintType(os, indent).visit(*this, ""); os << "\n"; } From a7d6cd8126147b6aaa7043d02d0d77c1e44dd331 Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Thu, 30 Jan 2020 23:05:29 -0800 Subject: [PATCH 074/237] [Build System: build-script] Adds a new cache_utils module to build_swift which replaces the existing module from swift_build_support. --- utils/build_swift/build_swift/cache_utils.py | 69 ++++++++++ .../tests/build_swift/test_cache_utils.py | 121 ++++++++++++++++++ utils/build_swift/tests/utils.py | 36 +++++- .../swift_build_support/cache_util.py | 58 --------- .../swift_build_support/products/ninja.py | 4 +- .../swift_build_support/toolchain.py | 6 +- .../tests/test_cache_util.py | 99 -------------- 7 files changed, 224 insertions(+), 169 deletions(-) create mode 100644 utils/build_swift/build_swift/cache_utils.py create mode 100644 utils/build_swift/tests/build_swift/test_cache_utils.py delete mode 100644 utils/swift_build_support/swift_build_support/cache_util.py delete mode 100644 utils/swift_build_support/tests/test_cache_util.py diff --git a/utils/build_swift/build_swift/cache_utils.py b/utils/build_swift/build_swift/cache_utils.py new file mode 100644 index 0000000000000..ede762d7d2c78 --- /dev/null +++ b/utils/build_swift/build_swift/cache_utils.py @@ -0,0 +1,69 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +""" +Cache related utitlity functions and decorators. +""" + + +from __future__ import absolute_import, unicode_literals + +import functools + + +__all__ = [ + 'cache', + 'reify', +] + + +def cache(func): + """Decorator that caches result of a function call. + + NOTE: This decorator does not play nice with methods as the created cache + is not instance-local, rather it lives in the decorator. + NOTE: When running in Python 3.2 or newer this decorator is replaced with + the standard `functools.lru_cache` using a maxsize of None. + """ + + # Use the standard functools.lru_cache decorator for Python 3.2 and newer. + if hasattr(functools, 'lru_cache'): + return functools.lru_cache(maxsize=None)(func) + + # Otherwise use a naive caching strategy. + _cache = {} + + @functools.wraps(func) + def wrapper(*args, **kwargs): + key = tuple(args) + tuple(kwargs.items()) + + if key not in _cache: + result = func(*args, **kwargs) + _cache[key] = result + return result + + return _cache[key] + return wrapper + + +def reify(func): + """Decorator that replaces the wrapped method with the result after the + first call. Used to wrap property-like methods with no arguments. + """ + + class wrapper(object): + def __get__(self, obj, type=None): + if obj is None: + return self + + result = func(obj) + setattr(obj, func.__name__, result) + return result + + return functools.update_wrapper(wrapper(), func) diff --git a/utils/build_swift/tests/build_swift/test_cache_utils.py b/utils/build_swift/tests/build_swift/test_cache_utils.py new file mode 100644 index 0000000000000..8dba0d434c49c --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_cache_utils.py @@ -0,0 +1,121 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import unittest + +from build_swift import cache_utils + +from .. import utils + + +try: + # Python 3.3 + from unittest import mock +except ImportError: + mock = None + + +class _CallCounter(object): + """Callable helper class used to count and return the number of times an + instance has been called. + """ + + def __init__(self): + self._counter = 0 + + def __call__(self, *args, **kwargs): + count = self._counter + self._counter += 1 + return count + + +class TestCache(unittest.TestCase): + """Unit tests for the cache decorator in the cache_utils module. + """ + + @utils.requires_module('unittest.mock') + @utils.requires_python('3.2') # functools.lru_cache + def test_replaced_with_functools_lru_cache_python_3_2(self): + with mock.patch('functools.lru_cache') as mock_lru_cache: + @cache_utils.cache + def func(): + return None + + mock_lru_cache.assert_called() + + def test_call_with_no_args(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(), 0) + self.assertEqual(func(), 0) + + def test_call_with_args(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(0), 0) + self.assertEqual(func(0), 0) + + self.assertEqual(func(1), 1) + self.assertEqual(func(1), 1) + + self.assertEqual(func(2), 2) + self.assertEqual(func(2), 2) + + def test_call_with_args_and_kwargs(self): + # Increments the counter once per unique call. + counter = _CallCounter() + + @cache_utils.cache + def func(*args, **kwargs): + return counter(*args, **kwargs) + + self.assertEqual(func(n=0), 0) + self.assertEqual(func(n=0), 0) + + self.assertEqual(func(a=1, b='b'), 1) + self.assertEqual(func(a=1, b='b'), 1) + + self.assertEqual(func(0, x=1, y=2.0), 2) + self.assertEqual(func(0, x=1, y=2.0), 2) + + +class TestReify(unittest.TestCase): + """Unit tests for the reify decorator in the cache_utils module. + """ + + def test_replaces_attr_after_first_call(self): + class Counter(object): + def __init__(self): + self._counter = 0 + + @cache_utils.reify + def count(self): + count = self._counter + self._counter += 1 + return count + + counter = Counter() + + self.assertEqual(counter.count, 0) + self.assertEqual(counter.count, 0) + + # Assert that the count property has been replaced with the constant. + self.assertEqual(getattr(counter, 'count'), 0) diff --git a/utils/build_swift/tests/utils.py b/utils/build_swift/tests/utils.py index c21d8361bb8fd..85fc24350e9ff 100644 --- a/utils/build_swift/tests/utils.py +++ b/utils/build_swift/tests/utils.py @@ -15,9 +15,11 @@ import sys import unittest -from six import StringIO +from build_swift import cache_utils +from build_swift.versions import Version -from swift_build_support.swift_build_support import cache_util +import six +from six import StringIO __all__ = [ @@ -27,6 +29,7 @@ 'requires_attr', 'requires_module', 'requires_platform', + 'requires_python', 'BUILD_SCRIPT_IMPL_PATH', 'BUILD_SWIFT_PATH', @@ -38,6 +41,8 @@ # ----------------------------------------------------------------------------- # Constants +_PYTHON_VERSION = Version(platform.python_version()) + TESTS_PATH = os.path.abspath(os.path.dirname(__file__)) BUILD_SWIFT_PATH = os.path.abspath(os.path.join(TESTS_PATH, os.pardir)) UTILS_PATH = os.path.abspath(os.path.join(BUILD_SWIFT_PATH, os.pardir)) @@ -124,9 +129,10 @@ def __exit__(self, exc_type, exc_value, traceback): sys.stderr = self._old_stdout -@cache_util.cached +@cache_utils.cache def requires_attr(obj, attr): - """ + """Decorator used to skip tests if an object does not have the required + attribute. """ try: @@ -137,7 +143,7 @@ def requires_attr(obj, attr): attr, obj)) -@cache_util.cached +@cache_utils.cache def requires_module(fullname): """Decorator used to skip tests if a module is not imported. """ @@ -148,7 +154,7 @@ def requires_module(fullname): return unittest.skip('Unable to import "{}"'.format(fullname)) -@cache_util.cached +@cache_utils.cache def requires_platform(name): """Decorator used to skip tests if not running on the given platform. """ @@ -157,4 +163,20 @@ def requires_platform(name): return lambda func: func return unittest.skip( - 'Required platform "{}"" does not match system'.format(name)) + 'Required platform "{}" does not match system'.format(name)) + + +@cache_utils.cache +def requires_python(version): + """Decorator used to skip tests if the running Python version is not + greater or equal to the required version. + """ + + if isinstance(version, six.string_types): + version = Version(version) + + if _PYTHON_VERSION >= version: + return lambda func: func + + return unittest.skip( + 'Requires Python version {} or greater'.format(version)) diff --git a/utils/swift_build_support/swift_build_support/cache_util.py b/utils/swift_build_support/swift_build_support/cache_util.py deleted file mode 100644 index 493b558ea9084..0000000000000 --- a/utils/swift_build_support/swift_build_support/cache_util.py +++ /dev/null @@ -1,58 +0,0 @@ -# swift_build_support/cache_util.py -----------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- -""" -Cache related utilities -""" -# ---------------------------------------------------------------------------- - -from functools import update_wrapper - -__all__ = [ - 'cached', - 'reify' -] - - -def cached(func): - """Decorator that caches result of method or function. - - Note: Support method or function. - """ - cache = {} - - def wrapper(*args, **kwargs): - key = tuple(args) + tuple(kwargs.items()) - if key not in cache: - result = func(*args, **kwargs) - cache[key] = result - return result - else: - return cache[key] - - return update_wrapper(wrapper, func) - - -def reify(func): - """Decorator that replaces the wrapped method with the result after the - first call. - - Note: Support method that takes no arguments. - """ - class Wrapper(object): - def __get__(self, obj, objtype=None): - if obj is None: - return self - result = func(obj) - setattr(obj, func.__name__, result) - return result - - return update_wrapper(Wrapper(), func) diff --git a/utils/swift_build_support/swift_build_support/products/ninja.py b/utils/swift_build_support/swift_build_support/products/ninja.py index 0ffd054ccafc4..53ebb15c6e32e 100644 --- a/utils/swift_build_support/swift_build_support/products/ninja.py +++ b/utils/swift_build_support/swift_build_support/products/ninja.py @@ -18,10 +18,10 @@ import platform import sys +from build_swift.build_swift import cache_utils from build_swift.build_swift.wrappers import xcrun from . import product -from .. import cache_util from .. import shell @@ -44,7 +44,7 @@ def __init__(self, product_class, args, toolchain, workspace): self.args = args self.toolchain = toolchain - @cache_util.reify + @cache_utils.reify def ninja_bin_path(self): return os.path.join(self.build_dir, 'ninja') diff --git a/utils/swift_build_support/swift_build_support/toolchain.py b/utils/swift_build_support/swift_build_support/toolchain.py index 1395397a80332..b7c8247374e2a 100644 --- a/utils/swift_build_support/swift_build_support/toolchain.py +++ b/utils/swift_build_support/swift_build_support/toolchain.py @@ -18,10 +18,10 @@ import platform +from build_swift.build_swift import cache_utils from build_swift.build_swift.shell import which from build_swift.build_swift.wrappers import xcrun -from . import cache_util from . import shell @@ -44,7 +44,7 @@ def _register(name, *tool): def _getter(self): return self.find_tool(*tool) _getter.__name__ = name - setattr(Toolchain, name, cache_util.reify(_getter)) + setattr(Toolchain, name, cache_utils.reify(_getter)) if platform.system() == 'Windows': @@ -162,7 +162,7 @@ def __init__(self): suffixes = ['38', '37', '36', '35'] super(FreeBSD, self).__init__(suffixes) - @cache_util.reify + @cache_utils.reify def _release_date(self): """Return the release date for FreeBSD operating system on this host. If the release date cannot be ascertained, return None. diff --git a/utils/swift_build_support/tests/test_cache_util.py b/utils/swift_build_support/tests/test_cache_util.py deleted file mode 100644 index c65f308b48295..0000000000000 --- a/utils/swift_build_support/tests/test_cache_util.py +++ /dev/null @@ -1,99 +0,0 @@ -# tests/test_cache_util.py --------------------------------------*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- - -import unittest - -from swift_build_support import cache_util - - -my_func_called = 0 -my_kfunc_called = 0 - - -@cache_util.cached -def my_func(arg1, arg2): - global my_func_called - my_func_called += 1 - return "my_func_result(%s, %s)" % (arg1, arg2) - - -@cache_util.cached -def my_kfunc(arg1, arg2): - global my_kfunc_called - my_kfunc_called += 1 - return "my_kfunc_result(%s, %s)" % (arg1, arg2) - - -class MyClass(object): - def __init__(self, prop=None): - self.my_method_called = 0 - self.my_prop_called = 0 - self.prop_value = prop - - @cache_util.cached - def my_method(self, arg1, arg2): - self.my_method_called += 1 - return "my_meth_result(%s, %s)" % (arg1, arg2) - - @cache_util.reify - def my_prop(self): - self.my_prop_called += 1 - return "==%s==" % (self.prop_value) - - -class CacheUtilTestCase(unittest.TestCase): - def test_cached_func(self): - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 1) - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 1) - self.assertEqual(my_func("bar", 42), "my_func_result(bar, 42)") - self.assertEqual(my_func_called, 2) - self.assertEqual(my_func("foo", 42), "my_func_result(foo, 42)") - self.assertEqual(my_func_called, 2) - - def test_cached_kwfunc(self): - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 1) - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 1) - self.assertEqual(my_kfunc("bar", arg2=42), "my_kfunc_result(bar, 42)") - self.assertEqual(my_kfunc_called, 2) - self.assertEqual(my_kfunc("foo", arg2=42), "my_kfunc_result(foo, 42)") - self.assertEqual(my_kfunc_called, 2) - - def test_cached_method(self): - obj1 = MyClass() - self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj1.my_method_called, 1) - self.assertEqual(obj1.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj1.my_method_called, 1) - self.assertEqual(obj1.my_method("bar", 12), "my_meth_result(bar, 12)") - self.assertEqual(obj1.my_method_called, 2) - - # Test for instance independency. - obj2 = MyClass() - self.assertEqual(obj2.my_method("foo", 42), "my_meth_result(foo, 42)") - self.assertEqual(obj2.my_method_called, 1) - self.assertEqual(obj1.my_method_called, 2) - - def test_reify(self): - obj1 = MyClass(prop='foo') - self.assertEqual(obj1.my_prop, '==foo==') - self.assertEqual(obj1.my_prop_called, 1) - self.assertEqual(obj1.my_prop, '==foo==') - self.assertEqual(obj1.my_prop_called, 1) - - # Test for instance independency. - obj2 = MyClass(prop='bar') - self.assertEqual(obj2.my_prop, '==bar==') - self.assertEqual(obj1.my_prop, '==foo==') From fce335bf360e439fccab15e729d655565d96b30d Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Fri, 31 Jan 2020 08:52:45 -0800 Subject: [PATCH 075/237] SILGen: Fix withoutActuallyEscaping of 'c' closures They don't have a context and therefore are not consumed. Fixes a failing assert. rdar://59046275 --- lib/SILGen/SILGenExpr.cpp | 12 ++++++++---- test/SILGen/without_actually_escaping.swift | 13 +++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 74f9b04070f75..879c9082c6272 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -5023,13 +5023,17 @@ RValue RValueEmitter::visitMakeTemporarilyEscapableExpr( return visit(E->getSubExpr(), C); }; - // Handle @convention(block). No withoutActuallyEscaping verification yet. - if (silFnTy->getExtInfo().getRepresentation() != - SILFunctionTypeRepresentation::Thick) { + // Handle @convention(block) an @convention(c). No withoutActuallyEscaping + // verification yet. + auto closureRepresentation = silFnTy->getExtInfo().getRepresentation(); + if (closureRepresentation != SILFunctionTypeRepresentation::Thick) { auto escapingClosure = SGF.B.createConvertFunction(E, functionValue, escapingFnTy, /*WithoutActuallyEscaping=*/true); - return visitSubExpr(escapingClosure, true /*isClosureConsumable*/); + bool isBlockConvention = + closureRepresentation == SILFunctionTypeRepresentation::Block; + return visitSubExpr(escapingClosure, + isBlockConvention /*isClosureConsumable*/); } // Convert it to an escaping function value. diff --git a/test/SILGen/without_actually_escaping.swift b/test/SILGen/without_actually_escaping.swift index 2979157df33b5..1d5e1569e627e 100644 --- a/test/SILGen/without_actually_escaping.swift +++ b/test/SILGen/without_actually_escaping.swift @@ -100,3 +100,16 @@ func withoutActuallyEscapingConflict() { modifyAndPerform(&localVar, closure: $0) } } + +// CHECK-LABEL: sil [ossa] @$s25without_actually_escaping0A25ActuallyEscapingCFunction8functionyyyXC_tF +// CHECK: bb0([[ARG:%.*]] : $@convention(c) @noescape () -> ()): +// CHECK: [[E:%.*]] = convert_function [[ARG]] : $@convention(c) @noescape () -> () to [without_actually_escaping] $@convention(c) () -> () +// CHECK: [[F:%.*]] = function_ref @$s25without_actually_escaping0A25ActuallyEscapingCFunction8functionyyyXC_tFyyyXCXEfU_ : $@convention(thin) (@convention(c) () -> ()) -> () +// CHECK: apply [[F]]([[E]]) : $@convention(thin) (@convention(c) () -> ()) -> () +public func withoutActuallyEscapingCFunction(function: (@convention(c) () -> Void)) { + withoutActuallyEscaping(function) { f in + var pointer: UnsafeRawPointer? = nil + pointer = unsafeBitCast(f, to: UnsafeRawPointer.self) + print(pointer) + } +} From 81b4d84ee92b3ec6d71159cc2028e721ac27dd8c Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Thu, 30 Jan 2020 22:47:33 -0800 Subject: [PATCH 076/237] swift-symbolgraph-extract: Mark required args - Mark required arguments - Print help usage on zero args rdar://problem/58770554 --- .../driver/swift_symbolgraph_extract_main.cpp | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/tools/driver/swift_symbolgraph_extract_main.cpp index 1bba65f72a996..a08d29b58fad1 100644 --- a/tools/driver/swift_symbolgraph_extract_main.cpp +++ b/tools/driver/swift_symbolgraph_extract_main.cpp @@ -32,10 +32,10 @@ namespace options { static llvm::cl::OptionCategory Category("swift-symbolgraph-extract Options"); static llvm::cl::opt -ModuleName("module-name", llvm::cl::desc("Name of the module to extract"), llvm::cl::cat(Category)); +ModuleName("module-name", llvm::cl::desc("Name of the module to extract (Required)"), llvm::cl::cat(Category)); static llvm::cl::list -FrameworkSearchPaths("F", llvm::cl::desc("add a directory to the framework search paths"), llvm::cl::ZeroOrMore, +FrameworkSearchPaths("F", llvm::cl::desc("Add a directory to the framework search paths"), llvm::cl::ZeroOrMore, llvm::cl::cat(Category)); static llvm::cl::list @@ -54,7 +54,7 @@ SDK("sdk", llvm::cl::desc("Path to the SDK"), llvm::cl::cat(Category)); static llvm::cl::opt -Target("target", llvm::cl::desc("Target triple"), +Target("target", llvm::cl::desc("Target triple (Required)"), llvm::cl::cat(Category)); static llvm::cl::opt @@ -67,9 +67,29 @@ static llvm::cl::opt MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category)); static llvm::cl::opt -OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category)); +OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path (Required)"), llvm::cl::cat(Category)); } // end namespace options +static bool argumentsAreValid() { + bool Valid = true; + if (options::Target.empty()) { + llvm::errs() << "Required -target option is missing\n"; + Valid = false; + } + + if (options::ModuleName.empty()) { + llvm::errs() << "Required -module-name argument is missing\n"; + Valid = false; + } + + if (options::OutputPath.empty()) { + llvm::errs() << "Required -o argument is missing\n"; + Valid = false; + } + + return Valid; +} + int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv0, void *MainAddr) { INITIALIZE_LLVM(); @@ -79,8 +99,18 @@ int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv SmallVector ArgsWithArgv0 { Argv0 }; ArgsWithArgv0.append(Args.begin(), Args.end()); + if (Args.empty()) { + ArgsWithArgv0.push_back("-help"); + } + llvm::cl::ParseCommandLineOptions(ArgsWithArgv0.size(), - llvm::makeArrayRef(ArgsWithArgv0).data(), "Swift Symbol Graph Extractor\n"); + llvm::makeArrayRef(ArgsWithArgv0).data(), + "Swift Symbol Graph Extractor\n"); + + if (!argumentsAreValid()) { + llvm::cl::PrintHelpMessage(); + return EXIT_FAILURE; + } CompilerInvocation Invocation; From 7030301ae18bc82d0780c23f29a73e5e4676da4a Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Thu, 30 Jan 2020 23:30:11 -0800 Subject: [PATCH 077/237] Pass -Xcc flags through swift-symbolgraph-extract rdar://problem/59055039 --- tools/driver/swift_symbolgraph_extract_main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/driver/swift_symbolgraph_extract_main.cpp b/tools/driver/swift_symbolgraph_extract_main.cpp index 1bba65f72a996..fa8e0e0813998 100644 --- a/tools/driver/swift_symbolgraph_extract_main.cpp +++ b/tools/driver/swift_symbolgraph_extract_main.cpp @@ -66,6 +66,10 @@ PrettyPrint("pretty-print", llvm::cl::desc("Pretty-print the resulting Symbol Gr static llvm::cl::opt MinimumAccessLevel("minimum-access-level", llvm::cl::desc("Include symbols with this access level or more"), llvm::cl::cat(Category)); +static llvm::cl::list +Xcc("Xcc", llvm::cl::desc("Pass the following command-line flag to Clang"), + llvm::cl::cat(Category)); + static llvm::cl::opt OutputPath("o", llvm::cl::desc("Symbol Graph JSON Output Path"), llvm::cl::cat(Category)); } // end namespace options @@ -90,6 +94,10 @@ int swift_symbolgraph_extract_main(ArrayRef Args, const char *Argv Invocation.setSDKPath(options::SDK); Invocation.setTargetTriple(options::Target); + for (auto &Arg : options::Xcc) { + Invocation.getClangImporterOptions().ExtraArgs.push_back(Arg); + } + std::vector FrameworkSearchPaths; for (const auto &Path : options::FrameworkSearchPaths) { FrameworkSearchPaths.push_back({ Path, /*isSystem*/ false}); From 6b9c570b38fbf210213a26651cbe2f17045e7f48 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 31 Jan 2020 15:53:43 -0800 Subject: [PATCH 078/237] build: correct the handling for the static variants The static variants should use the same flags as the dynamic variants. We missed the name conversion in the variable computation causing the static and dynamic builds to diverge. --- utils/build-script-impl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index b5b3264410f36..255ac4beb3844 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1416,7 +1416,7 @@ for host in "${ALL_HOSTS[@]}"; do module_cache="${build_dir}/module-cache" # Add any specific cmake options specified by build-script - product_cmake_options_name=$(to_varname "${product}")_CMAKE_OPTIONS + product_cmake_options_name=$(to_varname "${product/_static}")_CMAKE_OPTIONS product_cmake_options=(${!product_cmake_options_name}) # convert to array cmake_options+=("${product_cmake_options[@]}") From 950c73f47163e2c0c09511dd9109576ebdcf1e31 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 16:19:42 -0800 Subject: [PATCH 079/237] [Serialization] Bump module format version. The change to introduce a "top-level" bit for VarDecls requires a module format version bump, per https://github.com/apple/swift/pull/29024. Fixes rdar://problem/59078925 --- lib/Serialization/ModuleFormat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9c32d255477b4..c2769a9c5ef97 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -55,7 +55,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 534; // add SIL parameter differentiability +const uint16_t SWIFTMODULE_VERSION_MINOR = 535; // top-level var decls /// A standard hash seed used for all string hashes in a serialized module. /// From ab5c682683fd24e7dd70ceee9ed34fb20309aac8 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 31 Jan 2020 16:19:18 -0800 Subject: [PATCH 080/237] Revert "Revert "build: simplify version tracking logic"" This reverts commit be37141b0fc5ad82dafbaaed2936a5a62c4de1e0. Add a small fix in the dereferencing. --- lib/Basic/CMakeLists.txt | 58 ++++++++++++---------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 6cd7577ce3f93..491de85f96bb9 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -11,59 +11,32 @@ else() set(UUID_INCLUDE "${UUID_INCLUDE_DIRS}") endif() -# Figure out if we can track VC revisions. -# FIXME: Copied from Clang. -function(find_first_existing_file out_var) - foreach(file ${ARGN}) - if(EXISTS "${file}") - set(${out_var} "${file}" PARENT_SCOPE) - return() - endif() - endforeach() -endfunction() +function(generate_revision_inc revision_inc_var name dir) + find_first_existing_vc_file("${dir}" ${name}_vc) -macro(find_first_existing_vc_file out_var path) - find_first_existing_file(${out_var} - "${path}/.git/logs/HEAD" # Git - "${path}/.svn/wc.db" # SVN 1.7 - "${path}/.svn/entries" # SVN 1.6 - ) -endmacro() + # Create custom target to generate the VC revision include. + set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") -set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") + set(generate_vcs_version_script "${LLVM_MAIN_SRC_DIR}/cmake/modules/GenerateVersionFromVCS.cmake") -function(generate_revision_inc revision_inc_var name dir) - find_first_existing_vc_file(dep_file "${dir}") - # Create custom target to generate the VC revision include. - set(revision_inc "${CMAKE_CURRENT_BINARY_DIR}/${name}Revision.inc") - string(TOUPPER ${name} upper_name) - if(DEFINED dep_file) - add_custom_command(OUTPUT "${revision_inc}" - DEPENDS "${dep_file}" "${generate_vcs_version_script}" - COMMAND - ${CMAKE_COMMAND} "-DNAMES=${upper_name}" - "-D${upper_name}_SOURCE_DIR=${dir}" - "-DHEADER_FILE=${revision_inc}" - -P "${generate_vcs_version_script}") - else() - # Generate an empty Revision.inc file if we are not using git or SVN. - file(WRITE "${revision_inc}" "") - endif() + add_custom_command(OUTPUT "${version_inc}" + DEPENDS "${${name}_vc}" "${generate_vcs_version_script}" + COMMAND ${CMAKE_COMMAND} "-DNAMES=$" + "-D$_SOURCE_DIR=${dir}" + "-DHEADER_FILE=${version_inc}" + -P "${generate_vcs_version_script}") # Mark the generated header as being generated. - set_source_files_properties("${revision_inc}" + set_source_files_properties("${version_inc}" PROPERTIES GENERATED TRUE HEADER_FILE_ONLY TRUE) - set(${revision_inc_var} ${revision_inc} PARENT_SCOPE) + set(${revision_inc_var} ${version_inc} PARENT_SCOPE) endfunction() generate_revision_inc(llvm_revision_inc LLVM "${LLVM_MAIN_SRC_DIR}") generate_revision_inc(clang_revision_inc Clang "${CLANG_MAIN_SRC_DIR}") generate_revision_inc(swift_revision_inc Swift "${SWIFT_SOURCE_DIR}") -set(version_inc_files - ${llvm_revision_inc} ${clang_revision_inc} ${swift_revision_inc}) - add_swift_host_library(swiftBasic STATIC AnyValue.cpp Cache.cpp @@ -95,7 +68,10 @@ add_swift_host_library(swiftBasic STATIC Unicode.cpp UUID.cpp Version.cpp - ${version_inc_files} + + ${llvm_revision_inc} + ${clang_revision_inc} + ${swift_revision_inc} # Platform-specific TaskQueue implementations Unix/TaskQueue.inc From a92724b09c90f2bc9dfa0a7e5c49bd98ebb5b023 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 31 Jan 2020 15:28:30 -0800 Subject: [PATCH 081/237] AST: Fix computeSelfParam() to respect __consuming on class methods Fixes . --- lib/AST/ASTContext.cpp | 7 ++----- test/SILGen/value_ownership_class.swift | 7 +++++++ test/decl/protocol/protocols.swift | 7 ++++--- 3 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 test/SILGen/value_ownership_class.swift diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ef6969430da40..a5d494d2805df 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2431,7 +2431,8 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isInitializingCtor) { // initializing constructors of value types always have an implicitly // inout self. - selfAccess = SelfAccessKind::Mutating; + if (!containerTy->hasReferenceSemantics()) + selfAccess = SelfAccessKind::Mutating; } else { // allocating constructors have metatype 'self'. isStatic = true; @@ -2459,10 +2460,6 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD, if (isStatic) return AnyFunctionType::Param(MetatypeType::get(selfTy, Ctx)); - // Reference types have 'self' of type T. - if (containerTy->hasReferenceSemantics()) - return AnyFunctionType::Param(selfTy); - auto flags = ParameterTypeFlags(); switch (selfAccess) { case SelfAccessKind::Consuming: diff --git a/test/SILGen/value_ownership_class.swift b/test/SILGen/value_ownership_class.swift new file mode 100644 index 0000000000000..0c8fbe81ea54a --- /dev/null +++ b/test/SILGen/value_ownership_class.swift @@ -0,0 +1,7 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +class ConsumingClass { + __consuming func consumingMethod() {} +} + +// CHECK-LABEL: sil hidden [ossa] @$s21value_ownership_class14ConsumingClassC15consumingMethodyyF : $@convention(method) (@owned ConsumingClass) -> () { diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index f0b6bf4977bc6..2ed52c88bd3b7 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -101,9 +101,10 @@ struct DoesNotConform : Up { // Circular protocols -protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 3 {{protocol 'CircleMiddle' refines itself}} -protocol CircleStart : CircleEnd { func circle_start() } -// expected-note@-1 3 {{protocol 'CircleStart' declared here}} +protocol CircleMiddle : CircleStart { func circle_middle() } // expected-error 2 {{protocol 'CircleMiddle' refines itself}} +// expected-note@-1 {{protocol 'CircleMiddle' declared here}} +protocol CircleStart : CircleEnd { func circle_start() } // expected-error {{protocol 'CircleStart' refines itself}} +// expected-note@-1 2 {{protocol 'CircleStart' declared here}} protocol CircleEnd : CircleMiddle { func circle_end()} // expected-note 3 {{protocol 'CircleEnd' declared here}} protocol CircleEntry : CircleTrivial { } From 62ca2aaf22f76f63ce8924da5e1ff1dbda356d1a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 31 Jan 2020 15:29:11 -0800 Subject: [PATCH 082/237] SIL: Fix an old FIXME --- lib/SIL/SILFunctionType.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 6e0fac061678f..8b667f595bdb2 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -983,10 +983,8 @@ class DestructureInputs { for (auto i : indices(substTupleTy.getElementTypes())) { auto &elt = substTupleTy->getElement(i); auto ownership = elt.getParameterFlags().getValueOwnership(); - // FIXME(swift3): Once the entire parameter list is no longer a - // target for substitution, re-enable this. - // assert(ownership == ValueOwnership::Default); - // assert(!elt.isVararg()); + assert(ownership == ValueOwnership::Default); + assert(!elt.isVararg()); visit(ownership, forSelf, origType.getTupleElementType(i), CanType(elt.getRawType()), rep); From 47cd59b0cc6bdb94638bf18628a864efa5c4c269 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 31 Jan 2020 17:36:20 -0800 Subject: [PATCH 083/237] Remove some unnecessary generalization in exportability checking. I need to diagnose a new kind of thing, and the idea of introducing a third callback when in practiice there's only one implementation (and it's already parameterized by a "reason", no less) seems like it's building on a bad pattern. --- lib/Sema/TypeCheckAccess.cpp | 116 ++++++++++++----------------------- 1 file changed, 39 insertions(+), 77 deletions(-) diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index 716872331ef99..bc6e859d74ab7 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -1445,15 +1445,11 @@ class UsableFromInlineChecker : public AccessControlCheckerBase, }; class ExportabilityChecker : public DeclVisitor { - using CheckExportabilityTypeCallback = - llvm::function_ref; - using CheckExportabilityConformanceCallback = - llvm::function_ref; + class Diagnoser; void checkTypeImpl( Type type, const TypeRepr *typeRepr, const SourceFile &SF, - CheckExportabilityTypeCallback diagnoseType, - CheckExportabilityConformanceCallback diagnoseConformance) { + const Diagnoser &diagnoser) { // Don't bother checking errors. if (type && type->hasError()) return; @@ -1469,7 +1465,7 @@ class ExportabilityChecker : public DeclVisitor { if (!SF.isImportedImplementationOnly(M)) return true; - diagnoseType(component->getBoundDecl(), component); + diagnoser.diagnoseType(component->getBoundDecl(), component); foundAnyIssues = true; // We still continue even in the diagnostic case to report multiple // violations. @@ -1488,22 +1484,17 @@ class ExportabilityChecker : public DeclVisitor { class ProblematicTypeFinder : public TypeDeclFinder { const SourceFile &SF; - CheckExportabilityTypeCallback diagnoseType; - CheckExportabilityConformanceCallback diagnoseConformance; + const Diagnoser &diagnoser; public: - ProblematicTypeFinder( - const SourceFile &SF, - CheckExportabilityTypeCallback diagnoseType, - CheckExportabilityConformanceCallback diagnoseConformance) - : SF(SF), diagnoseType(diagnoseType), - diagnoseConformance(diagnoseConformance) {} + ProblematicTypeFinder(const SourceFile &SF, const Diagnoser &diagnoser) + : SF(SF), diagnoser(diagnoser) {} void visitTypeDecl(const TypeDecl *typeDecl) { ModuleDecl *M = typeDecl->getModuleContext(); if (!SF.isImportedImplementationOnly(M)) return; - diagnoseType(typeDecl, /*typeRepr*/nullptr); + diagnoser.diagnoseType(typeDecl, /*typeRepr*/nullptr); } void visitSubstitutionMap(SubstitutionMap subs) { @@ -1521,7 +1512,7 @@ class ExportabilityChecker : public DeclVisitor { ModuleDecl *M = rootConf->getDeclContext()->getParentModule(); if (!SF.isImportedImplementationOnly(M)) continue; - diagnoseConformance(rootConf); + diagnoser.diagnoseConformance(rootConf); } } @@ -1545,25 +1536,20 @@ class ExportabilityChecker : public DeclVisitor { } }; - type.walk(ProblematicTypeFinder(SF, diagnoseType, diagnoseConformance)); + type.walk(ProblematicTypeFinder(SF, diagnoser)); } void checkType( Type type, const TypeRepr *typeRepr, const Decl *context, - CheckExportabilityTypeCallback diagnoseType, - CheckExportabilityConformanceCallback diagnoseConformance) { + const Diagnoser &diagnoser) { auto *SF = context->getDeclContext()->getParentSourceFile(); assert(SF && "checking a non-source declaration?"); - return checkTypeImpl(type, typeRepr, *SF, diagnoseType, - diagnoseConformance); + return checkTypeImpl(type, typeRepr, *SF, diagnoser); } void checkType( - const TypeLoc &TL, const Decl *context, - CheckExportabilityTypeCallback diagnoseType, - CheckExportabilityConformanceCallback diagnoseConformance) { - checkType(TL.getType(), TL.getTypeRepr(), context, diagnoseType, - diagnoseConformance); + const TypeLoc &TL, const Decl *context, const Diagnoser &diagnoser) { + checkType(TL.getType(), TL.getTypeRepr(), context, diagnoser); } void checkGenericParams(const GenericContext *ownerCtx, @@ -1577,15 +1563,13 @@ class ExportabilityChecker : public DeclVisitor { continue; assert(param->getInherited().size() == 1); checkType(param->getInherited().front(), ownerDecl, - getDiagnoseCallback(ownerDecl), - getDiagnoseCallback(ownerDecl)); + getDiagnoser(ownerDecl)); } forAllRequirementTypes(WhereClauseOwner( const_cast(ownerCtx)), [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ownerDecl, getDiagnoseCallback(ownerDecl), - getDiagnoseCallback(ownerDecl)); + checkType(type, typeRepr, ownerDecl, getDiagnoser(ownerDecl)); }); } @@ -1598,14 +1582,14 @@ class ExportabilityChecker : public DeclVisitor { ExtensionWithConditionalConformances }; - class DiagnoseGenerically { + class Diagnoser { const Decl *D; Reason reason; public: - DiagnoseGenerically(const Decl *D, Reason reason) : D(D), reason(reason) {} + Diagnoser(const Decl *D, Reason reason) : D(D), reason(reason) {} - void operator()(const TypeDecl *offendingType, - const TypeRepr *complainRepr) { + void diagnoseType(const TypeDecl *offendingType, + const TypeRepr *complainRepr) const { ModuleDecl *M = offendingType->getModuleContext(); auto diag = D->diagnose(diag::decl_from_implementation_only_module, offendingType->getDescriptiveKind(), @@ -1614,7 +1598,7 @@ class ExportabilityChecker : public DeclVisitor { highlightOffendingType(diag, complainRepr); } - void operator()(const ProtocolConformance *offendingConformance) { + void diagnoseConformance(const ProtocolConformance *offendingConformance) const { ModuleDecl *M = offendingConformance->getDeclContext()->getParentModule(); D->diagnose(diag::conformance_from_implementation_only_module, offendingConformance->getType(), @@ -1623,18 +1607,8 @@ class ExportabilityChecker : public DeclVisitor { } }; - static_assert( - std::is_convertible::value, - "DiagnoseGenerically has wrong call signature"); - static_assert( - std::is_convertible::value, - "DiagnoseGenerically has wrong call signature for conformance diags"); - - DiagnoseGenerically getDiagnoseCallback(const Decl *D, - Reason reason = Reason::General) { - return DiagnoseGenerically(D, reason); + Diagnoser getDiagnoser(const Decl *D, Reason reason = Reason::General) { + return Diagnoser(D, reason); } public: @@ -1768,7 +1742,7 @@ class ExportabilityChecker : public DeclVisitor { return; checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar, - getDiagnoseCallback(theVar), getDiagnoseCallback(theVar)); + getDiagnoser(theVar)); } /// \see visitPatternBindingDecl @@ -1787,8 +1761,7 @@ class ExportabilityChecker : public DeclVisitor { if (shouldSkipChecking(anyVar)) return; - checkType(TP->getTypeLoc(), anyVar, getDiagnoseCallback(anyVar), - getDiagnoseCallback(anyVar)); + checkType(TP->getTypeLoc(), anyVar, getDiagnoser(anyVar)); } void visitPatternBindingDecl(PatternBindingDecl *PBD) { @@ -1812,25 +1785,22 @@ class ExportabilityChecker : public DeclVisitor { void visitTypeAliasDecl(TypeAliasDecl *TAD) { checkGenericParams(TAD, TAD); checkType(TAD->getUnderlyingType(), - TAD->getUnderlyingTypeRepr(), TAD, getDiagnoseCallback(TAD), - getDiagnoseCallback(TAD)); + TAD->getUnderlyingTypeRepr(), TAD, getDiagnoser(TAD)); } void visitAssociatedTypeDecl(AssociatedTypeDecl *assocType) { llvm::for_each(assocType->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, assocType, getDiagnoseCallback(assocType), - getDiagnoseCallback(assocType)); + checkType(requirement, assocType, getDiagnoser(assocType)); }); checkType(assocType->getDefaultDefinitionType(), assocType->getDefaultDefinitionTypeRepr(), assocType, - getDiagnoseCallback(assocType), getDiagnoseCallback(assocType)); + getDiagnoser(assocType)); if (assocType->getTrailingWhereClause()) { forAllRequirementTypes(assocType, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, assocType, getDiagnoseCallback(assocType), - getDiagnoseCallback(assocType)); + checkType(type, typeRepr, assocType, getDiagnoser(assocType)); }); } } @@ -1840,22 +1810,19 @@ class ExportabilityChecker : public DeclVisitor { llvm::for_each(nominal->getInherited(), [&](TypeLoc nextInherited) { - checkType(nextInherited, nominal, getDiagnoseCallback(nominal), - getDiagnoseCallback(nominal)); + checkType(nextInherited, nominal, getDiagnoser(nominal)); }); } void visitProtocolDecl(ProtocolDecl *proto) { llvm::for_each(proto->getInherited(), [&](TypeLoc requirement) { - checkType(requirement, proto, getDiagnoseCallback(proto), - getDiagnoseCallback(proto)); + checkType(requirement, proto, getDiagnoser(proto)); }); if (proto->getTrailingWhereClause()) { forAllRequirementTypes(proto, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, proto, getDiagnoseCallback(proto), - getDiagnoseCallback(proto)); + checkType(type, typeRepr, proto, getDiagnoser(proto)); }); } } @@ -1865,10 +1832,9 @@ class ExportabilityChecker : public DeclVisitor { for (auto &P : *SD->getIndices()) { checkType(P->getInterfaceType(), P->getTypeRepr(), SD, - getDiagnoseCallback(SD), getDiagnoseCallback(SD)); + getDiagnoser(SD)); } - checkType(SD->getElementTypeLoc(), SD, getDiagnoseCallback(SD), - getDiagnoseCallback(SD)); + checkType(SD->getElementTypeLoc(), SD, getDiagnoser(SD)); } void visitAbstractFunctionDecl(AbstractFunctionDecl *fn) { @@ -1876,13 +1842,12 @@ class ExportabilityChecker : public DeclVisitor { for (auto *P : *fn->getParameters()) checkType(P->getInterfaceType(), P->getTypeRepr(), fn, - getDiagnoseCallback(fn), getDiagnoseCallback(fn)); + getDiagnoser(fn)); } void visitFuncDecl(FuncDecl *FD) { visitAbstractFunctionDecl(FD); - checkType(FD->getBodyResultTypeLoc(), FD, getDiagnoseCallback(FD), - getDiagnoseCallback(FD)); + checkType(FD->getBodyResultTypeLoc(), FD, getDiagnoser(FD)); } void visitEnumElementDecl(EnumElementDecl *EED) { @@ -1890,7 +1855,7 @@ class ExportabilityChecker : public DeclVisitor { return; for (auto &P : *EED->getParameterList()) checkType(P->getInterfaceType(), P->getTypeRepr(), EED, - getDiagnoseCallback(EED), getDiagnoseCallback(EED)); + getDiagnoser(EED)); } void checkConstrainedExtensionRequirements(ExtensionDecl *ED, @@ -1898,8 +1863,7 @@ class ExportabilityChecker : public DeclVisitor { if (!ED->getTrailingWhereClause()) return; forAllRequirementTypes(ED, [&](Type type, TypeRepr *typeRepr) { - checkType(type, typeRepr, ED, getDiagnoseCallback(ED, reason), - getDiagnoseCallback(ED, reason)); + checkType(type, typeRepr, ED, getDiagnoser(ED, reason)); }); } @@ -1913,8 +1877,7 @@ class ExportabilityChecker : public DeclVisitor { // but just hide that from interfaces. llvm::for_each(ED->getInherited(), [&](TypeLoc nextInherited) { - checkType(nextInherited, ED, getDiagnoseCallback(ED), - getDiagnoseCallback(ED)); + checkType(nextInherited, ED, getDiagnoser(ED)); }); bool hasPublicMembers = llvm::any_of(ED->getMembers(), @@ -1927,8 +1890,7 @@ class ExportabilityChecker : public DeclVisitor { if (hasPublicMembers) { checkType(ED->getExtendedType(), ED->getExtendedTypeRepr(), ED, - getDiagnoseCallback(ED, Reason::ExtensionWithPublicMembers), - getDiagnoseCallback(ED, Reason::ExtensionWithPublicMembers)); + getDiagnoser(ED, Reason::ExtensionWithPublicMembers)); } if (hasPublicMembers || !ED->getInherited().empty()) { From bf124b8a4a24d14f8a5bb01300f379583966495d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 17:55:15 -0800 Subject: [PATCH 084/237] [Type checker] Use typeCheckCondition() rather than fake it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Type checking an expression pattern had a hack to work around some diagnostics issues with conditions. These issues have been addressed by the new diagnostic infrastructure, so remove this unnecessary use of ExprTypeCheckListener and check the expression pattern condition using… typeCheckCondition(). --- lib/Sema/TypeCheckConstraints.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 0a9f90509187a..1a5e16abeff3f 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -3209,30 +3209,7 @@ bool TypeChecker::typeCheckExprPattern(ExprPattern *EP, DeclContext *DC, /*Implicit=*/true); // Check the expression as a condition. - // - // TODO: Type-check of `~=` operator can't (yet) use `typeCheckCondition` - // because that utilizes contextual type which interferes with diagnostics. - // We don't yet have a full access to pattern-matching context in - // constraint system, which is required to enable these situations - // to be properly diagnosed. - struct ConditionListener : public ExprTypeCheckListener { - // Add the appropriate Boolean constraint. - bool builtConstraints(ConstraintSystem &cs, Expr *expr) override { - // Otherwise, the result must be convertible to Bool. - auto boolDecl = cs.getASTContext().getBoolDecl(); - if (!boolDecl) - return true; - - // Condition must convert to Bool. - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), - boolDecl->getDeclaredType(), - cs.getConstraintLocator(expr)); - return false; - } - }; - - ConditionListener listener; - bool hadError = !typeCheckExpression(matchCall, DC, &listener); + bool hadError = typeCheckCondition(matchCall, DC); // Save the type-checked expression in the pattern. EP->setMatchExpr(matchCall); // Set the type on the pattern. From 4b0e7b27808247e63be70c8f46abf71e4583cc19 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 18:20:46 -0800 Subject: [PATCH 085/237] [Type checker] Sink logic for @autoclosure default parameters into the solver Rather than use an ExprTypeCheckListener subclass to introduce the autoclosure expression, do it at the end of solving. --- lib/Sema/CSDiagnostics.cpp | 3 +++ lib/Sema/CSSimplify.cpp | 1 + lib/Sema/TypeCheckConstraints.cpp | 44 +++++++++++++------------------ lib/Sema/TypeChecker.h | 4 +++ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b85e37691bb76..c0ff3daf3f172 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -560,6 +560,7 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( case CTP_ReturnSingleExpr: return diag::cannot_convert_to_return_type; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return diag::cannot_convert_default_arg_value; case CTP_YieldByValue: return diag::cannot_convert_yield_value; @@ -2085,6 +2086,7 @@ getContextualNilDiagnostic(ContextualTypePurpose CTP) { case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value_nil; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return diag::cannot_convert_default_arg_value_nil; case CTP_YieldByValue: return diag::cannot_convert_yield_value_nil; @@ -2863,6 +2865,7 @@ ContextualFailure::getDiagnosticFor(ContextualTypePurpose context, case CTP_EnumCaseRawValue: return diag::cannot_convert_raw_initializer_value; case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: return forProtocol ? diag::cannot_convert_default_arg_value_protocol : diag::cannot_convert_default_arg_value; case CTP_YieldByValue: diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 09a1c7eda4973..e017aeb2cee63 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9130,6 +9130,7 @@ void ConstraintSystem::addContextualConversionConstraint( case CTP_ThrowStmt: case CTP_EnumCaseRawValue: case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: case CTP_ClosureResult: case CTP_DictionaryKey: case CTP_DictionaryValue: diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1a5e16abeff3f..d1ca555020aea 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2107,6 +2107,14 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, } } + // For an @autoclosure default parameter, we want to convert to the result + // type. Stash the autoclosure default parameter type. + FunctionType *autoclosureDefaultParamType = nullptr; + if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { + autoclosureDefaultParamType = convertType.getType()->castTo(); + convertType.setType(autoclosureDefaultParamType->getResult()); + } + // Tell the constraint system what the contextual type is. This informs // diagnostics and is a hint for various performance optimizations. // FIXME: Look through LoadExpr. This is an egregious hack due to the @@ -2130,6 +2138,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType; Type convertTo = convertType.getType(); + if (options.contains(TypeCheckExprFlags::ExpressionTypeMustBeOptional)) { assert(!convertTo && "convertType and type check options conflict"); auto *convertTypeLocator = @@ -2175,6 +2184,12 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, } result = resultTarget->getAsExpr(); + // For an @autoclosure default parameter type, add the autoclosure + // conversion. + if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { + result = cs.buildAutoClosureExpr(result, autoclosureDefaultParamType); + } + // Notify listener that we've applied the solution. if (listener) result = listener->appliedSolution(solution, result); @@ -2204,32 +2219,9 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC, Type paramType, bool isAutoClosure) { assert(paramType && !paramType->hasError()); - - if (isAutoClosure) { - class AutoClosureListener : public ExprTypeCheckListener { - FunctionType *ParamType; - - public: - AutoClosureListener(FunctionType *paramType) - : ParamType(paramType) {} - - Expr *appliedSolution(constraints::Solution &solution, - Expr *expr) override { - auto &cs = solution.getConstraintSystem(); - return cs.buildAutoClosureExpr(expr, ParamType); - } - }; - - auto *fnType = paramType->castTo(); - AutoClosureListener listener(fnType); - return typeCheckExpression(defaultValue, DC, - TypeLoc::withoutLoc(fnType->getResult()), - CTP_DefaultParameter, TypeCheckExprOptions(), - &listener); - } - - return typeCheckExpression(defaultValue, DC, TypeLoc::withoutLoc(paramType), - CTP_DefaultParameter); + return typeCheckExpression( + defaultValue, DC, TypeLoc::withoutLoc(paramType), + isAutoClosure ? CTP_AutoclosureDefaultParameter : CTP_DefaultParameter); } Type TypeChecker:: diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 3c643d706e25e..341f00a9b0e63 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -133,6 +133,10 @@ enum ContextualTypePurpose { CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum. CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'. + /// Default value in @autoclosure parameter + /// 'foo(a : @autoclosure () -> Int = 42)'. + CTP_AutoclosureDefaultParameter, + CTP_CalleeResult, ///< Constraint is placed on the result of a callee. CTP_CallArgument, ///< Call to function or operator requires type. CTP_ClosureResult, ///< Closure result expects a specific type. From e056d46ba42da7338b7ef9672739694874b06b3c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 20:54:32 -0800 Subject: [PATCH 086/237] [Type checker] Remove unused overload of typeCheckExpression(). --- lib/Sema/TypeChecker.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 341f00a9b0e63..93d9e36e79d3f 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -847,12 +847,6 @@ class TypeChecker final { ExprTypeCheckListener *listener = nullptr, constraints::ConstraintSystem *baseCS = nullptr); - static Type typeCheckExpression(Expr *&expr, DeclContext *dc, - ExprTypeCheckListener *listener) { - return TypeChecker::typeCheckExpression(expr, dc, TypeLoc(), CTP_Unused, - TypeCheckExprOptions(), listener); - } - /// Type check the given expression and return its type without /// applying the solution. /// From 9fa03cd6480db89a940730446562a7bafb4406de Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:11:16 -0800 Subject: [PATCH 087/237] [Type checker] Eliminate TypeCheckExprFlags::ConvertTypeIsOnlyAHint. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag is recoverable from the contextual type purpose; don’t duplicate the logic. --- lib/Sema/ConstraintSystem.h | 2 +- lib/Sema/TypeCheckConstraints.cpp | 41 ++++++++++++++++++++++++++++--- lib/Sema/TypeChecker.h | 14 +++-------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index e5383f0a7e518..f18bbd8cc8ff4 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -789,7 +789,7 @@ using OpenedTypeMap = /// Describes contextual type information about a particular expression /// within a constraint system. -struct ContextualTypeInfo { +struct ContextualTypeInfo { TypeLoc typeLoc; ContextualTypePurpose purpose; bool isOpaqueReturnType = false; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d1ca555020aea..1244e32537770 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2058,6 +2058,40 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } +/// Whether the contextual type provided for the given purpose is only a +/// hint, and not a requirement. +static bool contextualTypeIsOnlyAHint(ContextualTypePurpose ctp, + TypeCheckExprOptions options) { + switch (ctp) { + case CTP_Initialization: + return !options.contains( + TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType); + case CTP_ForEachStmt: + return true; + case CTP_Unused: + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_YieldByValue: + case CTP_YieldByReference: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: + case CTP_CalleeResult: + case CTP_CallArgument: + case CTP_ClosureResult: + case CTP_ArrayElement: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_AssignSource: + case CTP_SubscriptAssignSource: + case CTP_Condition: + case CTP_CannotFail: + return false; + } +} + #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType, @@ -2128,7 +2162,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // If the convertType is *only* provided for that hint, then null it out so // that we don't later treat it as an actual conversion constraint. - if (options.contains(TypeCheckExprFlags::ConvertTypeIsOnlyAHint)) + if (contextualTypeIsOnlyAHint(convertTypePurpose, options)) convertType = TypeLoc(); // If the client can handle unresolved type variables, leave them in the @@ -2655,7 +2689,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, TypeLoc contextualType; auto contextualPurpose = CTP_Unused; - TypeCheckExprOptions flags = TypeCheckExprFlags::ConvertTypeIsOnlyAHint; + TypeCheckExprOptions flags = None; // Set the contextual purpose even if the pattern doesn't have a type so // if there's an error we can use that information to inform diagnostics. @@ -2674,7 +2708,6 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // opaque type. if (auto opaqueType = patternType->getAs()){ flags |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; - flags -= TypeCheckExprFlags::ConvertTypeIsOnlyAHint; } // Only provide a TypeLoc if it makes sense to allow diagnostics. @@ -3042,7 +3075,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Type-check the for-each loop sequence and element pattern. auto resultTy = TypeChecker::typeCheckExpression( seq, dc, TypeLoc::withoutLoc(sequenceProto->getDeclaredType()), - CTP_ForEachStmt, TypeCheckExprFlags::ConvertTypeIsOnlyAHint, &listener); + CTP_ForEachStmt, None, &listener); if (!resultTy) return true; return false; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 93d9e36e79d3f..e24a6a7e879c0 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -174,12 +174,6 @@ enum class TypeCheckExprFlags { /// left in-tact. AllowUnresolvedTypeVariables = 0x08, - /// If set, the 'convertType' specified to typeCheckExpression should not - /// produce a conversion constraint, but it should be used to guide the - /// solution in terms of performance optimizations of the solver, and in terms - /// of guiding diagnostics. - ConvertTypeIsOnlyAHint = 0x10, - /// If set, this expression isn't embedded in a larger expression or /// statement. This should only be used for syntactic restrictions, and should /// not affect type checking itself. @@ -821,11 +815,9 @@ class TypeChecker final { /// to be possible. /// /// \param convertType The type that the expression is being converted to, - /// or null if the expression is standalone. If the 'ConvertTypeIsOnlyAHint' - /// option is specified, then this is only a hint, it doesn't produce a full - /// conversion constraint. The location information is only used for - /// diagnostics should the conversion fail; it is safe to pass a TypeLoc - /// without location information. + /// or null if the expression is standalone. The location information is + /// only used for diagnostics should the conversion fail; it is safe to pass + /// a TypeLoc without location information. /// /// \param options Options that control how type checking is performed. /// From 25c2e02bc9cd7a8c77b74b152bae97eb3584a992 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:20:14 -0800 Subject: [PATCH 088/237] [Type checker] Eliminate TypeCheckExprFlags::DisableStructuralChecks. This is always set along with SubExpressionDiagnostics, so use that for both purposes. That flag will go away when `CSDiag.cpp` goes away. --- lib/Sema/CSDiag.cpp | 9 ++------- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeChecker.h | 4 ---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index ddb0d6d86c42d..5ded14fcf79c8 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -394,15 +394,10 @@ Expr *FailureDiagnosis::typeCheckChildIndependently( // type check operation. Expr *preCheckedExpr = subExpr; - // Disable structural checks, because we know that the overall expression - // has type constraint problems, and we don't want to know about any - // syntactic issues in a well-typed subexpression (which might be because - // the context is missing). - TypeCheckExprOptions TCEOptions = TypeCheckExprFlags::DisableStructuralChecks; - // Make sure that typechecker knows that this is an attempt // to diagnose a problem. - TCEOptions |= TypeCheckExprFlags::SubExpressionDiagnostics; + TypeCheckExprOptions TCEOptions = + TypeCheckExprFlags::SubExpressionDiagnostics; // Claim that the result is discarded to preserve the lvalue type of // the expression. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1244e32537770..2e89c44c8f7a4 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2240,7 +2240,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // Unless the client has disabled them, perform syntactic checks on the // expression now. if (!cs.shouldSuppressDiagnostics() && - !options.contains(TypeCheckExprFlags::DisableStructuralChecks)) { + !options.contains(TypeCheckExprFlags::SubExpressionDiagnostics)) { bool isExprStmt = options.contains(TypeCheckExprFlags::IsExprStmt); performSyntacticExprDiagnostics(result, dc, isExprStmt); } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e24a6a7e879c0..e88546ac33913 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -163,10 +163,6 @@ enum class TypeCheckExprFlags { /// disables constraints forcing an lvalue result to be loadable. IsDiscarded = 0x01, - /// Whether the client wants to disable the structural syntactic restrictions - /// that we force for style or other reasons. - DisableStructuralChecks = 0x02, - /// If set, the client wants a best-effort solution to the constraint system, /// but can tolerate a solution where all of the constraints are solved, but /// not all type variables have been determined. In this case, the constraint From a493004a1394161c1f3e8f5b2f79dcf99c06f3f1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:40:21 -0800 Subject: [PATCH 089/237] [Type checker] Remove unused IsInOutYield flag. --- lib/Sema/TypeChecker.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e88546ac33913..47e4aef224621 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -175,9 +175,6 @@ enum class TypeCheckExprFlags { /// not affect type checking itself. IsExprStmt = 0x20, - /// This is an inout yield. - IsInOutYield = 0x100, - /// If set, a conversion constraint should be specified so that the result of /// the expression is an optional type. ExpressionTypeMustBeOptional = 0x200, From c5ed8d67a981c5af62f501bf086f4f07166f9b37 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 21:40:39 -0800 Subject: [PATCH 090/237] [Constraint solver] Fix tautological assert. --- lib/Sema/CSDiagnostics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index c0ff3daf3f172..6f0a4f38a3c6c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -641,7 +641,7 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() { switch (last.getKind()) { case ConstraintLocator::ContextualType: { auto purpose = getContextualTypePurpose(); - assert(!(purpose == CTP_Unused && purpose == CTP_CannotFail)); + assert(!(purpose == CTP_Unused || purpose == CTP_CannotFail)); // If this is call to a closure e.g. `let _: A = { B() }()` // let's point diagnostic to its result. From 3190433d58cd8b4fde573792d15894200379591c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Jan 2020 22:03:13 -0800 Subject: [PATCH 091/237] [Constraint system] Give SolutionApplicationTarget purpose. A contextual type purpose, that is, so it captures more about what the entity is that is being solved. --- lib/Sema/ConstraintSystem.h | 33 ++++++++++++++++++++++++++++--- lib/Sema/TypeCheckConstraints.cpp | 8 +++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index f18bbd8cc8ff4..96c87701064b8 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1149,8 +1149,11 @@ class SolutionApplicationTarget { struct { Expr *expression; + /// The purpose of the contextual type. + ContextualTypePurpose contextualPurpose; + /// The type to which the expression should be converted. - Type convertType; + TypeLoc convertType; /// Whether the expression result will be discarded at the end. bool isDiscarded; @@ -1163,9 +1166,18 @@ class SolutionApplicationTarget { }; public: - SolutionApplicationTarget(Expr *expr, Type convertType, bool isDiscarded) { + SolutionApplicationTarget(Expr *expr, + ContextualTypePurpose contextualPurpose, + Type convertType, bool isDiscarded) + : SolutionApplicationTarget(expr, contextualPurpose, + TypeLoc::withoutLoc(convertType), + isDiscarded) { } + + SolutionApplicationTarget(Expr *expr, ContextualTypePurpose contextualPurpose, + TypeLoc convertType, bool isDiscarded) { kind = Kind::expression; expression.expression = expr; + expression.contextualPurpose = contextualPurpose; expression.convertType = convertType; expression.isDiscarded = isDiscarded; } @@ -1189,16 +1201,31 @@ class SolutionApplicationTarget { } } + ContextualTypePurpose getExprContextualTypePurpose() const { + assert(kind == Kind::expression); + return expression.contextualPurpose; + } + Type getExprConversionType() const { + assert(kind == Kind::expression); + return expression.convertType.getType(); + } + + TypeLoc getExprConversionTypeLoc() const { assert(kind == Kind::expression); return expression.convertType; } void setExprConversionType(Type type) { + assert(kind == Kind::expression); + expression.convertType = TypeLoc::withoutLoc(type); + } + + void setExprConversionTypeLoc(TypeLoc type) { assert(kind == Kind::expression); expression.convertType = type; } - + bool isDiscardedExpr() const { assert(kind == Kind::expression); return expression.isDiscarded; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 2e89c44c8f7a4..d81a3936ecd85 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2183,7 +2183,7 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, // Attempt to solve the constraint system. SolutionApplicationTarget target( - expr, convertTo, + expr, convertTypePurpose, convertTo, options.contains(TypeCheckExprFlags::IsDiscarded)); auto viable = cs.solve(target, listener, allowFreeTypeVariables); if (!viable) @@ -2283,7 +2283,8 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, // re-check. if (needClearType) expr->setType(Type()); - SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + SolutionApplicationTarget target( + expr, CTP_Unused, Type(), /*isDiscarded=*/false); auto viable = cs.solve(target, listener, allowFreeTypeVariables); if (!viable) { recoverOriginalType(); @@ -2363,7 +2364,8 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( if (originalType && originalType->hasError()) expr->setType(Type()); - SolutionApplicationTarget target(expr, Type(), /*isDiscarded=*/false); + SolutionApplicationTarget target( + expr, CTP_Unused, Type(), /*isDiscarded=*/false); if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { expr = target.getAsExpr(); for (auto &solution : *viable) { From 60522887d9dcfd2bee2422834523b2422e747a4f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 31 Jan 2020 16:17:49 -0800 Subject: [PATCH 092/237] [Sema/CSApply] Make sure that the member type references created for a `type(of: x).A` expression are visible to the `SourceEntityWalker` For code like ``` _ = type(of: x).A.self ``` the `A` type reference is written explicitely by the user, but the AST created using `TypeExpr::createImplicitHack` is hiding such a reference, making the reference invisible to semantic functionality like 'rename'. rdar://56885871 --- lib/Sema/CSApply.cpp | 6 ++---- test/Index/expressions.swift | 13 +++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 443f67d35fd29..045a6e73ec454 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -845,11 +845,9 @@ namespace { // If we're referring to a member type, it's just a type // reference. - if (isa(member)) { + if (auto *TD = dyn_cast(member)) { Type refType = simplifyType(openedType); - auto ref = - TypeExpr::createImplicitHack(memberLoc.getBaseNameLoc(), - refType, context); + auto ref = TypeExpr::createForDecl(memberLoc, TD, cs.DC, /*isImplicit=*/false); cs.setType(ref, refType); auto *result = new (context) DotSyntaxBaseIgnoredExpr( base, dotLoc, ref, refType); diff --git a/test/Index/expressions.swift b/test/Index/expressions.swift index e156ee866f6a3..c92fb142fd9f5 100644 --- a/test/Index/expressions.swift +++ b/test/Index/expressions.swift @@ -45,3 +45,16 @@ func test1() { // CHECK: [[@LINE]]:6 | function/Swift | test1() | [[test1_USR:.* } } } + +protocol AP { + // CHECK: [[@LINE+1]]:18 | type-alias/associated-type/Swift | A | [[AP_P_USR:.*]] | Def,RelChild | rel: 1 + associatedtype A +} +// CHECK: [[@LINE+1]]:19 | param/Swift | x | [[TEST2_X_USR:.*]] | Def,RelChild | rel: 1 +func test2(x: X) { + // CHECK: [[@LINE+1]]:9 | type-alias/associated-type/Swift | A | [[AP_P_USR]] | Ref,RelCont | rel: 1 + _ = X.A.self + // CHECK: [[@LINE+2]]:16 | param/Swift | x | [[TEST2_X_USR]] | Ref,Read,RelCont | rel: 1 + // CHECK: [[@LINE+1]]:19 | type-alias/associated-type/Swift | A | [[AP_P_USR]] | Ref,RelCont | rel: 1 + _ = type(of: x).A.self +} From b7b0a25acb78373f6a58353df716a9954d921504 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 25 Jan 2020 16:27:06 -0800 Subject: [PATCH 093/237] build: remove meaningless dependency --- lib/Option/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Option/CMakeLists.txt b/lib/Option/CMakeLists.txt index bb58724f945e6..70280746f2b28 100644 --- a/lib/Option/CMakeLists.txt +++ b/lib/Option/CMakeLists.txt @@ -1,7 +1,6 @@ add_swift_host_library(swiftOption STATIC Options.cpp - SanitizerOptions.cpp - FILE_DEPENDS SwiftOptions) + SanitizerOptions.cpp) add_dependencies(swiftOption SwiftOptions) target_link_libraries(swiftOption PRIVATE From c964d184678029d68c63db970bf8d09fb83bf998 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 1 Feb 2020 13:39:55 -0800 Subject: [PATCH 094/237] Add target variant to -print-target-info --- lib/FrontendTool/FrontendTool.cpp | 39 ++++++++++++++++++++--------- test/Driver/print_target_info.swift | 24 ++++++++++++++++++ 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 02386f37e7102..912ce5cc572f8 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1894,39 +1894,54 @@ createJSONFixItDiagnosticConsumerIfNeeded( }); } -/// Print information about the selected target in JSON. -static void printTargetInfo(const CompilerInvocation &invocation, +/// Print information about the target triple in JSON. +static void printTripleInfo(const llvm::Triple &triple, llvm::raw_ostream &out) { out << "{\n"; - // Target information. - auto &langOpts = invocation.getLangOptions(); - out << " \"target\": {\n"; - out << " \"triple\": \""; - out.write_escaped(langOpts.Target.getTriple()); + out.write_escaped(triple.getTriple()); out << "\",\n"; out << " \"unversionedTriple\": \""; - out.write_escaped(getUnversionedTriple(langOpts.Target).getTriple()); + out.write_escaped(getUnversionedTriple(triple).getTriple()); out << "\",\n"; out << " \"moduleTriple\": \""; - out.write_escaped(getTargetSpecificModuleTriple(langOpts.Target).getTriple()); + out.write_escaped(getTargetSpecificModuleTriple(triple).getTriple()); out << "\",\n"; if (auto runtimeVersion = getSwiftRuntimeCompatibilityVersionForTarget( - langOpts.Target)) { + triple)) { out << " \"swiftRuntimeCompatibilityVersion\": \""; out.write_escaped(runtimeVersion->getAsString()); out << "\",\n"; } out << " \"librariesRequireRPath\": " - << (tripleRequiresRPathForSwiftInOS(langOpts.Target) ? "true" : "false") + << (tripleRequiresRPathForSwiftInOS(triple) ? "true" : "false") << "\n"; - out << " },\n"; + out << " }"; + +} + +/// Print information about the selected target in JSON. +static void printTargetInfo(const CompilerInvocation &invocation, + llvm::raw_ostream &out) { + out << "{\n"; + + // Target triple and target variant triple. + auto &langOpts = invocation.getLangOptions(); + out << " \"target\": "; + printTripleInfo(langOpts.Target, out); + out << ",\n"; + + if (auto &variant = langOpts.TargetVariant) { + out << " \"targetVariant\": "; + printTripleInfo(*variant, out); + out << ",\n"; + } // Various paths. auto &searchOpts = invocation.getSearchPathOptions(); diff --git a/test/Driver/print_target_info.swift b/test/Driver/print_target_info.swift index 28a65dea78d06..301473f984747 100644 --- a/test/Driver/print_target_info.swift +++ b/test/Driver/print_target_info.swift @@ -4,6 +4,9 @@ // RUN: %swift_driver -print-target-info -target x86_64-unknown-linux | %FileCheck -check-prefix CHECK-LINUX %s // RUN: %target-swift-frontend -print-target-info -target x86_64-unknown-linux | %FileCheck -check-prefix CHECK-LINUX %s +// RUN: %swift_driver -print-target-info -target x86_64-apple-macosx10.15 -target-variant x86_64-apple-ios13-macabi | %FileCheck -check-prefix CHECK-ZIPPERED %s +// RUN: %target-swift-frontend -print-target-info -target x86_64-apple-macosx10.15 -target-variant x86_64-apple-ios13-macabi | %FileCheck -check-prefix CHECK-ZIPPERED %s + // CHECK-IOS: "target": { // CHECK-IOS: "triple": "arm64-apple-ios12.0", // CHECK-IOS: "unversionedTriple": "arm64-apple-ios", @@ -12,6 +15,8 @@ // CHECK-IOS: "librariesRequireRPath": true // CHECK-IOS: } +// CHECK-IOS-NOT: "targetVariant": + // CHECK-IOS: "paths": { // CHECK-IOS: "runtimeLibraryPaths": [ // CHECK-IOS: ], @@ -26,3 +31,22 @@ // CHECK-LINUX: "moduleTriple": "x86_64-unknown-linux", // CHECK-LINUX: "librariesRequireRPath": false // CHECK-LINUX: } + +// CHECK-LINUX-NOT: "targetVariant": + + +// CHECK-ZIPPERED: "target": { +// CHECK-ZIPPERED: "triple": "x86_64-apple-macosx10.15" +// CHECK-ZIPPERED: "unversionedTriple": "x86_64-apple-macosx" +// CHECK-ZIPPERED: "moduleTriple": "x86_64-apple-macos" +// CHECK-ZIPPERED: "swiftRuntimeCompatibilityVersion": "5.1" +// CHECK-ZIPPERED: "librariesRequireRPath": false +// CHECK-ZIPPERED: } + +// CHECK-ZIPPERED: "targetVariant": { +// CHECK-ZIPPERED: "triple": "x86_64-apple-ios13-macabi" +// CHECK-ZIPPERED: "unversionedTriple": "x86_64-apple-ios-macabi" +// CHECK-ZIPPERED: "moduleTriple": "x86_64-apple-ios-macabi" +// CHECK-ZIPPERED: "swiftRuntimeCompatibilityVersion": "5.1" +// CHECK-ZIPPERED: "librariesRequireRPath": false +// CHECK-ZIPPERED: } From 161061f8a36f1577aedcce078eaf80719af583bc Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Sat, 1 Feb 2020 15:33:21 -0800 Subject: [PATCH 095/237] Pass target-variant through the driver for -print-target-info --- lib/Driver/Driver.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index cac9af11f2be9..d1f55a7cdc942 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2176,6 +2176,11 @@ bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) { commandLine.push_back("-target"); commandLine.push_back(targetArg->getValue()); } + if (const Arg *targetVariantArg = + Args.getLastArg(options::OPT_target_variant)) { + commandLine.push_back("-target-variant"); + commandLine.push_back(targetVariantArg->getValue()); + } if (const Arg *sdkArg = Args.getLastArg(options::OPT_sdk)) { commandLine.push_back("-sdk"); commandLine.push_back(sdkArg->getValue()); @@ -2188,6 +2193,7 @@ bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) { std::string executable = getSwiftProgramPath(); + // FIXME: This bypasses mechanisms like -v and -###. (SR-12119) sys::TaskQueue queue; queue.addTask(executable.c_str(), commandLine); queue.execute(nullptr, From ec46f5b987300d42325ca1b556dd2bcdb1dad340 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 1 Feb 2020 16:14:26 -0800 Subject: [PATCH 096/237] docs: update the repository name (NFC) swift-windows was renamed to swift-build. Update the repository name to reflect that. --- docs/AndroidBuild.md | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/docs/AndroidBuild.md b/docs/AndroidBuild.md index 03d5f9eddad27..c45f77bec0644 100644 --- a/docs/AndroidBuild.md +++ b/docs/AndroidBuild.md @@ -11,9 +11,9 @@ Windows. 1. Configure git to work with Unix file endings 1. Clone `apple/swift-llvm` into a directory named `llvm` 1. Clone `apple/swift-corelibs-libdispatch` into a directory named `swift-corelibs-libdispatch` -1. Clone `apple/swift-corelibs-foundation` into a directory named `swift-corelibs-foundation`G +1. Clone `apple/swift-corelibs-foundation` into a directory named `swift-corelibs-foundation` 1. Clone `apple/swift-corelibs-xctest` into a directory named `swift-corelibs-xctest` -1. Clone `compnerd/swift-windows` into a directory named `swift-windows` +1. Clone `compnerd/swift-build` into a directory named `swift-build` - Currently, other repositories in the Swift project have not been tested and may not be supported. @@ -31,28 +31,27 @@ git clone https://github.com/apple/swift-llvm llvm git clone https://github.com/apple/swift-corelibs-libdispatch swift-corelibs-libdispatch git clone https://github.com/apple/swift-corelibs-foundation swift-corelibs-foundation git clone https://github.com/apple/swift-corelibs-xctest swift-corelibs-xctest -git clone https://github.com/compnerd/swift-windows swift-windows +git clone https://github.com/compnerd/swift-build swift-build ``` ## 1. Acquire the lastest toolchain and dependencies 1. Download the toolchain, ICU, libxml2, and curl for android from - [Azure](https://dev.azure.com/compnerd/windows-swift) into `S:\b\a\Library`. + [Azure](https://dev.azure.com/compnerd/swift-build) into `S:\b\a\Library`. -- You can alternatively use `Download-AndroidArtifacts.ps1` from - [compnerd/windows-swift](https://www.github.com/compnerd/windows-swift) under - the utilities directory. This will implicitly setup the requisite directory - structure. +- You can alternatively use `swift-build.py` from + [compnerd/swift-build](https://www.github.com/compnerd/swift-build) under + the utilities directory. ## 1. Configure LLVM ```cmd md S:\b\a\llvm cd S:\b\a\llvm -cmake -C S:\swift-windows\cmake\caches\android-armv7.cmake ^ +cmake -C S:\swift-build\cmake\caches\android-armv7.cmake ^ -G Ninja ^ -DCMAKE_BUILD_TYPE=Release ^ - -DCMAKE_TOOLCHAIN_FILE=S:\swift-windows\cmake\toolchains\android.toolchain.cmake ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-build\cmake\toolchains\android.toolchain.cmake ^ -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ -DLLVM_HOST_TRIPLE=armv7-unknown-linux-androideabi ^ S:/llvm @@ -66,12 +65,12 @@ cmake -C S:\swift-windows\cmake\caches\android-armv7.cmake ```cmd md S:\b\a\stdlib cd S:\b\a\stdlib -cmake -C S:\windows-swift\cmake\caches\android-armv7.cmake ^ - -C S:\windows-swift\cmake\caches\swift-stdlib-android-armv7.cmake ^ +cmake -C S:\swift-build\cmake\caches\android-armv7.cmake ^ + -C S:\swift-build\cmake\caches\swift-stdlib-android-armv7.cmake ^ -G Ninja ^ -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ - -DCMAKE_TOOLCHAIN_FILE=S:\windows-swift\cmake\toolchains\android.toolchain.cmake ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-build\cmake\toolchains\android.toolchain.cmake ^ -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ -DLLVM_DIR=S:/b/a/llvm/lib/cmake/llvm ^ -DSWIFT_NATIVE_SWIFT_TOOLS_PATH=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin ^ @@ -92,14 +91,14 @@ ninja install ```cmd md S:\b\a\libdispatch cd S:\b\a\libdispatch -cmake -C S:\windows-swift\cmake\caches\android-armv7.cmake ^ +cmake -C S:\swift-build\cmake\caches\android-armv7.cmake ^ -DSWIFT_ANDROID_SDK=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk ^ - -C S:\windows-swift\cmake\caches\android-armv7-swift-flags.cmake ^ + -C S:\swift-build\cmake\caches\android-armv7-swift-flags.cmake ^ -G Ninja ^ -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ -DCMAKE_SWIFT_COMPILER=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/swiftc.exe ^ - -DCMAKE_TOOLCHAIN_FILE=S:\windows-swift\cmake\toolchains\android.toolchain.cmake ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-build\cmake\toolchains\android.toolchain.cmake ^ -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ -DENABLE_SWIFT=YES ^ -DENABLE_TESTING=NO ^ @@ -112,14 +111,14 @@ ninja ```cmd md S:\b\a\foundation cd S:\b\a\foundation -cmake -C S:\windows-swift\cmake\caches\android-armv7.cmake ^ +cmake -C S:\swift-build\cmake\caches\android-armv7.cmake ^ -DSWIFT_ANDROID_SDK=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk ^ - -C S:\windows-swift\cmake\caches\android-armv7-swift-flags.cmake ^ + -C S:\swift-build\cmake\caches\android-armv7-swift-flags.cmake ^ -G Ninja ^ -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ -DCMAKE_SWIFT_COMPILER=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/swiftc.exe ^ - -DCMAKE_TOOLCHAIN_FILE=S:\windows-swift\cmake\toolchains\android.toolchain.cmake ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-build\cmake\toolchains\android.toolchain.cmake ^ -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ -DCURL_LIBRARY=S:/b/a/Library/libcurl-development/usr/lib/libcurl.a ^ -DCURL_INCLUDE_DIR=S:/b/a/Library/libcurl-development/usr/include ^ @@ -141,13 +140,13 @@ ninja ```cmd md S:\b\a\xctest cd S:\b\a\xctest -cmake -C S:\swift-windows\cmake\caches\android-armv7.cmake ^ - -C S:\swift-windows\cmake\caches\android-armv7-swift-flags.cmake ^ +cmake -C S:\swift-build\cmake\caches\android-armv7.cmake ^ + -C S:\swift-build\cmake\caches\android-armv7-swift-flags.cmake ^ -G Ninja ^ -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ -DCMAKE_INSTALL_PREFIX=S:/b/a/Library/Developer/Platforms/android.platform/Developer/SDKs/android.sdk/usr ^ -DCMAKE_SWIFT_COMPILER=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/swiftc.exe ^ - -DCMAKE_TOOLCHAIN_FILE=S:\swift-windows\cmake\toolchains\android.toolchain.cmake ^ + -DCMAKE_TOOLCHAIN_FILE=S:\swift-build\cmake\toolchains\android.toolchain.cmake ^ -DANDROID_ALTERNATE_TOOLCHAIN=S:/b/a/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr ^ -DSWIFT_ANDROID_SDK=S:/b/a/Library/Developer/Platforms/andrfoid.platform/Developer/SDKs/android.sdk ^ -DXCTEST_PATH_TO_FOUNDATION_BUILD=S:/b/a/foundation ^ From 3b761dcae18ea717e2cfd4b8d95f56db1a8c6afa Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 1 Feb 2020 23:46:13 -0800 Subject: [PATCH 097/237] [Constraint system] Add a SolutionApplicationTarget-based typeCheckExpression Rework most of typeCheckExpression() to use SolutionApplicationTarget, folding more information into that data structure and sinking more expression-checking behavior down into the more general solver and solution-application code. --- lib/Sema/CSApply.cpp | 7 ++ lib/Sema/ConstraintSystem.cpp | 59 +++++++++++++ lib/Sema/ConstraintSystem.h | 31 +++++-- lib/Sema/TypeCheckConstraints.cpp | 141 +++++++++++------------------- lib/Sema/TypeChecker.h | 8 ++ test/Constraints/closures.swift | 2 +- 6 files changed, 150 insertions(+), 98 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 443f67d35fd29..5c08b584144e5 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7345,6 +7345,13 @@ Optional ConstraintSystem::applySolution( if (!resultExpr) return None; + // For an @autoclosure default parameter type, add the autoclosure + // conversion. + if (FunctionType *autoclosureParamType = + target.getAsAutoclosureParamType()) { + resultExpr = buildAutoClosureExpr(resultExpr, autoclosureParamType); + } + solution.setExprTypes(resultExpr); result.setExpr(resultExpr); } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index f6690b4a82353..2f19661ed7fcf 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3968,3 +3968,62 @@ ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) { return choice.getDecl(); } + +SolutionApplicationTarget::SolutionApplicationTarget( + Expr *expr, ContextualTypePurpose contextualPurpose, + TypeLoc convertType, bool isDiscarded) { + // Verify that a purpose was specified if a convertType was. Note that it is + // ok to have a purpose without a convertType (which is used for call + // return types). + assert((!convertType.getType() || contextualPurpose != CTP_Unused) && + "Purpose for conversion type was not specified"); + + // Take a look at the conversion type to check to make sure it is sensible. + if (auto type = convertType.getType()) { + // If we're asked to convert to an UnresolvedType, then ignore the request. + // This happens when CSDiags nukes a type. + if (type->is() || + (type->is() && type->hasUnresolvedType())) { + convertType = TypeLoc(); + contextualPurpose = CTP_Unused; + } + } + + kind = Kind::expression; + expression.expression = expr; + expression.contextualPurpose = contextualPurpose; + expression.convertType = convertType; + expression.isDiscarded = isDiscarded; +} + +bool SolutionApplicationTarget::contextualTypeIsOnlyAHint( + bool isOpaqueReturnType) const { + assert(kind == Kind::expression); + switch (expression.contextualPurpose) { + case CTP_Initialization: + return !isOpaqueReturnType; + case CTP_ForEachStmt: + return true; + case CTP_Unused: + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_YieldByValue: + case CTP_YieldByReference: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: + case CTP_CalleeResult: + case CTP_CallArgument: + case CTP_ClosureResult: + case CTP_ArrayElement: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_AssignSource: + case CTP_SubscriptAssignSource: + case CTP_Condition: + case CTP_CannotFail: + return false; + } +} diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 96c87701064b8..7d733b80df614 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1174,13 +1174,7 @@ class SolutionApplicationTarget { isDiscarded) { } SolutionApplicationTarget(Expr *expr, ContextualTypePurpose contextualPurpose, - TypeLoc convertType, bool isDiscarded) { - kind = Kind::expression; - expression.expression = expr; - expression.contextualPurpose = contextualPurpose; - expression.convertType = convertType; - expression.isDiscarded = isDiscarded; - } + TypeLoc convertType, bool isDiscarded); SolutionApplicationTarget(AnyFunctionRef fn) : SolutionApplicationTarget(fn, fn.getBody()) { } @@ -1207,15 +1201,31 @@ class SolutionApplicationTarget { } Type getExprConversionType() const { - assert(kind == Kind::expression); - return expression.convertType.getType(); + return getExprConversionTypeLoc().getType(); } TypeLoc getExprConversionTypeLoc() const { assert(kind == Kind::expression); + + // For an @autoclosure parameter, the conversion type is + // the result of the function type. + if (FunctionType *autoclosureParamType = getAsAutoclosureParamType()) { + return TypeLoc(expression.convertType.getTypeRepr(), + autoclosureParamType->getResult()); + } + return expression.convertType; } + /// Returns the autoclosure parameter type, or \c nullptr if the + /// expression has a different kind of context. + FunctionType *getAsAutoclosureParamType() const { + assert(kind == Kind::expression); + if (expression.contextualPurpose == CTP_AutoclosureDefaultParameter) + return expression.convertType.getType()->castTo(); + return nullptr; + } + void setExprConversionType(Type type) { assert(kind == Kind::expression); expression.convertType = TypeLoc::withoutLoc(type); @@ -1226,6 +1236,9 @@ class SolutionApplicationTarget { expression.convertType = type; } + /// Whether the contextual type is only a hint, rather than a type + bool contextualTypeIsOnlyAHint(bool isOpaqueReturnType) const; + bool isDiscardedExpr() const { assert(kind == Kind::expression); return expression.isDiscarded; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d81a3936ecd85..6c8da6ea38877 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2058,40 +2058,6 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement( return false; } -/// Whether the contextual type provided for the given purpose is only a -/// hint, and not a requirement. -static bool contextualTypeIsOnlyAHint(ContextualTypePurpose ctp, - TypeCheckExprOptions options) { - switch (ctp) { - case CTP_Initialization: - return !options.contains( - TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType); - case CTP_ForEachStmt: - return true; - case CTP_Unused: - case CTP_ReturnStmt: - case CTP_ReturnSingleExpr: - case CTP_YieldByValue: - case CTP_YieldByReference: - case CTP_ThrowStmt: - case CTP_EnumCaseRawValue: - case CTP_DefaultParameter: - case CTP_AutoclosureDefaultParameter: - case CTP_CalleeResult: - case CTP_CallArgument: - case CTP_ClosureResult: - case CTP_ArrayElement: - case CTP_DictionaryKey: - case CTP_DictionaryValue: - case CTP_CoerceOperand: - case CTP_AssignSource: - case CTP_SubscriptAssignSource: - case CTP_Condition: - case CTP_CannotFail: - return false; - } -} - #pragma mark High-level entry points Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeLoc convertType, @@ -2099,14 +2065,34 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, TypeCheckExprOptions options, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { + SolutionApplicationTarget target( + expr, convertTypePurpose, convertType, + options.contains(TypeCheckExprFlags::IsDiscarded)); + auto resultType = typeCheckExpression(target, dc, options, listener, baseCS); + if (!resultType) { + return Type(); + } + + expr = resultType->getAsExpr(); + return expr->getType(); +} + +Optional +TypeChecker::typeCheckExpression( + SolutionApplicationTarget target, + DeclContext *dc, + TypeCheckExprOptions options, + ExprTypeCheckListener *listener, + ConstraintSystem *baseCS) { auto &Context = dc->getASTContext(); + Expr *expr = target.getAsExpr(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) { - return Type(); + return None; } // Construct a constraint system from this expression. @@ -2124,45 +2110,23 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, ConstraintSystem cs(dc, csOptions); cs.baseCS = baseCS; - // Verify that a purpose was specified if a convertType was. Note that it is - // ok to have a purpose without a convertType (which is used for call - // return types). - assert((!convertType.getType() || convertTypePurpose != CTP_Unused) && - "Purpose for conversion type was not specified"); - - // Take a look at the conversion type to check to make sure it is sensible. - if (auto type = convertType.getType()) { - // If we're asked to convert to an UnresolvedType, then ignore the request. - // This happens when CSDiags nukes a type. - if (type->is() || - (type->is() && type->hasUnresolvedType())) { - convertType = TypeLoc(); - convertTypePurpose = CTP_Unused; - } - } - - // For an @autoclosure default parameter, we want to convert to the result - // type. Stash the autoclosure default parameter type. - FunctionType *autoclosureDefaultParamType = nullptr; - if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { - autoclosureDefaultParamType = convertType.getType()->castTo(); - convertType.setType(autoclosureDefaultParamType->getResult()); - } - // Tell the constraint system what the contextual type is. This informs // diagnostics and is a hint for various performance optimizations. // FIXME: Look through LoadExpr. This is an egregious hack due to the // way typeCheckExprIndependently works. + TypeLoc convertType = target.getExprConversionTypeLoc(); Expr *contextualTypeExpr = expr; if (auto loadExpr = dyn_cast_or_null(contextualTypeExpr)) contextualTypeExpr = loadExpr->getSubExpr(); cs.setContextualType( - contextualTypeExpr, convertType, convertTypePurpose, + contextualTypeExpr, convertType, + target.getExprContextualTypePurpose(), options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)); // If the convertType is *only* provided for that hint, then null it out so // that we don't later treat it as an actual conversion constraint. - if (contextualTypeIsOnlyAHint(convertTypePurpose, options)) + if (target.contextualTypeIsOnlyAHint( + options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType))) convertType = TypeLoc(); // If the client can handle unresolved type variables, leave them in the @@ -2179,15 +2143,20 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); Type var = cs.createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape); convertTo = getOptionalType(expr->getLoc(), var); + } else if (target.getExprContextualTypePurpose() + == CTP_AutoclosureDefaultParameter) { + // FIXME: Hack around the convertTo adjustment below, which we want to + // eliminate. + convertTo = Type(target.getAsAutoclosureParamType()); } // Attempt to solve the constraint system. - SolutionApplicationTarget target( - expr, convertTypePurpose, convertTo, - options.contains(TypeCheckExprFlags::IsDiscarded)); - auto viable = cs.solve(target, listener, allowFreeTypeVariables); + SolutionApplicationTarget innerTarget( + expr, target.getExprContextualTypePurpose(), convertTo, + target.isDiscardedExpr()); + auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables); if (!viable) - return Type(); + return None; // If the client allows the solution to have unresolved type expressions, // check for them now. We cannot apply the solution with unresolved TypeVars, @@ -2195,40 +2164,36 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && (viable->size() != 1 || (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { - return ErrorType::get(Context); + // FIXME: This hack should only be needed for CSDiag. + target.getAsExpr()->setType(ErrorType::get(Context)); + return target; } - auto result = target.getAsExpr(); - auto &solution = (*viable)[0]; - if (!result) - return Type(); - // Apply this solution to the constraint system. + // FIXME: This shouldn't be necessary. + auto &solution = (*viable)[0]; cs.applySolution(solution); // Apply the solution to the expression. bool performingDiagnostics = options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); - // FIXME: HACK! - target.setExprConversionType(convertType.getType()); + // FIXME: HACK! Copy over the inner target's expression info. + target.setExpr(innerTarget.getAsExpr()); + if (convertTo.isNull()) + target.setExprConversionType(convertTo); auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); if (!resultTarget) { // Failure already diagnosed, above, as part of applying the solution. - return Type(); - } - result = resultTarget->getAsExpr(); - - // For an @autoclosure default parameter type, add the autoclosure - // conversion. - if (convertTypePurpose == CTP_AutoclosureDefaultParameter) { - result = cs.buildAutoClosureExpr(result, autoclosureDefaultParamType); + return None; } + Expr *result = resultTarget->getAsExpr(); // Notify listener that we've applied the solution. - if (listener) + if (listener) { result = listener->appliedSolution(solution, result); - if (!result) - return Type(); + if (!result) + return None; + } if (Context.TypeCheckerOpts.DebugConstraintSolver) { auto &log = Context.TypeCheckerDebug->getStream(); @@ -2245,8 +2210,8 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, performSyntacticExprDiagnostics(result, dc, isExprStmt); } - expr = result; - return cs.getType(expr); + target.setExpr(result); + return target; } Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue, diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 47e4aef224621..3ac30b427267b 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -49,6 +49,7 @@ namespace constraints { enum class ConstraintKind : char; class ConstraintSystem; class Solution; + class SolutionApplicationTarget; class SolutionResult; } @@ -832,6 +833,13 @@ class TypeChecker final { ExprTypeCheckListener *listener = nullptr, constraints::ConstraintSystem *baseCS = nullptr); + static Optional + typeCheckExpression(constraints::SolutionApplicationTarget target, + DeclContext *dc, + TypeCheckExprOptions options = TypeCheckExprOptions(), + ExprTypeCheckListener *listener = nullptr, + constraints::ConstraintSystem *baseCS = nullptr); + /// Type check the given expression and return its type without /// applying the solution. /// diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index fb15d6de202ce..085931b78f0e2 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -186,7 +186,7 @@ func testMap() { } // "UnresolvedDot" "in wrong phase" assertion from verifier -[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(_)'}} +[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(<>)'}} From c5c7ed17b8516554ce74c8b88eed86529bb7fbe6 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 1 Feb 2020 19:01:03 -0800 Subject: [PATCH 098/237] build: hoist `LINK_LIBRARIES` out of `_add_swift_executable_single` Hoist the responsibility for adding the linked libraries out of `_add_swift_executable_single` to the invoker. This impacts only `swift_add_target_executable`. This continues to bring the computation of the properties nearer the site of definition. --- cmake/modules/AddSwift.cmake | 10 +++------- stdlib/cmake/modules/AddSwiftStdlib.cmake | 10 ++++++++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index a89442f485d85..a334144423a33 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -2506,6 +2506,9 @@ function(_add_swift_executable_single name) if(SWIFTEXE_SINGLE_EXCLUDE_FROM_ALL) message(SEND_ERROR "${name} is using EXCLUDE_FROM_ALL option which is deprecated.") endif() + if(SWIFTEXE_SINGLE_LINK_LIBRARIES) + message(SEND_ERROR "${name} is using LINK_LIBRARIES parameter which is deprecated. Please use target_link_libraries instead") + endif() # Check arguments. precondition(SWIFTEXE_SINGLE_SDK MESSAGE "Should specify an SDK") @@ -2540,12 +2543,6 @@ function(_add_swift_executable_single name) LINK_LIBRARIES_VAR_NAME link_libraries LIBRARY_SEARCH_DIRECTORIES_VAR_NAME library_search_directories) - _list_add_string_suffix( - "${SWIFTEXE_SINGLE_LINK_LIBRARIES}" - "-${SWIFT_SDK_${SWIFTEXE_SINGLE_SDK}_LIB_SUBDIR}-${SWIFTEXE_SINGLE_ARCHITECTURE}" - SWIFTEXE_SINGLE_LINK_LIBRARIES_TARGETS) - set(SWIFTEXE_SINGLE_LINK_LIBRARIES ${SWIFTEXE_SINGLE_LINK_LIBRARIES_TARGETS}) - handle_swift_sources( dependency_target unused_module_dependency_target @@ -2609,7 +2606,6 @@ function(_add_swift_executable_single name) BINARY_DIR ${SWIFT_RUNTIME_OUTPUT_INTDIR} LIBRARY_DIR ${SWIFT_LIBRARY_OUTPUT_INTDIR}) - target_link_libraries("${name}" PRIVATE ${SWIFTEXE_SINGLE_LINK_LIBRARIES}) swift_common_llvm_config("${name}" ${SWIFTEXE_SINGLE_LLVM_LINK_COMPONENTS}) # NOTE(compnerd) use the C linker language to invoke `clang` rather than diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index a80005624d4ec..1f16c29127878 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -45,8 +45,14 @@ function(add_swift_target_executable name) DEPENDS ${SWIFTEXE_TARGET_DEPENDS_with_suffix} LLVM_LINK_COMPONENTS ${SWIFTEXE_TARGET_LLVM_LINK_COMPONENTS} SDK "${sdk}" - ARCHITECTURE "${arch}" - LINK_LIBRARIES ${SWIFTEXE_TARGET_LINK_LIBRARIES}) + ARCHITECTURE "${arch}") + + _list_add_string_suffix( + "${SWIFTEXE_TARGET_LINK_LIBRARIES}" + "-${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}" + SWIFTEXE_TARGET_LINK_LIBRARIES_TARGETS) + target_link_libraries(${VARIANT_NAME} PRIVATE + ${SWIFTEXE_TARGET_LINK_LIBRARIES_TARGETS}) if(NOT "${VARIANT_SUFFIX}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SUFFIX}") # By default, don't build executables for target SDKs to avoid building From 70a417c3f567f603594e38e7b1337b8ff5ee6b88 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 28 Jan 2020 13:24:41 -0800 Subject: [PATCH 099/237] [ownership] Refactor checked conversion from ArrayRef -> ArrayRef onto BranchPropagatedUser. A branch propagated user that isn't a cond_br is layout compatible with a SILInstruction *. This helper function converts from ArrayRef -> ArrayRef but also in asserts builds checks that our invariant (namely all of the 'SILInstruction *' are not cond_br. --- include/swift/SIL/BranchPropagatedUser.h | 12 ++++++++++++ include/swift/SIL/OwnershipUtils.h | 21 +++------------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/include/swift/SIL/BranchPropagatedUser.h b/include/swift/SIL/BranchPropagatedUser.h index 5e00ebf4ab9b5..1c85fb22e2637 100644 --- a/include/swift/SIL/BranchPropagatedUser.h +++ b/include/swift/SIL/BranchPropagatedUser.h @@ -108,6 +108,18 @@ class BranchPropagatedUser { llvm::PointerLikeTypeTraits::NumLowBitsAvailable }; + static ArrayRef + convertFromInstArray(ArrayRef instArray) { + assert(llvm::all_of( + instArray, + [](SILInstruction *i) { return !isa(i); }) && + "Passed cond branch to a non-BranchPropagatedUser API"); + auto *castData = + reinterpret_cast(instArray.data()); + ArrayRef castArray(castData, instArray.size()); + return castArray; + } + private: BranchPropagatedUser(SILInstruction *inst) : user(inst) { assert(!isa(inst)); diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h index 8f72bb8ef4d0e..88d2a566c55e1 100644 --- a/include/swift/SIL/OwnershipUtils.h +++ b/include/swift/SIL/OwnershipUtils.h @@ -187,24 +187,9 @@ class LinearLifetimeChecker { bool validateLifetime(SILValue value, ArrayRef consumingUses, ArrayRef nonConsumingUses) { - assert(llvm::all_of( - consumingUses, - [](SILInstruction *i) { return !isa(i); }) && - "Passed cond branch to a non-BranchPropagatedUser API"); - assert(llvm::all_of( - nonConsumingUses, - [](SILInstruction *i) { return !isa(i); }) && - "Passed cond branch to a non-BranchPropagatedUser API"); - auto *consumingUsesCast = - reinterpret_cast(consumingUses.data()); - auto *nonConsumingUsesCast = - reinterpret_cast(nonConsumingUses.data()); - ArrayRef consumingUsesCastArray(consumingUsesCast, - consumingUses.size()); - ArrayRef nonConsumingUsesCastArray( - nonConsumingUsesCast, nonConsumingUses.size()); - return validateLifetime(value, consumingUsesCastArray, - nonConsumingUsesCastArray); + return validateLifetime( + value, BranchPropagatedUser::convertFromInstArray(consumingUses), + BranchPropagatedUser::convertFromInstArray(nonConsumingUses)); } }; From 178b0ff4d9cb12a3f85b6b2645189af670d49df9 Mon Sep 17 00:00:00 2001 From: kelvin13 Date: Sun, 2 Feb 2020 14:52:47 -0600 Subject: [PATCH 100/237] add tests --- .../synthesized_extension_conformances.swift | 52 ++++++++++++------- test/Sema/enum_conformance_synthesis.swift | 11 ++++ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/test/Interpreter/synthesized_extension_conformances.swift b/test/Interpreter/synthesized_extension_conformances.swift index 3b47c7f794a83..3a82cbfa23484 100644 --- a/test/Interpreter/synthesized_extension_conformances.swift +++ b/test/Interpreter/synthesized_extension_conformances.swift @@ -39,6 +39,7 @@ enum EGeneric { } extension EGeneric: Equatable where T: Equatable {} extension EGeneric: Hashable where T: Hashable {} +extension EGeneric: Comparable where T: Comparable {} enum NoValues { case a, b, c @@ -89,6 +90,19 @@ func testEquatableHashable(cases: [Int: (T, T, Bool, Bo "\(#file):\(testLine) LHS <\(debugDescription(lhs)).hashValue> (\(lhsHash)) == RHS <\(debugDescription(rhs)).hashValue> (\(rhsHash)) doesn't match <\(hashEqual)>") } } +func testEquatableHashableComparable(cases: [Int: (T, T, Bool, Bool, Bool)]) { + for (testLine, (lhs, rhs, equal, hashEqual, less)) in cases { + expectEqual(lhs == rhs, equal, + "\(#file):\(testLine) LHS <\(debugDescription(lhs))> == RHS <\(debugDescription(rhs))> doesn't match <\(equal)>") + expectEqual(lhs < rhs, less, + "\(#file):\(testLine) LHS <\(debugDescription(lhs))> < RHS <\(debugDescription(rhs))> doesn't match <\(less)>") + + let lhsHash = lhs.hashValue + let rhsHash = rhs.hashValue + expectEqual(lhsHash == rhsHash, hashEqual, + "\(#file):\(testLine) LHS <\(debugDescription(lhs)).hashValue> (\(lhsHash)) == RHS <\(debugDescription(rhs)).hashValue> (\(rhsHash)) doesn't match <\(hashEqual)>") + } +} class TestEquatableHashable : TestSuper { lazy var int: [Int: (SInt, SInt, Bool, Bool)] = [ @@ -147,28 +161,28 @@ class TestEquatableHashable : TestSuper { testEquatableHashable(cases: generic) } - lazy var egeneric: [Int: (EGeneric, EGeneric, Bool, Bool)] = [ - #line : (EGaOne, EGaOne, true, true), - #line : (EGaOne, EGaTwo, false, false), - #line : (EGaOne, EGbOne, false, false), - #line : (EGaOne, EGbTwo, false, false), - #line : (EGaOne, EGc___, false, false), - - #line : (EGbOne, EGaOne, false, false), - #line : (EGbOne, EGaTwo, false, false), - #line : (EGbOne, EGbOne, true, true), - #line : (EGbOne, EGbTwo, false, false), - #line : (EGbOne, EGc___, false, false), - - #line : (EGc___, EGaOne, false, false), - #line : (EGc___, EGaTwo, false, false), - #line : (EGc___, EGbOne, false, false), - #line : (EGc___, EGbTwo, false, false), - #line : (EGc___, EGc___, true, true), + lazy var egeneric: [Int: (EGeneric, EGeneric, Bool, Bool, Bool)] = [ + #line : (EGaOne, EGaOne, true, true, false), + #line : (EGaOne, EGaTwo, false, false, true), + #line : (EGaOne, EGbOne, false, false, true), + #line : (EGaOne, EGbTwo, false, false, true), + #line : (EGaOne, EGc___, false, false, true), + + #line : (EGbOne, EGaOne, false, false, false), + #line : (EGbOne, EGaTwo, false, false, false), + #line : (EGbOne, EGbOne, true, true, false), + #line : (EGbOne, EGbTwo, false, false, true), + #line : (EGbOne, EGc___, false, false, true), + + #line : (EGc___, EGaOne, false, false, false), + #line : (EGc___, EGaTwo, false, false, false), + #line : (EGc___, EGbOne, false, false, false), + #line : (EGc___, EGbTwo, false, false, false), + #line : (EGc___, EGc___, true, true, false), ] func test_EGeneric() { - testEquatableHashable(cases: egeneric) + testEquatableHashableComparable(cases: egeneric) } } diff --git a/test/Sema/enum_conformance_synthesis.swift b/test/Sema/enum_conformance_synthesis.swift index 29a2d8305197f..551b1bb876b30 100644 --- a/test/Sema/enum_conformance_synthesis.swift +++ b/test/Sema/enum_conformance_synthesis.swift @@ -357,6 +357,17 @@ func miss(_ a: Publicist, outsold b: Publicist) -> Bool { return b < a // expected-error{{binary operator '<' cannot be applied to two 'Publicist' operands}} } +// can synthesize Comparable conformance through extension +enum Birthyear { + case eighties(Int) + case nineties(Int) + case twothousands(Int) +} +extension Birthyear: Comparable { +} +func canEatHotChip(_ birthyear:Birthyear) -> Bool { + return birthyear > .nineties(3) +} // FIXME: Remove -verify-ignore-unknown. // :0: error: unexpected error produced: invalid redeclaration of 'hashValue' // :0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool' From 8f91b9e89a78e7936e0de084d213a43a466a798c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 2 Feb 2020 12:44:52 -0800 Subject: [PATCH 101/237] [ownership] Get rid of old entrypoint for creatingEndBorrow that takes the original value. The original design was to make it so that end_borrow tied at the use level its original/borrowed value. So we would have: ``` %borrowedVal = begin_borrow %original ... end_borrow %borrowedVal from %original ``` In the end we decided not to use that design and instead just use: ``` %borrowedVal = begin_borrow %original ... end_borrow %borrowedVal ``` In order to enable that transition, I left the old API for end_borrow that took both original and borrowedVal and reimplemented it on top of the new API that just took the borrowedVal (i.e. the original was just a dead arg). Now given where we are in the development, it makes sense to get rid of that transition API and move to just use the new API. --- include/swift/SIL/SILBuilder.h | 6 ------ include/swift/SIL/SILCloner.h | 5 ++--- lib/SILGen/FormalEvaluation.cpp | 3 +-- lib/SILGen/SILGenLValue.cpp | 2 +- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index bda95ab9f3aca..f7d8bf2f884df 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -806,12 +806,6 @@ class SILBuilder { EndBorrowInst(getSILDebugLocation(loc), borrowedValue)); } - EndBorrowInst *createEndBorrow(SILLocation Loc, SILValue BorrowedValue, - SILValue OriginalValue) { - return insert(new (getModule()) - EndBorrowInst(getSILDebugLocation(Loc), BorrowedValue)); - } - BeginAccessInst *createBeginAccess(SILLocation loc, SILValue address, SILAccessKind accessKind, SILAccessEnforcement enforcement, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index ff751f61d20c2..8563c2dcbe315 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1177,9 +1177,8 @@ void SILCloner::visitEndBorrowInst(EndBorrowInst *Inst) { return; recordClonedInstruction( - Inst, - getBuilder().createEndBorrow(getOpLocation(Inst->getLoc()), - getOpValue(Inst->getOperand()), SILValue())); + Inst, getBuilder().createEndBorrow(getOpLocation(Inst->getLoc()), + getOpValue(Inst->getOperand()))); } template diff --git a/lib/SILGen/FormalEvaluation.cpp b/lib/SILGen/FormalEvaluation.cpp index e392b712cb694..b15414b4f1352 100644 --- a/lib/SILGen/FormalEvaluation.cpp +++ b/lib/SILGen/FormalEvaluation.cpp @@ -48,8 +48,7 @@ void FormalAccess::verify(SILGenFunction &SGF) const { //===----------------------------------------------------------------------===// void SharedBorrowFormalAccess::finishImpl(SILGenFunction &SGF) { - SGF.B.createEndBorrow(CleanupLocation::get(loc), borrowedValue, - originalValue); + SGF.B.createEndBorrow(CleanupLocation::get(loc), borrowedValue); } //===----------------------------------------------------------------------===// diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 747c6a97d1922..9cc3be95bdb9d 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -3711,7 +3711,7 @@ static SILValue emitLoadOfSemanticRValue(SILGenFunction &SGF, if (!isTake) { \ SILValue value = SGF.B.createLoadBorrow(loc, src); \ SILValue strongValue = SGF.B.createStrongCopy##Name##Value(loc, value); \ - SGF.B.createEndBorrow(loc, value, src); \ + SGF.B.createEndBorrow(loc, value); \ return strongValue; \ } \ /* Otherwise perform a load take and destroy the stored value. */ \ From 92864f1a60d8f775239a3e72498bcce3be65358c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 2 Feb 2020 13:29:35 -0800 Subject: [PATCH 102/237] [sil] Add SILArgument::getSingleTerminator() helper. This is just like SILArgument::getSingleTerminatorOperand() except that it returns the actual Terminator rather than the terminator's argument. --- include/swift/SIL/SILArgument.h | 18 ++++++++++++++++++ lib/SIL/SILArgument.cpp | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/include/swift/SIL/SILArgument.h b/include/swift/SIL/SILArgument.h index 4f23c1cd6ff46..d5d8537fd8c3b 100644 --- a/include/swift/SIL/SILArgument.h +++ b/include/swift/SIL/SILArgument.h @@ -173,6 +173,10 @@ class SILArgument : public ValueBase { /// is the enum itself (the operand of the switch_enum). SILValue getSingleTerminatorOperand() const; + /// If this SILArgument's parent block has a single predecessor whose + /// terminator has a single operand, return that terminator. + TermInst *getSingleTerminator() const; + /// Return the SILArgumentKind of this argument. SILArgumentKind getKind() const { return SILArgumentKind(ValueBase::getKind()); @@ -264,6 +268,10 @@ class SILPhiArgument : public SILArgument { /// is the enum itself (the operand of the switch_enum). SILValue getSingleTerminatorOperand() const; + /// If this SILArgument's parent block has a single predecessor whose + /// terminator has a single operand, return that terminator. + TermInst *getSingleTerminator() const; + static bool classof(const SILInstruction *) = delete; static bool classof(const SILUndef *) = delete; static bool classof(const SILNode *node) { @@ -403,6 +411,16 @@ inline bool SILArgument::getSingleTerminatorOperands( llvm_unreachable("Covered switch is not covered?!"); } +inline TermInst *SILArgument::getSingleTerminator() const { + switch (getKind()) { + case SILArgumentKind::SILPhiArgument: + return cast(this)->getSingleTerminator(); + case SILArgumentKind::SILFunctionArgument: + return nullptr; + } + llvm_unreachable("Covered switch is not covered?!"); +} + } // end swift namespace #endif diff --git a/lib/SIL/SILArgument.cpp b/lib/SIL/SILArgument.cpp index f45bdf80edd11..131b8b888ae85 100644 --- a/lib/SIL/SILArgument.cpp +++ b/lib/SIL/SILArgument.cpp @@ -235,6 +235,14 @@ SILValue SILPhiArgument::getSingleTerminatorOperand() const { return getSingleTerminatorOperandForPred(parentBlock, predBlock, getIndex()); } +TermInst *SILPhiArgument::getSingleTerminator() const { + auto *parentBlock = getParent(); + auto *predBlock = parentBlock->getSinglePredecessorBlock(); + if (!predBlock) + return nullptr; + return const_cast(predBlock)->getTerminator(); +} + const SILPhiArgument *BranchInst::getArgForOperand(const Operand *oper) const { assert(oper->getUser() == this); return cast( From 52428ff971bf7b920484055235663de2a31d6db9 Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Sun, 2 Feb 2020 14:01:19 -0800 Subject: [PATCH 103/237] [Build System: build-script] Remove the host module from swift_build_support. The functions used to calculate default LTO link jobs for LLVM and Swift have been moved to the build_swift.defaults module. --- utils/build_swift/build_swift/defaults.py | 61 ++++++++- .../build_swift/driver_arguments.py | 6 +- .../tests/build_swift/test_defaults.py | 121 ++++++++++++++++++ utils/build_swift/tests/expected_options.py | 5 +- .../swift_build_support/host.py | 91 ------------- utils/swift_build_support/tests/test_host.py | 61 --------- 6 files changed, 184 insertions(+), 161 deletions(-) create mode 100644 utils/build_swift/tests/build_swift/test_defaults.py delete mode 100644 utils/swift_build_support/swift_build_support/host.py delete mode 100644 utils/swift_build_support/tests/test_host.py diff --git a/utils/build_swift/build_swift/defaults.py b/utils/build_swift/build_swift/defaults.py index a6c66ff7e035d..1abc91f4f78e4 100644 --- a/utils/build_swift/build_swift/defaults.py +++ b/utils/build_swift/build_swift/defaults.py @@ -3,8 +3,8 @@ # Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # -# See http://swift.org/LICENSE.txt for license information -# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors """ @@ -14,6 +14,9 @@ from __future__ import absolute_import, unicode_literals +import platform + +from . import shell from .versions import Version @@ -32,6 +35,8 @@ 'DARWIN_DEPLOYMENT_VERSION_WATCHOS', 'UNIX_INSTALL_PREFIX', 'DARWIN_INSTALL_PREFIX', + 'LLVM_MAX_PARALLEL_LTO_LINK_JOBS', + 'SWIFT_MAX_PARALLEL_LTO_LINK_JOBS', # Constants ] @@ -56,6 +61,58 @@ DARWIN_INSTALL_PREFIX = ('/Applications/Xcode.app/Contents/Developer/' 'Toolchains/XcodeDefault.xctoolchain/usr') + +def _system_memory(): + """Returns the system memory as an int. None if the system memory cannot + be determined. + + TODO: Support Linux and Windows platforms. + """ + + if platform.platform() == 'Darwin': + try: + output = shell.check_output(['sysctl', 'hw.memsize']).strip() + return int(output.split(' ')[1]) + except shell.CalledProcessError: + return None + + return None + + +def _default_llvm_lto_link_jobs(): + """Use the formula (GB Memory - 3)/6.0GB to get the number of parallel + link threads we can support. This gives the OS 3 GB of room to work with. + + This is a bit conservative, but I have found that this hueristic prevents + me from swapping on my test machine. + """ + + memory = _system_memory() + if memory is None: + return None + + return int((memory / 1000000000.0 - 3.0) / 6.0) + + +def _default_swift_lto_link_jobs(): + """Use the formula (GB Memory - 3)/8.0GB to get the number of parallel + link threads we can support. This gives the OS 3 GB of room to work with. + + This is a bit conservative, but I have found that this hueristic prevents + me from swapping on my test machine. + """ + + memory = _system_memory() + if memory is None: + return None + + return int((memory / 1000000000.0 - 3.0) / 8.0) + + +LLVM_MAX_PARALLEL_LTO_LINK_JOBS = _default_llvm_lto_link_jobs() +SWIFT_MAX_PARALLEL_LTO_LINK_JOBS = _default_swift_lto_link_jobs() + + # Options that can only be "configured" by editing this file. # # These options are not exposed as command line options on purpose. If you diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index ed569ad8a8473..becd8a02db234 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -14,7 +14,6 @@ import android.adb.commands -from swift_build_support.swift_build_support import host from swift_build_support.swift_build_support import targets from swift_build_support.swift_build_support.targets import \ StdlibDeploymentTarget @@ -464,15 +463,14 @@ def create_argument_parser(): option('--clang-profile-instr-use', store_path, help='profile file to use for clang PGO') - default_max_lto_link_job_counts = host.max_lto_link_job_counts() option('--llvm-max-parallel-lto-link-jobs', store_int, - default=default_max_lto_link_job_counts['llvm'], + default=defaults.LLVM_MAX_PARALLEL_LTO_LINK_JOBS, metavar='COUNT', help='the maximum number of parallel link jobs to use when ' 'compiling llvm') option('--swift-tools-max-parallel-lto-link-jobs', store_int, - default=default_max_lto_link_job_counts['swift'], + default=defaults.SWIFT_MAX_PARALLEL_LTO_LINK_JOBS, metavar='COUNT', help='the maximum number of parallel link jobs to use when ' 'compiling swift tools.') diff --git a/utils/build_swift/tests/build_swift/test_defaults.py b/utils/build_swift/tests/build_swift/test_defaults.py new file mode 100644 index 0000000000000..0e38ba868d620 --- /dev/null +++ b/utils/build_swift/tests/build_swift/test_defaults.py @@ -0,0 +1,121 @@ +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +# Licensed under Apache License v2.0 with Runtime Library Exception +# +# See https://swift.org/LICENSE.txt for license information +# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors + + +from __future__ import absolute_import, unicode_literals + +import unittest + +from build_swift import defaults +from build_swift import shell + +from .. import utils + + +try: + # Python 3.3 + from unittest import mock + from unittest.mock import patch, MagicMock +except ImportError: + mock = None + + class MagicMock(object): + def __init__(self, *args, **kwargs): + pass + + def patch(*args, **kwargs): + return lambda func: func + + +# ---------------------------------------------------------------------------- +# Constants + +_SYSCTL_HW_MEMSIZE = 17179869184 +_SYSCTL_HW_MEMSIZE_OUTPUT = 'hw.memsize: {}'.format(_SYSCTL_HW_MEMSIZE) + +# Safe upper bound to sanity check the LTO link job heuristics. +_LTO_LINK_JOBS_UPPER_BOUND = 100 + + +# ---------------------------------------------------------------------------- + +class TestDefaults(unittest.TestCase): + """Unit tests for the defaults module in build_swift. + """ + + # ------------------------------------------------------------------------ + # _system_memory + + @utils.requires_module('unittest.mock') + @patch('platform.platform', MagicMock(return_value='Darwin')) + def test_system_memory_darwin_platform(self): + with mock.patch.object(shell, 'check_output') as mock_check_output: + mock_check_output.return_value = _SYSCTL_HW_MEMSIZE_OUTPUT + + self.assertEqual( + defaults._system_memory(), _SYSCTL_HW_MEMSIZE) + + @utils.requires_module('unittest.mock') + @patch('platform.platform', MagicMock(return_value='Darwin')) + def test_system_memory_darwin_platform_when_sysctl_fails(self): + with mock.patch.object(shell, 'check_output') as mock_check_output: + mock_check_output.side_effect = shell.CalledProcessError( + returncode=1, + cmd=['sysctl', 'hw.memsize']) + + self.assertIsNone(defaults._system_memory()) + + @utils.requires_module('unittest.mock') + @patch('platform.platform', MagicMock(return_value='Linux')) + def test_system_memory_linux_platform(self): + self.assertIsNone(defaults._system_memory()) + + @utils.requires_module('unittest.mock') + @patch('platform.platform', MagicMock(return_value='Windows')) + def test_system_memory_windows_platform(self): + self.assertIsNone(defaults._system_memory()) + + # ------------------------------------------------------------------------ + # _default_llvm_lto_link_jobs + + @utils.requires_module('unittest.mock') + def test_default_llvm_lto_link_jobs(self): + with mock.patch.object(defaults, '_system_memory') as mock_memory: + mock_memory.return_value = _SYSCTL_HW_MEMSIZE + + lto_link_jobs = defaults._default_llvm_lto_link_jobs() + + self.assertIsNotNone(lto_link_jobs) + self.assertLess(lto_link_jobs, _LTO_LINK_JOBS_UPPER_BOUND) + + @utils.requires_module('unittest.mock') + def test_default_llvm_lto_link_jobs_with_unknown_system_memory(self): + with mock.patch.object(defaults, '_system_memory') as mock_memory: + mock_memory.return_value = None + + self.assertIsNone(defaults._default_llvm_lto_link_jobs()) + + # ------------------------------------------------------------------------ + # _default_swift_lto_link_jobs + + @utils.requires_module('unittest.mock') + def test_default_swift_lto_link_jobs(self): + with mock.patch.object(defaults, '_system_memory') as mock_memory: + mock_memory.return_value = _SYSCTL_HW_MEMSIZE + + lto_link_jobs = defaults._default_swift_lto_link_jobs() + + self.assertIsNotNone(lto_link_jobs) + self.assertLess(lto_link_jobs, _LTO_LINK_JOBS_UPPER_BOUND) + + @utils.requires_module('unittest.mock') + def test_default_swift_lto_link_jobs_with_unknown_system_memory(self): + with mock.patch.object(defaults, '_system_memory') as mock_memory: + mock_memory.return_value = None + + self.assertIsNone(defaults._default_llvm_lto_link_jobs()) diff --git a/utils/build_swift/tests/expected_options.py b/utils/build_swift/tests/expected_options.py index 38276cfe9c782..0b8e75e1eae6d 100644 --- a/utils/build_swift/tests/expected_options.py +++ b/utils/build_swift/tests/expected_options.py @@ -14,7 +14,6 @@ from build_swift import argparse from build_swift import defaults -from swift_build_support.swift_build_support import host from swift_build_support.swift_build_support import targets @@ -169,7 +168,7 @@ 'llvm_assertions': True, 'llvm_build_variant': 'Debug', 'llvm_max_parallel_lto_link_jobs': - host.max_lto_link_job_counts()['llvm'], + defaults.LLVM_MAX_PARALLEL_LTO_LINK_JOBS, 'llvm_targets_to_build': 'X86;ARM;AArch64;PowerPC;SystemZ;Mips', 'tsan_libdispatch_test': False, 'long_test': False, @@ -191,7 +190,7 @@ 'swift_stdlib_assertions': True, 'swift_stdlib_build_variant': 'Debug', 'swift_tools_max_parallel_lto_link_jobs': - host.max_lto_link_job_counts()['swift'], + defaults.SWIFT_MAX_PARALLEL_LTO_LINK_JOBS, 'swift_user_visible_version': defaults.SWIFT_USER_VISIBLE_VERSION, 'symbols_package': None, 'test': None, diff --git a/utils/swift_build_support/swift_build_support/host.py b/utils/swift_build_support/swift_build_support/host.py deleted file mode 100644 index eb8b327c3062e..0000000000000 --- a/utils/swift_build_support/swift_build_support/host.py +++ /dev/null @@ -1,91 +0,0 @@ -# swift_build_support/host.py ----------- Migrating build-script -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ----------------------------------------------------------------------------- -# -# This file contains routines for determining information about the host for -# use in utils/build-script. -# -# ----------------------------------------------------------------------------- - -from __future__ import absolute_import - -import platform - -from . import shell - - -# Utilities -def _return_none_fun(): - return None - - -def _return_none_fun_pair(): - return (_return_none_fun, _return_none_fun) - - -def _compute_system_key(): - return (platform.system(), platform.machine()) - - -# System Memory -def _darwin_system_memory(): - # Output looks like "hw.memsize: \d+\n" - return int(shell.capture(["sysctl", "hw.memsize"], - dry_run=False, echo=False, - optional=False).strip().split(" ")[1]) - - -_PER_PLATFORM_SYSTEM_MEMORY = { - ('Darwin', 'x86_64'): _darwin_system_memory -} - - -def system_memory(): - return _PER_PLATFORM_SYSTEM_MEMORY.get(_compute_system_key(), - _return_none_fun)() - - -# Max Num CPU Threads for use with LTO -def _darwin_max_num_llvm_parallel_lto_link_jobs(): - # *WARNING! HEURISTIC!* - # - # Use the formula (GB Memory - 3)/6.0GB to get the number of - # parallel link threads we can support. This gives the OS 3 GB of - # room to work with. - # - # This is a bit conservative, but I have found that this number - # prevents me from swapping on my test machine. - return int((_darwin_system_memory() / 1000000000.0 - 3.0) / 6.0) - - -def _darwin_max_num_swift_parallel_lto_link_jobs(): - # *WARNING! HEURISTIC!* - # - # Use the formula (GB Memory - 3)/8.0GB to get the number of - # parallel link threads we can support. This gives the OS 3 GB of - # room to work with. - # - # This is a bit conservative, but I have found that this number - # prevents me from swapping on my test machine. - return int((_darwin_system_memory() / 1000000000.0 - 3.0) / 8.0) - - -_PER_PLATFORM_MAX_PARALLEL_LTO_JOBS = { - ('Darwin', 'x86_64'): (_darwin_max_num_llvm_parallel_lto_link_jobs, - _darwin_max_num_swift_parallel_lto_link_jobs) -} - - -def max_lto_link_job_counts(): - key = _compute_system_key() - info = _PER_PLATFORM_MAX_PARALLEL_LTO_JOBS.get(key, - _return_none_fun_pair()) - return {'llvm': info[0](), 'swift': info[1]()} diff --git a/utils/swift_build_support/tests/test_host.py b/utils/swift_build_support/tests/test_host.py deleted file mode 100644 index 26141204e245f..0000000000000 --- a/utils/swift_build_support/tests/test_host.py +++ /dev/null @@ -1,61 +0,0 @@ -# test_host.py - Unit tests for swift_build_support.cmake -*-- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors - -import platform -import unittest - -import swift_build_support.host as sbs_host - - -class HostTestCase(unittest.TestCase): - - def test_system_memory(self): - # We make sure that we get an integer back. If we get an integer back, - # we know that we at least were able to get some sort of information - # from the system and it could be parsed as an integer. This is just a - # smoke test. - supported_platforms = [('Darwin', 'x86_64')] - - mem = sbs_host.system_memory() - - if (platform.system(), platform.machine()) not in supported_platforms: - self.assertIsNone(mem) - else: - self.assertIsInstance(mem, int) - - def test_lto_link_job_counts(self): - # Make sure that: - # - # 1. we get back a dictionary with two keys in it, the first called - # llvm, the other called swift. - # - # 2. The values associated with these keys is either None (if we do not - # support the platform) or is an int that is reasonable (i.e. < - # 100). The number 100 is just a heuristic number that is appropriate - # currently since LTO uses so much memory. If and when that changes, - # this number should change. - supported_platforms = [('Darwin', 'x86_64')] - reasonable_upper_bound_of_lto_threads = 100 - - result = sbs_host.max_lto_link_job_counts() - self.assertIsInstance(result, dict) - self.assertEqual(len(result), 2) - - if (platform.system(), platform.machine()) not in supported_platforms: - self.assertIsNone(result['llvm']) - self.assertIsNone(result['swift']) - return - - self.assertIsNotNone(result['llvm']) - self.assertIsNotNone(result['swift']) - self.assertIsInstance(result['llvm'], int) - self.assertIsInstance(result['swift'], int) - self.assertLess(result['llvm'], reasonable_upper_bound_of_lto_threads) - self.assertLess(result['swift'], reasonable_upper_bound_of_lto_threads) From 4450ce365ab6216b284b43afef19c8e4bd3a4add Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Sun, 2 Feb 2020 14:30:51 -0800 Subject: [PATCH 104/237] [Build System: build-script] Remove the diagnostics module from swift_build_support. --- utils/build-script | 60 ++++++++++++------- .../swift_build_support/__init__.py | 11 ---- .../swift_build_support/diagnostics.py | 34 ----------- .../host_specific_configuration.py | 9 +-- .../swift_build_support/shell.py | 17 ++++-- 5 files changed, 55 insertions(+), 76 deletions(-) delete mode 100644 utils/swift_build_support/swift_build_support/diagnostics.py diff --git a/utils/build-script b/utils/build-script index e5811049a68c0..1404db6132d4a 100755 --- a/utils/build-script +++ b/utils/build-script @@ -31,7 +31,6 @@ from build_swift.build_swift import presets import six -from swift_build_support.swift_build_support import diagnostics from swift_build_support.swift_build_support import products from swift_build_support.swift_build_support import shell from swift_build_support.swift_build_support import targets @@ -60,6 +59,25 @@ BUILD_SCRIPT_IMPL = os.path.join( # ----------------------------------------------------------------------------- # Helpers +def print_note(message, stream=sys.stdout): + """Writes a diagnostic message to the given stream. By default this + function outputs to stdout. + """ + + stream.write('[{}] NOTE: {}\n'.format(sys.argv[0], message)) + stream.flush() + + +def fatal_error(message, stream=sys.stderr): + """Writes a message to the given stream and exits. By default this + function outputs to stderr. + """ + + stream.write('[{}] ERROR: {}\n'.format(sys.argv[0], message)) + stream.flush() + sys.exit(1) + + def clean_delay(): """Provide a short delay so accidentally invoked clean builds can be canceled. @@ -156,23 +174,23 @@ def tar(source, destination): def validate_arguments(toolchain, args): if toolchain.cc is None or toolchain.cxx is None: - diagnostics.fatal( + fatal_error( "can't find clang (please install clang-3.5 or a " "later version)") if toolchain.cmake is None: - diagnostics.fatal("can't find CMake (please install CMake)") + fatal_error("can't find CMake (please install CMake)") if args.distcc: if toolchain.distcc is None: - diagnostics.fatal( + fatal_error( "can't find distcc (please install distcc)") if toolchain.distcc_pump is None: - diagnostics.fatal( + fatal_error( "can't find distcc-pump (please install distcc-pump)") if args.host_target is None or args.stdlib_deployment_targets is None: - diagnostics.fatal("unknown operating system") + fatal_error("unknown operating system") if args.symbols_package: if not os.path.isabs(args.symbols_package): @@ -181,7 +199,7 @@ def validate_arguments(toolchain, args): '(was \'{}\')'.format(args.symbols_package)) return 1 if not args.install_symroot: - diagnostics.fatal( + fatal_error( "--install-symroot is required when specifying " "--symbols-package.") @@ -193,7 +211,7 @@ def validate_arguments(toolchain, args): args.android_icu_i18n is None or \ args.android_icu_i18n_include is None or \ args.android_icu_data is None: - diagnostics.fatal( + fatal_error( "when building for Android, --android-ndk, " "--android-api-level, --android-icu-uc, " "--android-icu-uc-include, --android-icu-i18n, " @@ -210,7 +228,7 @@ def validate_arguments(toolchain, args): has_target_needing_toolchain = \ bool(sum(getattr(args, x) for x in targets_needing_toolchain)) if args.legacy_impl and has_target_needing_toolchain: - diagnostics.fatal( + fatal_error( "--legacy-impl is incompatible with building packages needing " "a toolchain (%s)" % ", ".join(targets_needing_toolchain)) @@ -351,7 +369,7 @@ class BuildScriptInvocation(object): def build_ninja(self): if not os.path.exists(self.workspace.source_dir("ninja")): - diagnostics.fatal( + fatal_error( "can't find source directory for ninja " "(tried %s)" % (self.workspace.source_dir("ninja"))) @@ -434,7 +452,7 @@ class BuildScriptInvocation(object): source_dir = self.workspace.source_dir(product_source_name) if not os.path.exists(source_dir): - diagnostics.fatal( + fatal_error( "can't find source directory for %s " "(tried %s)" % (product_name, source_dir)) @@ -745,7 +763,7 @@ class BuildScriptInvocation(object): try: config = HostSpecificConfiguration(host_target, args) except argparse.ArgumentError as e: - exit_rejecting_arguments(e.message) + exit_rejecting_arguments(six.text_type(e)) # Convert into `build-script-impl` style variables. options[host_target] = { @@ -850,7 +868,7 @@ class BuildScriptInvocation(object): try: config = HostSpecificConfiguration(host_target.name, self.args) except argparse.ArgumentError as e: - exit_rejecting_arguments(e.message) + exit_rejecting_arguments(six.text_type(e)) print("Building the standard library for: {}".format( " ".join(config.swift_stdlib_build_targets))) if config.swift_test_run_targets and ( @@ -1047,7 +1065,7 @@ def main_preset(): try: preset_parser.read_files(args.preset_file_names) except presets.PresetError as e: - diagnostics.fatal(e.message) + fatal_error(six.text_type(e)) if args.show_presets: for name in sorted(preset_parser.preset_names, @@ -1056,7 +1074,7 @@ def main_preset(): return 0 if not args.preset: - diagnostics.fatal("missing --preset option") + fatal_error("missing --preset option") args.preset_substitutions = {} for arg in args.preset_substitutions_raw: @@ -1068,12 +1086,12 @@ def main_preset(): args.preset, vars=args.preset_substitutions) except presets.PresetError as e: - diagnostics.fatal(e.message) + fatal_error(six.text_type(e)) preset_args = migration.migrate_swift_sdks(preset.args) if args.distcc and (args.cmake_c_launcher or args.cmake_cxx_launcher): - diagnostics.fatal( + fatal_error( '--distcc can not be used with' + ' --cmake-c-launcher or --cmake-cxx-launcher') @@ -1106,7 +1124,7 @@ def main_preset(): if args.reconfigure: build_script_args += ["--reconfigure"] - diagnostics.note('using preset "{}", which expands to \n\n{}\n'.format( + print_note('using preset "{}", which expands to \n\n{}\n'.format( args.preset, printable_command)) shell.call_without_sleeping(build_script_args) return 0 @@ -1139,7 +1157,7 @@ def main_normal(): xcrun_toolchain = os.environ.get('TOOLCHAINS', defaults.DARWIN_XCRUN_TOOLCHAIN) - diagnostics.note('Using toolchain {}'.format(xcrun_toolchain)) + print_note('Using toolchain {}'.format(xcrun_toolchain)) args.darwin_xcrun_toolchain = xcrun_toolchain toolchain = host_toolchain(xcrun_toolchain=args.darwin_xcrun_toolchain) @@ -1223,12 +1241,12 @@ def main_normal(): def main(): if not SWIFT_SOURCE_ROOT: - diagnostics.fatal( + fatal_error( "could not infer source root directory " + "(forgot to set $SWIFT_SOURCE_ROOT environment variable?)") if not os.path.isdir(SWIFT_SOURCE_ROOT): - diagnostics.fatal( + fatal_error( "source root directory \'" + SWIFT_SOURCE_ROOT + "\' does not exist " + "(forgot to set $SWIFT_SOURCE_ROOT environment variable?)") diff --git a/utils/swift_build_support/swift_build_support/__init__.py b/utils/swift_build_support/swift_build_support/__init__.py index 8757e35cc222b..ae8c0a28021c7 100644 --- a/utils/swift_build_support/swift_build_support/__init__.py +++ b/utils/swift_build_support/swift_build_support/__init__.py @@ -14,14 +14,3 @@ # utils/swift_build_support/ directory as a module. # # ---------------------------------------------------------------------------- - -__all__ = [ - "cmake", - "debug", - "diagnostics", - "host_specific_configuration", - "tar", - "targets", - "toolchain", - "xcrun", -] diff --git a/utils/swift_build_support/swift_build_support/diagnostics.py b/utils/swift_build_support/swift_build_support/diagnostics.py deleted file mode 100644 index 6ed4c2735c580..0000000000000 --- a/utils/swift_build_support/swift_build_support/diagnostics.py +++ /dev/null @@ -1,34 +0,0 @@ -# swift_build_support/diagnostics.py - Diagnostic Utilities -*- python -*- -# -# This source file is part of the Swift.org open source project -# -# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors -# Licensed under Apache License v2.0 with Runtime Library Exception -# -# See https://swift.org/LICENSE.txt for license information -# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -# -# ---------------------------------------------------------------------------- - -from __future__ import print_function - -import sys - - -def note(message): - """ - note(message) - - Print a diagnostic notification to the standard error stream. - """ - print(sys.argv[0] + ": note: " + message, file=sys.stderr) - sys.stderr.flush() - - -def fatal(message): - """ - fatal(message) - - Raise a fatal error. - """ - raise SystemExit(sys.argv[0] + ": fatal error: " + message) diff --git a/utils/swift_build_support/swift_build_support/host_specific_configuration.py b/utils/swift_build_support/swift_build_support/host_specific_configuration.py index ecdb437113321..75e507ac011d4 100644 --- a/utils/swift_build_support/swift_build_support/host_specific_configuration.py +++ b/utils/swift_build_support/swift_build_support/host_specific_configuration.py @@ -10,10 +10,9 @@ # # ---------------------------------------------------------------------------- +import sys from argparse import ArgumentError -import diagnostics - from .targets import StdlibDeploymentTarget @@ -64,8 +63,10 @@ def __init__(self, host_target, args): deployment_target = StdlibDeploymentTarget.get_target_for_name( deployment_target_name) if deployment_target is None: - diagnostics.fatal("unknown target: %r" % ( - deployment_target_name,)) + sys.stderr.write('ERROR: unknown target: {}\n'.format( + deployment_target_name)) + sys.stderr.flush() + sys.exit(1) # Add the SDK to use. deployment_platform = deployment_target.platform diff --git a/utils/swift_build_support/swift_build_support/shell.py b/utils/swift_build_support/swift_build_support/shell.py index 4edfce0838de4..0c340e734ce9f 100644 --- a/utils/swift_build_support/swift_build_support/shell.py +++ b/utils/swift_build_support/swift_build_support/shell.py @@ -23,14 +23,19 @@ import sys from contextlib import contextmanager -from . import diagnostics - DEVNULL = getattr(subprocess, 'DEVNULL', subprocess.PIPE) dry_run = False +def _fatal_error(message): + """Raises a SystemExit error with the given message. + """ + + raise SystemExit('ERROR: {}\n'.format(message)) + + def _quote(arg): return pipes.quote(str(arg)) @@ -84,11 +89,11 @@ def call(command, stderr=None, env=None, dry_run=None, echo=True): try: subprocess.check_call(command, env=_env, stderr=stderr) except subprocess.CalledProcessError as e: - diagnostics.fatal( + _fatal_error( "command terminated with a non-zero exit status " + str(e.returncode) + ", aborting") except OSError as e: - diagnostics.fatal( + _fatal_error( "could not execute '" + quote_command(command) + "': " + e.strerror) @@ -135,13 +140,13 @@ def capture(command, stderr=None, env=None, dry_run=None, echo=True, return e.output if optional: return None - diagnostics.fatal( + _fatal_error( "command terminated with a non-zero exit status " + str(e.returncode) + ", aborting") except OSError as e: if optional: return None - diagnostics.fatal( + _fatal_error( "could not execute '" + quote_command(command) + "': " + e.strerror) From bf8228499d498393f4a16be2a5e9e04ad93319af Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Sun, 2 Feb 2020 15:55:11 -0800 Subject: [PATCH 105/237] [Python: flake8] Update the project .flake8 config to prepare for the black code formatting tool. --- .flake8 | 104 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 31 deletions(-) diff --git a/.flake8 b/.flake8 index 24e50cd6104cb..76cf787fad90f 100644 --- a/.flake8 +++ b/.flake8 @@ -1,32 +1,74 @@ [flake8] -ignore = W291 W504 -filename = *.py, - ./utils/80+-check, - ./utils/backtrace-check, - ./benchmark/scripts/Benchmark_Driver, - ./benchmark/scripts/Benchmark_DTrace.in, - ./benchmark/scripts/Benchmark_GuardMalloc.in, - ./benchmark/scripts/Benchmark_RuntimeLeaksRunner.in, - ./utils/build-script, - ./utils/check-incremental, - ./test/Driver/Inputs/fake-toolchain/clang++, - ./utils/coverage/coverage-build-db, - ./utils/coverage/coverage-generate-data, - ./utils/coverage/coverage-query-db, - ./utils/coverage/coverage-touch-tests, - ./utils/gyb, - ./test/Driver/Inputs/fake-toolchain/ld, - ./utils/line-directive, - ./utils/swift_build_support/tests/mock-distcc, - ./docs/scripts/ns-html2rst, - ./utils/PathSanitizingFileCheck, - ./utils/recursive-lipo, - ./utils/round-trip-syntax-test, - ./utils/rth, - ./utils/run-remote, - ./utils/run-test, - ./utils/scale-test, - ./utils/submit-benchmark-results, - ./utils/update-checkout, - ./utils/viewcfg, - ./utils/symbolicate-linux-fatal, + +filename = + *.py, + + ./benchmark/scripts/Benchmark_Driver, + ./benchmark/scripts/Benchmark_DTrace.in, + ./benchmark/scripts/Benchmark_GuardMalloc.in, + ./benchmark/scripts/Benchmark_RuntimeLeaksRunner.in, + + ./docs/scripts/ns-html2rst, + + ./test/Driver/Inputs/fake-toolchain/clang++, + ./test/Driver/Inputs/fake-toolchain/ld, + + ./utils/80+-check, + ./utils/backtrace-check, + ./utils/build-script, + ./utils/check-incremental, + ./utils/coverage/coverage-build-db, + ./utils/coverage/coverage-generate-data, + ./utils/coverage/coverage-query-db, + ./utils/coverage/coverage-touch-tests, + ./utils/gyb, + ./utils/line-directive, + ./utils/PathSanitizingFileCheck, + ./utils/recursive-lipo, + ./utils/round-trip-syntax-test, + ./utils/rth, + ./utils/run-remote, + ./utils/run-test, + ./utils/scale-test, + ./utils/submit-benchmark-results, + ./utils/swift_build_support/tests/mock-distcc, + ./utils/symbolicate-linux-fatal, + ./utils/update-checkout, + ./utils/viewcfg, + + # TODO: We should be linting the lit configs. + #lit.cfg, + + # FIXME: We need to be linting these files. + #./utils/build-parser-lib, + #./utils/dev-scripts/blockifyasm, + #./utils/dev-scripts/split-cmdline, + +exclude = + .git, + __pycache__, + +ignore = + # The black tool treats slices consistently, the E203 warning is not PEP8 + # compliant (https://github.com/psf/black#slices). + E203, + + # FIXME: We should not have trailing whitespace. + W291, + + # Line breaks before binary operators are not explicitly disallowed in + # PEP8, rather it should be consistent throughout the project. The black + # tool puts them on new lines which is to be considered a best practice + # in the future. + W503, + + # Similarly ignore line break after binary operators. + W504, + + # TODO: Ignore Bugbear lints for now, but we should enable these in the + # future. + B, + +# 10% larger than the standard 80 character limit. Conforms to the black +# standard and Bugbear's B950. +max-line-length = 88 From 6c5c853339956b339026807347f7301ab8040511 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 31 Jan 2020 18:52:08 -0800 Subject: [PATCH 106/237] [ownership] Change guaranteed args from transformation terminators to not require end_borrows. For those who are unaware, a transformation terminator is a terminator like switch_enum/checked_cast_br that always dominate their successor blocks. Since they dominate their successor blocks by design and transform their input into the args form, we can validate that they obey guaranteed ownership semantics just like a forwarding instruction. Beyond removing unnecessary code bloat, this also makes it significantly more easier to optimize/work with transformation terminators when converting @owned -> @guaranteed since we do not need to find end_borrow points when the owned value is consumed. --- include/swift/SIL/SILBuilder.h | 6 ++ include/swift/SIL/SILInstruction.h | 24 ++++++ lib/SIL/DynamicCasts.cpp | 3 - lib/SIL/OwnershipUtils.cpp | 24 +++++- lib/SIL/SILOwnershipVerifier.cpp | 52 ++++++++++++- lib/SILGen/SILGenBuilder.cpp | 7 ++ lib/SILGen/SILGenBuilder.h | 9 +++ lib/SILGen/SILGenDynamicCast.cpp | 5 +- lib/SILGen/SILGenExpr.cpp | 10 ++- lib/SILGen/SILGenPattern.cpp | 4 +- test/SIL/ownership-verifier/over_consume.sil | 17 ++--- test/SIL/ownership-verifier/use_verifier.sil | 7 -- test/SILGen/switch.swift | 14 ---- test/SILGen/switch_isa.swift | 7 -- test/SILOptimizer/destroy_hoisting.sil | 1 - test/SILOptimizer/diagnose_unreachable.sil | 76 ++++++++++++++++--- .../mandatory_inlining_ownership.sil | 2 - 17 files changed, 200 insertions(+), 68 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index f7d8bf2f884df..f279acb259b18 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -802,6 +802,12 @@ class SILBuilder { } EndBorrowInst *createEndBorrow(SILLocation loc, SILValue borrowedValue) { + if (auto *arg = dyn_cast(borrowedValue)) { + if (auto *ti = arg->getSingleTerminator()) { + assert(!ti->isTransformationTerminator() && + "Transforming terminators do not have end_borrow"); + } + } return insert(new (getModule()) EndBorrowInst(getSILDebugLocation(loc), borrowedValue)); } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 4afcb22beb988..ec22b11ea77c6 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -7042,6 +7042,30 @@ class TermInst : public NonValueInstruction { bool isProgramTerminating() const; TermKind getTermKind() const { return TermKind(getKind()); } + + /// Returns true if this is a transformation terminator. + bool isTransformationTerminator() const { + switch (getTermKind()) { + case TermKind::UnwindInst: + case TermKind::UnreachableInst: + case TermKind::ReturnInst: + case TermKind::ThrowInst: + case TermKind::YieldInst: + case TermKind::TryApplyInst: + case TermKind::BranchInst: + case TermKind::CondBranchInst: + case TermKind::SwitchValueInst: + case TermKind::SwitchEnumAddrInst: + case TermKind::DynamicMethodBranchInst: + case TermKind::CheckedCastAddrBranchInst: + case TermKind::CheckedCastValueBranchInst: + return false; + case TermKind::SwitchEnumInst: + case TermKind::CheckedCastBranchInst: + return true; + } + llvm_unreachable("Covered switch isn't covered."); + } }; /// UnreachableInst - Position in the code which would be undefined to reach. diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp index fe5b9a8bf8c68..d6cfef83222a2 100644 --- a/lib/SIL/DynamicCasts.cpp +++ b/lib/SIL/DynamicCasts.cpp @@ -1254,9 +1254,7 @@ void swift::emitIndirectConditionalCastWithScalar( case CastConsumptionKind::TakeOnSuccess: break; case CastConsumptionKind::CopyOnSuccess: { - SILValue originalSuccValue = succValue; succValue = B.emitCopyValueOperation(loc, succValue); - B.emitEndBorrowOperation(loc, originalSuccValue); B.emitEndBorrowOperation(loc, srcValue); break; } @@ -1295,7 +1293,6 @@ void swift::emitIndirectConditionalCastWithScalar( StoreOwnershipQualifier::Init); break; case CastConsumptionKind::CopyOnSuccess: - B.emitEndBorrowOperation(loc, failValue); B.emitEndBorrowOperation(loc, srcValue); break; case CastConsumptionKind::BorrowAlways: diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index 8047342ad08e6..9f37d9c37bcab 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -66,6 +66,15 @@ bool swift::isGuaranteedForwardingValueKind(SILNodeKind kind) { } bool swift::isGuaranteedForwardingValue(SILValue value) { + // If we have an argument from a transforming terminator, we can forward + // guaranteed. + if (auto *arg = dyn_cast(value)) { + if (auto *ti = arg->getSingleTerminator()) { + if (ti->isTransformationTerminator()) { + return true; + } + } + } return isGuaranteedForwardingValueKind( value->getKindOfRepresentativeSILNodeInObject()); } @@ -173,9 +182,18 @@ bool swift::getUnderlyingBorrowIntroducingValues( // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isGuaranteedForwardingValue(v)) { - auto *i = v->getDefiningInstruction(); - assert(i); - llvm::transform(i->getAllOperands(), std::back_inserter(worklist), + if (auto *i = v->getDefiningInstruction()) { + llvm::transform(i->getAllOperands(), std::back_inserter(worklist), + [](const Operand &op) -> SILValue { return op.get(); }); + continue; + } + + // Otherwise, we should have a block argument that is defined by a single + // predecessor terminator. + auto *arg = cast(v); + auto *termInst = arg->getSingleTerminator(); + assert(termInst && termInst->isTransformationTerminator()); + llvm::transform(termInst->getAllOperands(), std::back_inserter(worklist), [](const Operand &op) -> SILValue { return op.get(); }); continue; } diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index f05a70f435a82..5e9139683db6e 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -334,9 +334,55 @@ bool SILValueOwnershipChecker::gatherUsers( continue; } - // Otherwise if we have a terminator, add any as uses any end_borrow to - // ensure that the subscope is completely enclsed within the super scope. We - // require all of our arguments to be either trivial or guaranteed. + // See if our forwarding terminator is a transformation terminator. If so, + // just add all of the uses of its successors to the worklist to visit as + // users. + if (ti->isTransformationTerminator()) { + for (auto &succ : ti->getSuccessors()) { + auto *succBlock = succ.getBB(); + + // If we do not have any arguments, then continue. + if (succBlock->args_empty()) + continue; + + // Otherwise, make sure that all arguments are trivial or guaranteed. If + // we fail, emit an error. + // + // TODO: We could ignore this error and emit a more specific error on + // the actual terminator. + for (auto *succArg : succBlock->getSILPhiArguments()) { + // *NOTE* We do not emit an error here since we want to allow for more + // specific errors to be found during use_verification. + // + // TODO: Add a flag that associates the terminator instruction with + // needing to be verified. If it isn't verified appropriately, assert + // when the verifier is destroyed. + auto succArgOwnershipKind = succArg->getOwnershipKind(); + if (!succArgOwnershipKind.isCompatibleWith(ownershipKind)) { + // This is where the error would go. + continue; + } + + // If we have an any value, just continue. + if (succArgOwnershipKind == ValueOwnershipKind::None) + continue; + + // Otherwise add all users of this BBArg to the worklist to visit + // recursively. + llvm::copy(succArg->getUses(), std::back_inserter(users)); + } + } + continue; + } + + // Otherwise, we are dealing with the merging of true phis. To work with + // this we will eventually need to create a notion of forwarding borrow + // scopes. + + // But until then, we validate that the argument has an end_borrow that acts + // as a subscope that is compeltely enclosed within the scopes of all + // incoming values. We require all of our arguments to be either trivial or + // guaranteed. for (auto &succ : ti->getSuccessors()) { auto *succBlock = succ.getBB(); diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index 503e6d6d9b55c..4ba29af7d3abe 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -199,6 +199,13 @@ ManagedValue SILGenBuilder::createGuaranteedPhiArgument(SILType type) { return SGF.emitManagedBorrowedArgumentWithCleanup(arg); } +ManagedValue +SILGenBuilder::createGuaranteedTransformingTerminatorArgument(SILType type) { + SILPhiArgument *arg = + getInsertionBB()->createPhiArgument(type, ValueOwnershipKind::Guaranteed); + return ManagedValue::forUnmanaged(arg); +} + ManagedValue SILGenBuilder::createAllocRef( SILLocation loc, SILType refType, bool objc, bool canAllocOnStack, ArrayRef inputElementTypes, diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index c4b8cd3f1b42e..55d3dad837e00 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -127,6 +127,15 @@ class SILGenBuilder : public SILBuilder { ManagedValue createOwnedPhiArgument(SILType type); ManagedValue createGuaranteedPhiArgument(SILType type); + /// For arguments from terminators that are "transforming terminators". These + /// types of guaranteed arguments are validated as part of the operand of the + /// transforming terminator since transforming terminators are guaranteed to + /// be the only predecessor of our parent block. + /// + /// NOTE: Two examples of transforming terminators are switch_enum, + /// checked_cast_br. + ManagedValue createGuaranteedTransformingTerminatorArgument(SILType type); + using SILBuilder::createMarkUninitialized; ManagedValue createMarkUninitialized(ValueDecl *decl, ManagedValue operand, MarkUninitializedInst::Kind muKind); diff --git a/lib/SILGen/SILGenDynamicCast.cpp b/lib/SILGen/SILGenDynamicCast.cpp index 7b3ea94ceb66b..c63c446c3be58 100644 --- a/lib/SILGen/SILGenDynamicCast.cpp +++ b/lib/SILGen/SILGenDynamicCast.cpp @@ -185,7 +185,7 @@ namespace { // argument. ManagedValue argument; if (!shouldTakeOnSuccess(consumption)) { - argument = SGF.B.createGuaranteedPhiArgument( + argument = SGF.B.createGuaranteedTransformingTerminatorArgument( origTargetTL.getLoweredType()); } else { argument = @@ -236,7 +236,8 @@ namespace { switch (consumption) { case CastConsumptionKind::BorrowAlways: case CastConsumptionKind::CopyOnSuccess: - SGF.B.createGuaranteedPhiArgument(operandValue.getType()); + SGF.B.createGuaranteedTransformingTerminatorArgument( + operandValue.getType()); handleFalse(None); break; case CastConsumptionKind::TakeAlways: diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 879c9082c6272..0986ed1f33e46 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -165,8 +165,14 @@ namespace { struct EndBorrowCleanup : Cleanup { SILValue borrowedValue; - EndBorrowCleanup(SILValue borrowedValue) - : borrowedValue(borrowedValue) {} + EndBorrowCleanup(SILValue borrowedValue) : borrowedValue(borrowedValue) { + if (auto *arg = dyn_cast(borrowedValue)) { + if (auto *ti = arg->getSingleTerminator()) { + assert(!ti->isTransformationTerminator() && + "Transforming terminators do not have end_borrow"); + } + } + } void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index 711b70d101178..bc510cd09a56d 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -1921,7 +1921,7 @@ void PatternMatchEmission::emitEnumElementObjectDispatch( SILValue eltValue; if (isPlusZero) { - origCMV = {SGF.B.createGuaranteedPhiArgument(eltTy), + origCMV = {SGF.B.createGuaranteedTransformingTerminatorArgument(eltTy), CastConsumptionKind::BorrowAlways}; } else { origCMV = {SGF.B.createOwnedPhiArgument(eltTy), @@ -1971,7 +1971,7 @@ void PatternMatchEmission::emitEnumElementObjectDispatch( if (SILBasicBlock *defaultBB = blocks.getDefaultBlock()) { SGF.B.setInsertionPoint(defaultBB); if (isPlusZero) { - SGF.B.createGuaranteedPhiArgument(src.getType()); + SGF.B.createGuaranteedTransformingTerminatorArgument(src.getType()); } else { SGF.B.createOwnedPhiArgument(src.getType()); } diff --git a/test/SIL/ownership-verifier/over_consume.sil b/test/SIL/ownership-verifier/over_consume.sil index e178c87e53d0b..26df8e67f0d8a 100644 --- a/test/SIL/ownership-verifier/over_consume.sil +++ b/test/SIL/ownership-verifier/over_consume.sil @@ -235,7 +235,6 @@ bb0(%0 : @owned $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%1 : @guaranteed $Builtin.NativeObject): - end_borrow %1 : $Builtin.NativeObject br bb3 bb2: @@ -251,7 +250,7 @@ bb3: // CHECK: Found use after free?! // CHECK: Value: %1 = begin_borrow %0 : $Optional // CHECK: Consuming User: end_borrow %1 : $Optional -// CHECK: Non Consuming User: end_borrow %3 : $Builtin.NativeObject +// CHECK: Non Consuming User: %5 = unchecked_ref_cast %3 : $Builtin.NativeObject to $Builtin.NativeObject // CHECK: Block: bb1 sil [ossa] @switch_enum_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): @@ -260,7 +259,7 @@ bb0(%0 : @owned $Optional): bb1(%2 : @guaranteed $Builtin.NativeObject): end_borrow %1 : $Optional - end_borrow %2 : $Builtin.NativeObject + %2a = unchecked_ref_cast %2 : $Builtin.NativeObject to $Builtin.NativeObject br bb3 bb2: @@ -312,7 +311,6 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): - end_borrow %1 : $SuperKlass br bb3 bb2(%2 : @owned $Builtin.NativeObject): @@ -339,7 +337,6 @@ bb1(%1 : @owned $SuperKlass): br bb3 bb2(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb3: @@ -363,11 +360,9 @@ bb0(%0 : @owned $Builtin.NativeObject): checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): - end_borrow %1 : $SuperKlass br bb3 bb2(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb3: @@ -387,7 +382,6 @@ bb0(%0 : @owned $Builtin.NativeObject): checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): - end_borrow %1 : $SuperKlass br bb3 bb2(%2 : @owned $Builtin.NativeObject): @@ -414,7 +408,6 @@ bb1(%1 : @owned $SuperKlass): br bb3 bb2(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb3: @@ -426,7 +419,7 @@ bb3: // CHECK: Found use after free?! // CHECK: Value: %1 = begin_borrow %0 : $Builtin.NativeObject // CHECK: Consuming User: end_borrow %1 : $Builtin.NativeObject -// CHECK: Non Consuming User: end_borrow %7 : $Builtin.NativeObject +// CHECK: Non Consuming User: %9 = unchecked_ref_cast %7 : $Builtin.NativeObject to $Builtin.NativeObject // CHECK: Block: bb2 sil [ossa] @checked_cast_br_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): @@ -434,13 +427,13 @@ bb0(%0 : @owned $Builtin.NativeObject): checked_cast_br %1 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%2 : @guaranteed $SuperKlass): - end_borrow %2 : $SuperKlass end_borrow %1 : $Builtin.NativeObject + %2a = unchecked_ref_cast %2 : $SuperKlass to $SuperKlass br bb3 bb2(%3 : @guaranteed $Builtin.NativeObject): end_borrow %1 : $Builtin.NativeObject - end_borrow %3 : $Builtin.NativeObject + %3a = unchecked_ref_cast %3 : $Builtin.NativeObject to $Builtin.NativeObject br bb3 bb3: diff --git a/test/SIL/ownership-verifier/use_verifier.sil b/test/SIL/ownership-verifier/use_verifier.sil index 0c95b2b8351d9..2fe7809c7caea 100644 --- a/test/SIL/ownership-verifier/use_verifier.sil +++ b/test/SIL/ownership-verifier/use_verifier.sil @@ -518,7 +518,6 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): switch_enum %1 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb2: @@ -536,7 +535,6 @@ bb0(%0 : @owned $Builtin.NativeObject): switch_enum %2 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%3 : @guaranteed $Builtin.NativeObject): - end_borrow %3 : $Builtin.NativeObject end_borrow %1 : $Builtin.NativeObject br bb3 @@ -557,7 +555,6 @@ bb0(%0 : @owned $Builtin.NativeObject): switch_enum %2 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%3 : @guaranteed $Builtin.NativeObject): - end_borrow %3 : $Builtin.NativeObject br bb3 bb2: @@ -577,7 +574,6 @@ bb0(%0 : @owned $Builtin.NativeObject): switch_enum %2 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%3 : @guaranteed $Builtin.NativeObject): - end_borrow %3 : $Builtin.NativeObject br bb3 bb2: @@ -784,11 +780,9 @@ bb6: checked_cast_br %6 : $Builtin.NativeObject to SuperKlass, bb7, bb8 bb7(%7 : @guaranteed $SuperKlass): - end_borrow %7 : $SuperKlass br bb9 bb8(%8 : @guaranteed $Builtin.NativeObject): - end_borrow %8 : $Builtin.NativeObject br bb9 bb9: @@ -1195,4 +1189,3 @@ bb0(%0 : @guaranteed $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %9999 = tuple() return %9999 : $() } - diff --git a/test/SILGen/switch.swift b/test/SILGen/switch.swift index eacc3dd1d08f4..ba883822e6610 100644 --- a/test/SILGen/switch.swift +++ b/test/SILGen/switch.swift @@ -468,17 +468,14 @@ func test_isa_class_1(x: B) { case is D1 where runced(): // CHECK: function_ref @$s6switch1ayyF // CHECK: destroy_value [[CAST_D1_COPY]] - // CHECK: end_borrow [[CAST_D1]] // CHECK: br [[CONT:bb[0-9]+]] a() // CHECK: [[NO_CASE1]]: // CHECK-NEXT: destroy_value [[CAST_D1_COPY]] - // CHECK-NEXT: end_borrow [[CAST_D1]] // CHECK: br [[NEXT_CASE:bb[0-9]+]] // CHECK: [[IS_NOT_D1]]([[CASTFAIL_D1:%.*]] : @guaranteed $B): - // CHECK-NEXT: end_borrow [[CASTFAIL_D1]] // CHECK-NEXT: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: @@ -492,7 +489,6 @@ func test_isa_class_1(x: B) { b() // CHECK: [[IS_NOT_D2]]([[CASTFAIL_D2:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[CASTFAIL_D2]] // CHECK: checked_cast_br [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] case is E where funged(): // CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E): @@ -508,11 +504,9 @@ func test_isa_class_1(x: B) { // CHECK: [[NO_CASE3]]: // CHECK-NEXT: destroy_value [[CAST_E_COPY]] - // CHECK-NEXT: end_borrow // CHECK: br [[NEXT_CASE:bb[0-9]+]] // CHECK: [[IS_NOT_E]]([[NOTCAST_E:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOTCAST_E]] // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: @@ -524,12 +518,10 @@ func test_isa_class_1(x: B) { // CHECK: function_ref @$s6switch1dyyF // CHECK-NEXT: apply // CHECK: destroy_value [[CAST_C_COPY]] - // CHECK: end_borrow [[CAST_C]] // CHECK: br [[CONT]] d() // CHECK: [[IS_NOT_C]]([[NOCAST_C:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_C]] default: // CHECK: function_ref @$s6switch1eyyF // CHECK: br [[CONT]] @@ -570,7 +562,6 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: br [[NEXT_CASE:bb5]] // CHECK: [[IS_NOT_D1]]([[NOCAST_D1:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_D1]] // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: @@ -589,7 +580,6 @@ func test_isa_class_2(x: B) -> AnyObject { return y // CHECK: [[IS_NOT_D2]]([[NOCAST_D2:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_D2]] // CHECK: checked_cast_br [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] case let y as E where funged(): // CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E): @@ -604,7 +594,6 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: [[RET:%.*]] = init_existential_ref [[CAST_E_COPY_COPY]] // CHECK: end_borrow [[BORROWED_CAST_E_COPY]] // CHECK: destroy_value [[CAST_E_COPY]] - // CHECK: end_borrow [[CAST_E]] // CHECK: br [[CONT]]([[RET]] : $AnyObject) c() return y @@ -614,7 +603,6 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: br [[NEXT_CASE:bb[0-9]+]] // CHECK: [[IS_NOT_E]]([[NOCAST_E:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_E]] // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]] @@ -633,7 +621,6 @@ func test_isa_class_2(x: B) -> AnyObject { return y // CHECK: [[IS_NOT_C]]([[NOCAST_C:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_C]] default: // CHECK: function_ref @$s6switch1eyyF // CHECK: [[X_COPY_2:%.*]] = copy_value [[X]] @@ -1166,7 +1153,6 @@ func address_only_with_trivial_subtype(_ a: TrivialSingleCaseEnum, _ value: Any) // CHECK: switch_enum [[TUP_0_VAL]] // // CHECK: bb1([[CASE_VAL:%.*]] : -// CHECK-NEXT: end_borrow [[CASE_VAL]] // CHECK-NEXT: destroy_addr [[TUP_1]] // CHECK-NEXT: end_borrow [[TUP_0_VAL]] // CHECK-NEXT: destroy_addr [[TUP_0]] diff --git a/test/SILGen/switch_isa.swift b/test/SILGen/switch_isa.swift index 54a07371eee3c..b73abc34fd697 100644 --- a/test/SILGen/switch_isa.swift +++ b/test/SILGen/switch_isa.swift @@ -71,8 +71,6 @@ func guardFn(_ l: D, _ r: D) -> Bool { return true } // CHECK: [[GUARD_YES]]: // CHECK-NEXT: destroy_value [[L2]] // CHECK-NEXT: destroy_value [[R2]] -// CHECK-NEXT: end_borrow [[L]] -// CHECK-NEXT: end_borrow [[R]] // CHECK-NEXT: end_borrow [[BORROWED_TUP]] // CHECK-NEXT: destroy_value [[TUP]] // CHECK-NEXT: br [[EXIT:bb[0-9]+]] @@ -80,16 +78,11 @@ func guardFn(_ l: D, _ r: D) -> Bool { return true } // CHECK: [[GUARD_NO]]: // CHECK-NEXT: destroy_value [[L2]] // CHECK-NEXT: destroy_value [[R2]] -// TODO: Infer end_borrow from the input begin_borrow. This should be eliminated. -// CHECK-NEXT: end_borrow [[L]] -// CHECK-NEXT: end_borrow [[R]] // CHECK-NEXT: end_borrow [[BORROWED_TUP]] // CHECK-NEXT: br [[CONT:bb[0-9]+]] // // CHECK: [[L_CAST_NO]]([[LFAIL:%.*]] : @guaranteed $B): -// CHECK-NEXT: end_borrow [[LFAIL]] // CHECK-NEXT: destroy_value [[R2]] -// CHECK-NEXT: end_borrow [[R]] // CHECK-NEXT: end_borrow [[BORROWED_TUP]] // CHECK-NEXT: br [[CONT]] // diff --git a/test/SILOptimizer/destroy_hoisting.sil b/test/SILOptimizer/destroy_hoisting.sil index e97dc0e91aaac..f85bb5ffd2c3f 100644 --- a/test/SILOptimizer/destroy_hoisting.sil +++ b/test/SILOptimizer/destroy_hoisting.sil @@ -131,7 +131,6 @@ bb0(%0 : $*S, %1 : @owned $S, %2 : @guaranteed $TwoCases): bb1(%4 : @guaranteed $X): %5 = enum $TwoCases, #TwoCases.B!enumelt store %1 to [assign] %0 : $*S - end_borrow %4 : $X br bb3 bb2: diff --git a/test/SILOptimizer/diagnose_unreachable.sil b/test/SILOptimizer/diagnose_unreachable.sil index 7045ca9e368ab..be1a304fe1be8 100644 --- a/test/SILOptimizer/diagnose_unreachable.sil +++ b/test/SILOptimizer/diagnose_unreachable.sil @@ -783,23 +783,20 @@ bb2: unreachable } -// Test propagation of guaranteed phi arguments. The nested end_borrow -// must be removed, even with the outer borrow is *not* a function -// argument. - enum EnumWithB { case A(B) func testit() -> Int } -// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// Test propagation of guaranteed phi arguments. The nested end_borrow +// must be removed, even with the outer borrow is *not* a function +// argument. +// +// CHECK-LABEL: sil hidden [ossa] @testEliminatePropagateGuaranteedPhiAfterSwitch : $@convention(method) (@guaranteed EnumWithB) -> () { // CHECK: bb1([[PHI:%.*]] : @guaranteed $B): -// CHECK: br bb2 -// CHECK: bb2: -// CHECK: end_borrow [[PHI]] : $B // CHECK-NOT: end_borrow -// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' -sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK-LABEL: } // end sil function 'testEliminatePropagateGuaranteedPhiAfterSwitch' +sil hidden [ossa] @testEliminatePropagateGuaranteedPhiAfterSwitch : $@convention(method) (@guaranteed EnumWithB) -> () { bb0(%0 : @guaranteed $EnumWithB): switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 @@ -807,8 +804,67 @@ bb1(%2 : @guaranteed $B): br bb3(%2 : $B) bb3(%4 : @guaranteed $B): + %99 = tuple () end_borrow %4 : $B + return %99 : $() +} + +// CHECK-LABEL: sil hidden [ossa] @testEliminatePropagateBeginBorrowGuaranteedPhiAfterSwitch : $@convention(method) (@owned B) -> () { +// CHECK: [[BORROW:%.*]] = begin_borrow +// CHECK: end_borrow [[BORROW]] +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testEliminatePropagateBeginBorrowGuaranteedPhiAfterSwitch' +sil hidden [ossa] @testEliminatePropagateBeginBorrowGuaranteedPhiAfterSwitch : $@convention(method) (@owned B) -> () { +bb0(%0 : @owned $B): + %1 = begin_borrow %0 : $B + br bb1(%1 : $B) + +bb1(%2 : @guaranteed $B): + br bb3(%2 : $B) + +bb3(%3 : @guaranteed $B): + %99 = tuple () + end_borrow %3 : $B end_borrow %2 : $B + end_borrow %1 : $B + destroy_value %0 : $B + return %99 : $() +} + +// Make sure we do not look for end_borrow when looking at a switch_enum, switch_enum. +// +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhiSwitchEnum : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK: bb1([[PHI:%.*]] : @guaranteed $B): +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhiSwitchEnum' +sil hidden [ossa] @testPropagateGuaranteedPhiSwitchEnum : $@convention(method) (@guaranteed EnumWithB) -> () { +bb0(%0 : @guaranteed $EnumWithB): + switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 + +bb1(%2 : @guaranteed $B): + %3 = enum $EnumWithB, #EnumWithB.A!enumelt.1, %2 : $B + switch_enum %3 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb3 + +bb3(%4 : @guaranteed $B): %99 = tuple () return %99 : $() } + +// Make sure that we can handle iterated end_borrow. +// +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed B) -> () { +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' +sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed B) -> () { +bb0(%0 : @guaranteed $B): + br bb1(%0 : $B) + +bb1(%1 : @guaranteed $B): + br bb2(%1 : $B) + +bb2(%2 : @guaranteed $B): + %99 = tuple () + end_borrow %2 : $B + end_borrow %1 : $B + return %99 : $() +} diff --git a/test/SILOptimizer/mandatory_inlining_ownership.sil b/test/SILOptimizer/mandatory_inlining_ownership.sil index 8f5429b2869dc..2eceda9595f66 100644 --- a/test/SILOptimizer/mandatory_inlining_ownership.sil +++ b/test/SILOptimizer/mandatory_inlining_ownership.sil @@ -396,7 +396,6 @@ bb3: // // CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] : @guaranteed // CHECK-NEXT: [[CAST_VALUE_COPY:%.*]] = copy_value [[CAST_VALUE]] -// CHECK-NEXT: end_borrow [[CAST_VALUE]] // CHECK-NEXT: end_borrow [[RELOADED_ARG]] // CHECK-NEXT: store [[CAST_VALUE_COPY]] to [init] [[DEST_ADDR]] // CHECK-NEXT: destroy_addr [[DEST_ADDR]] @@ -404,7 +403,6 @@ bb3: // CHECK-NEXT: br [[CONT_BB:bb[0-9]+]] // // CHECK: [[FAILURE_BB]]([[FAILURE_BORROWED_ARG:%.*]] : -// CHECK-NEXT: end_borrow [[FAILURE_BORROWED_ARG]] // CHECK-NEXT: end_borrow [[RELOADED_ARG]] // CHECK-NEXT: destroy_addr [[SRC_ADDR]] // CHECK-NEXT: br [[CONT_BB]] From 1ed9f7f39326d1fe7fbd5431ca339fddf9dd98a5 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 2 Feb 2020 17:47:38 -0800 Subject: [PATCH 107/237] [ownership] Trivial/Any have both been merged in ValueOwnershipKind::None for a long time so these code paths are unnecessary. Since non-payloaded cases of non-trivial enums have ValueOwnershipKind::None ownership, the compatibility maps that we return cause this case to just work as expected due to the way we merge. --- lib/SIL/OperandOwnership.cpp | 73 +++++------------------------------- 1 file changed, 10 insertions(+), 63 deletions(-) diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index 120199e4fde5c..9be5f357ddd79 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -87,8 +87,6 @@ class OperandOwnershipKindClassifier return visitForwardingInst(i, i->getAllOperands()); } - OperandOwnershipKindMap - visitEnumArgument(ValueOwnershipKind requiredConvention); OperandOwnershipKindMap visitApplyParameter(ValueOwnershipKind requiredConvention, UseLifetimeConstraint requirement); @@ -409,17 +407,9 @@ OperandOwnershipKindClassifier::checkTerminatorArgumentMatchesDestBB( // Grab the ownership kind of the destination block. ValueOwnershipKind destBlockArgOwnershipKind = destBB->getArgument(opIndex)->getOwnershipKind(); - - // Then if we do not have an enum, make sure that the conventions match. - if (!getType().getEnumOrBoundGenericEnum()) { - auto lifetimeConstraint = - destBlockArgOwnershipKind.getForwardingLifetimeConstraint(); - return Map::compatibilityMap(destBlockArgOwnershipKind, lifetimeConstraint); - } - - // Otherwise, we need to properly handle the sum type nature of enum - // arguments. - return visitEnumArgument(destBlockArgOwnershipKind); + auto lifetimeConstraint = + destBlockArgOwnershipKind.getForwardingLifetimeConstraint(); + return Map::compatibilityMap(destBlockArgOwnershipKind, lifetimeConstraint); } OperandOwnershipKindMap @@ -548,12 +538,6 @@ OperandOwnershipKindClassifier::visitReturnInst(ReturnInst *ri) { return Map(); auto base = *mergedBase; - - // TODO: This may not be needed once trivial is any. - if (getType().getEnumOrBoundGenericEnum()) { - return visitEnumArgument(base); - } - return Map::compatibilityMap(base, base.getForwardingLifetimeConstraint()); } @@ -641,57 +625,20 @@ OperandOwnershipKindMap OperandOwnershipKindClassifier::visitCallee( llvm_unreachable("Unhandled ParameterConvention in switch."); } -// Visit an enum value that is passed at argument position, including block -// arguments, apply arguments, and return values. -// -// The operand definition's ownership kind may be known to be "trivial", -// but it is still valid to pass that enum to a argument nontrivial type. -// For example: -// -// %val = enum $Optional, #Optional.none // trivial ownership -// apply %f(%val) : (@owned Optional) // owned argument -OperandOwnershipKindMap OperandOwnershipKindClassifier::visitEnumArgument( - ValueOwnershipKind requiredKind) { - // Begin with an empty map. - OperandOwnershipKindMap map; - - // The operand has a non-trivial ownership kind. It must match the argument - // convention. - if (requiredKind != ValueOwnershipKind::Owned) { - map.addCompatibilityConstraint(ValueOwnershipKind::Owned, - UseLifetimeConstraint::MustBeLive); - } else { - map.addCompatibilityConstraint(ValueOwnershipKind::Owned, - UseLifetimeConstraint::MustBeInvalidated); - } - map.addCompatibilityConstraint(ValueOwnershipKind::Guaranteed, - UseLifetimeConstraint::MustBeLive); - map.addCompatibilityConstraint(ValueOwnershipKind::Unowned, - UseLifetimeConstraint::MustBeLive); - return map; -} - // We allow for trivial cases of enums with non-trivial cases to be passed in // non-trivial argument positions. This fits with modeling of a // SILFunctionArgument as a phi in a global program graph. OperandOwnershipKindMap OperandOwnershipKindClassifier::visitApplyParameter( ValueOwnershipKind kind, UseLifetimeConstraint requirement) { - // Check if we have an enum. If not, then we just check against the passed in - // convention. - if (!getType().getEnumOrBoundGenericEnum()) { - // We allow for owned to be passed to apply parameters. - if (kind != ValueOwnershipKind::Owned) { - return Map::compatibilityMap( - {{kind, requirement}, - {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeLive}}); - } - return Map::compatibilityMap(kind, requirement); + // Check against the passed in convention. We allow for owned to be passed to + // apply parameters. + if (kind != ValueOwnershipKind::Owned) { + return Map::compatibilityMap( + {{kind, requirement}, + {ValueOwnershipKind::Owned, UseLifetimeConstraint::MustBeLive}}); } - - // Otherwise consider that we may have a payload with a trivial case - // that has other non-trivial cases. - return visitEnumArgument(kind); + return Map::compatibilityMap(kind, requirement); } // Handle Apply and TryApply. From f9b0f99cda3e98864b41628336fa0cddbbcca6cb Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 27 Jan 2020 14:02:32 -0800 Subject: [PATCH 108/237] [semantic-arc] Attempt to handle indirect_mutating parameters that are never written to. It only handles local writes, so we can not handle chains of inout parameters. That being said, I wonder if we could hook this up to an analysis to determine if the inout has this same property in callees. --- .../Mandatory/SemanticARCOpts.cpp | 136 +++++++++++++++++- test/SILOptimizer/semantic-arc-opts.sil | 55 +++++++ 2 files changed, 187 insertions(+), 4 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp index 9915714f5c575..0640a2af98fb1 100644 --- a/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp @@ -17,6 +17,7 @@ #include "swift/SIL/DebugUtils.h" #include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" @@ -150,6 +151,129 @@ LiveRange::LiveRange(SILValue value) } } +//===----------------------------------------------------------------------===// +// Address Written To Analysis +//===----------------------------------------------------------------------===// + +namespace { + +/// A simple analysis that checks if a specific def (in our case an inout +/// argument) is ever written to. This is conservative, local and processes +/// recursively downwards from def->use. +struct IsAddressWrittenToDefUseAnalysis { + llvm::SmallDenseMap isWrittenToCache; + + bool operator()(SILValue value) { + auto iter = isWrittenToCache.try_emplace(value, true); + + // If we are already in the map, just return that. + if (!iter.second) + return iter.first->second; + + // Otherwise, compute our value, cache it and return. + bool result = isWrittenToHelper(value); + iter.first->second = result; + return result; + } + +private: + bool isWrittenToHelper(SILValue value); +}; + +} // end anonymous namespace + +bool IsAddressWrittenToDefUseAnalysis::isWrittenToHelper(SILValue initialValue) { + SmallVector worklist(initialValue->getUses()); + while (!worklist.empty()) { + auto *op = worklist.pop_back_val(); + SILInstruction *user = op->getUser(); + + if (Projection::isAddressProjection(user) || + isa(user)) { + for (SILValue r : user->getResults()) { + llvm::copy(r->getUses(), std::back_inserter(worklist)); + } + continue; + } + + if (auto *oeai = dyn_cast(user)) { + // Mutable access! + if (oeai->getAccessKind() != OpenedExistentialAccess::Immutable) { + return true; + } + + // Otherwise, look through it and continue. + llvm::copy(oeai->getUses(), std::back_inserter(worklist)); + continue; + } + + // load_borrow and incidental uses are fine as well. + if (isa(user) || isIncidentalUse(user)) { + continue; + } + + // Look through immutable begin_access. + if (auto *bai = dyn_cast(user)) { + // If we do not have a read, return true. + if (bai->getAccessKind() != SILAccessKind::Read) { + return true; + } + + // Otherwise, add the users to the worklist and continue. + llvm::copy(bai->getUses(), std::back_inserter(worklist)); + continue; + } + + // As long as we do not have a load [take], we are fine. + if (auto *li = dyn_cast(user)) { + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { + return true; + } + continue; + } + + // If we have a FullApplySite, see if we use the value as an + // indirect_guaranteed parameter. If we use it as inout, we need + // interprocedural analysis that we do not perform here. + if (auto fas = FullApplySite::isa(user)) { + if (fas.getArgumentConvention(*op) == + SILArgumentConvention::Indirect_In_Guaranteed) + continue; + + // Otherwise, be conservative and return true. + return true; + } + + // Copy addr that read are just loads. + if (auto *cai = dyn_cast(user)) { + // If our value is the destination, this is a write. + if (cai->getDest() == op->get()) { + return true; + } + + // Ok, so we are Src by process of elimination. Make sure we are not being + // taken. + if (cai->isTakeOfSrc()) { + return true; + } + + // Otherwise, we are safe and can continue. + continue; + } + + // If we did not recognize the user, just return conservatively that it was + // written to. + LLVM_DEBUG(llvm::dbgs() + << "Function: " << user->getFunction()->getName() << "\n"); + LLVM_DEBUG(llvm::dbgs() << "Value: " << op->get()); + LLVM_DEBUG(llvm::dbgs() << "Unknown instruction!: " << *user); + return true; + } + + // Ok, we finished our worklist and this address is not being written to. + return false; +} + //===----------------------------------------------------------------------===// // Implementation //===----------------------------------------------------------------------===// @@ -185,6 +309,7 @@ struct SemanticARCOptVisitor SILFunction &F; Optional TheDeadEndBlocks; ValueLifetimeAnalysis::Frontier lifetimeFrontier; + IsAddressWrittenToDefUseAnalysis isAddressWrittenToDefUseAnalysis; explicit SemanticARCOptVisitor(SILFunction &F) : F(F) {} @@ -712,12 +837,15 @@ class StorageGuaranteesLoadVisitor return answer(false); } + // If we have an inout parameter that isn't ever actually written to, return + // false. + if (arg->getKnownParameterInfo().isIndirectMutating()) { + return answer(ARCOpt.isAddressWrittenToDefUseAnalysis(arg)); + } + // TODO: This should be extended: // - // 1. We should be able to analyze inout arguments and see if the inout - // argument is never actually written to in a flow insensitive way. - // - // 2. We should be able to analyze in arguments and see if they are only + // 1. We should be able to analyze in arguments and see if they are only // ever destroyed at the end of the function. In such a case, we may be // able to also to promote load [copy] from such args to load_borrow. return answer(true); diff --git a/test/SILOptimizer/semantic-arc-opts.sil b/test/SILOptimizer/semantic-arc-opts.sil index 2c4e0e9bafeed..bb6824f988d66 100644 --- a/test/SILOptimizer/semantic-arc-opts.sil +++ b/test/SILOptimizer/semantic-arc-opts.sil @@ -1085,3 +1085,58 @@ bb0(%0 : @owned $Klass): %9999 = tuple() return %9999 : $() } + +// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_1 : $@convention(thin) (@inout NativeObjectPair) -> () { +// CHECK-NOT: load [copy] +// CHECK: load_borrow +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'inout_argument_never_written_to_1' +sil [ossa] @inout_argument_never_written_to_1 : $@convention(thin) (@inout NativeObjectPair) -> () { +bb0(%0 : $*NativeObjectPair): + %2 = load [copy] %0 : $*NativeObjectPair + (%3, %4) = destructure_struct %2 : $NativeObjectPair + + %5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %5(%4) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + + destroy_value %3 : $Builtin.NativeObject + destroy_value %4 : $Builtin.NativeObject + + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_2 : $@convention(thin) (@inout NativeObjectPair) -> () { +// CHECK-NOT: load [copy] +// CHECK: load_borrow +// CHECK-NOT: load [copy] +// CHECK: } // end sil function 'inout_argument_never_written_to_2' +sil [ossa] @inout_argument_never_written_to_2 : $@convention(thin) (@inout NativeObjectPair) -> () { +bb0(%0 : $*NativeObjectPair): + %2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1 + %3 = load [copy] %2 : $*Builtin.NativeObject + %5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} + +// We should be able to handle this, but we can not until we teach the ARC +// optimizer to avoid writes not within the load [copy] region. +// +// CHECK-LABEL: sil [ossa] @inout_argument_never_written_to_3 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () { +// CHECK: load [copy] +// CHECK: } // end sil function 'inout_argument_never_written_to_3' +sil [ossa] @inout_argument_never_written_to_3 : $@convention(thin) (@inout NativeObjectPair, @owned Builtin.NativeObject) -> () { +bb0(%0 : $*NativeObjectPair, %1 : @owned $Builtin.NativeObject): + %2 = struct_element_addr %0 : $*NativeObjectPair, #NativeObjectPair.obj1 + %3 = load [copy] %2 : $*Builtin.NativeObject + %5 = function_ref @guaranteed_user : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + apply %5(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () + destroy_value %3 : $Builtin.NativeObject + store %1 to [assign] %2 : $*Builtin.NativeObject + %9999 = tuple() + return %9999 : $() +} From f300b80665070579a2e41ca10ff79de8ad813e97 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 2 Feb 2020 20:32:52 -0800 Subject: [PATCH 109/237] build: prevent `FILE_DEPENDS` from being used The `FILE_DEPENDS` option is used in exactly one location: swiftCore. This prevention sets the groundwork for removing the option. Dependencies should be tracked at the target level. --- cmake/modules/AddSwift.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/modules/AddSwift.cmake b/cmake/modules/AddSwift.cmake index a334144423a33..267f09a3e6fd8 100644 --- a/cmake/modules/AddSwift.cmake +++ b/cmake/modules/AddSwift.cmake @@ -1541,6 +1541,9 @@ function(add_swift_host_library name) if(ASHL_DEPENDS) message(SEND_ERROR "library ${name} is using DEPENDS parameter which is deprecated. Please use add_dependencies instead") endif() + if(ASHL_FILE_DEPENDS) + message(SEND_ERROR "library ${name} is using FILE_DEPENDS parameter which is deprecated.") + endif() if(ASHL_LINK_LIBRARIES) message(SEND_ERROR "library ${name} is using LINK_LIBRARIES parameter which is deprecated. Please use target_link_libraries instead") endif() @@ -1560,7 +1563,6 @@ function(add_swift_host_library name) SDK ${SWIFT_HOST_VARIANT_SDK} ARCHITECTURE ${SWIFT_HOST_VARIANT_ARCH} LLVM_LINK_COMPONENTS ${ASHL_LLVM_LINK_COMPONENTS} - FILE_DEPENDS ${ASHL_FILE_DEPENDS} INSTALL_IN_COMPONENT "dev" ) From d8c9ef8f3f8f90c8c0b6490431b31388e788da14 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 2 Feb 2020 20:40:03 -0800 Subject: [PATCH 110/237] [Type checker] Return folded expressions even on failure. This is needed by code completion and the almost-removed CSDiag diagnostics generation. --- lib/Sema/TypeCheckConstraints.cpp | 27 +++++++++++++++++++++------ lib/Sema/TypeChecker.h | 3 ++- test/Constraints/closures.swift | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 6c8da6ea38877..d32e92f8ca34c 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2068,22 +2068,34 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, SolutionApplicationTarget target( expr, convertTypePurpose, convertType, options.contains(TypeCheckExprFlags::IsDiscarded)); - auto resultType = typeCheckExpression(target, dc, options, listener, baseCS); - if (!resultType) { + bool unresolvedTypeExprs = false; + auto resultTarget = typeCheckExpression( + target, dc, unresolvedTypeExprs, options, listener, baseCS); + if (!resultTarget) { + expr = target.getAsExpr(); return Type(); } - expr = resultType->getAsExpr(); + expr = resultTarget->getAsExpr(); + + // HACK for clients that want unresolved types. + if (unresolvedTypeExprs) { + return ErrorType::get(dc->getASTContext()); + } + + return expr->getType(); } Optional TypeChecker::typeCheckExpression( - SolutionApplicationTarget target, + SolutionApplicationTarget &target, DeclContext *dc, + bool &unresolvedTypeExprs, TypeCheckExprOptions options, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { + unresolvedTypeExprs = false; auto &Context = dc->getASTContext(); Expr *expr = target.getAsExpr(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); @@ -2092,6 +2104,7 @@ TypeChecker::typeCheckExpression( // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) { + target.setExpr(expr); return None; } @@ -2155,8 +2168,10 @@ TypeChecker::typeCheckExpression( expr, target.getExprContextualTypePurpose(), convertTo, target.isDiscardedExpr()); auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables); - if (!viable) + if (!viable) { + target.setExpr(expr); return None; + } // If the client allows the solution to have unresolved type expressions, // check for them now. We cannot apply the solution with unresolved TypeVars, @@ -2165,7 +2180,7 @@ TypeChecker::typeCheckExpression( (viable->size() != 1 || (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { // FIXME: This hack should only be needed for CSDiag. - target.getAsExpr()->setType(ErrorType::get(Context)); + unresolvedTypeExprs = true; return target; } diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 3ac30b427267b..cb799f8e271b1 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -834,8 +834,9 @@ class TypeChecker final { constraints::ConstraintSystem *baseCS = nullptr); static Optional - typeCheckExpression(constraints::SolutionApplicationTarget target, + typeCheckExpression(constraints::SolutionApplicationTarget &target, DeclContext *dc, + bool &unresolvedTypeExprs, TypeCheckExprOptions options = TypeCheckExprOptions(), ExprTypeCheckListener *listener = nullptr, constraints::ConstraintSystem *baseCS = nullptr); diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 085931b78f0e2..fb15d6de202ce 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -186,7 +186,7 @@ func testMap() { } // "UnresolvedDot" "in wrong phase" assertion from verifier -[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(<>)'}} +[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(_)'}} From 52da2d6c4cfc2f7b3467095ef7969bdfdc2e773e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 2 Feb 2020 22:31:27 -0800 Subject: [PATCH 111/237] [Constraint system] Extend SolutionApplicationTarget with an init pattern. Initializer expressions are always type checked against a pattern, so also include the pattern in the solution application target and use that to compute the contextual type from the pattern type. --- lib/Sema/ConstraintSystem.cpp | 26 ++++++++++++++++++ lib/Sema/ConstraintSystem.h | 16 +++++++++++ lib/Sema/TypeCheckConstraints.cpp | 44 +++++++++---------------------- 3 files changed, 55 insertions(+), 31 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 2f19661ed7fcf..cfeea51f9d551 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3996,6 +3996,32 @@ SolutionApplicationTarget::SolutionApplicationTarget( expression.isDiscarded = isDiscarded; } +SolutionApplicationTarget SolutionApplicationTarget::forInitialization( + Expr *initializer, Type patternType, Pattern *pattern) { + // Determine the contextual type for the initialization. + TypeLoc contextualType; + if (!isa(pattern) && + patternType && !patternType->isHole()) { + contextualType = TypeLoc::withoutLoc(patternType); + + // Only provide a TypeLoc if it makes sense to allow diagnostics. + if (auto *typedPattern = dyn_cast(pattern)) { + const Pattern *inner = typedPattern->getSemanticsProvidingPattern(); + if (isa(inner) || isa(inner)) { + contextualType = typedPattern->getTypeLoc(); + if (!contextualType.getType()) + contextualType.setType(patternType); + } + } + } + + SolutionApplicationTarget target( + initializer, CTP_Initialization, contextualType, + /*isDiscarded=*/false); + target.expression.pattern = pattern; + return target; +} + bool SolutionApplicationTarget::contextualTypeIsOnlyAHint( bool isOpaqueReturnType) const { assert(kind == Kind::expression); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 7d733b80df614..5bd446275e17b 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1147,6 +1147,7 @@ class SolutionApplicationTarget { union { struct { + /// The expression being type-checked. Expr *expression; /// The purpose of the contextual type. @@ -1155,6 +1156,10 @@ class SolutionApplicationTarget { /// The type to which the expression should be converted. TypeLoc convertType; + /// When initializing a pattern from the expression, this is the + /// pattern. + Pattern *pattern = nullptr; + /// Whether the expression result will be discarded at the end. bool isDiscarded; } expression; @@ -1185,6 +1190,10 @@ class SolutionApplicationTarget { function.body = body; } + /// Form a target for the initialization of a pattern from an expression. + static SolutionApplicationTarget forInitialization( + Expr *initializer, Type patternType, Pattern *pattern); + Expr *getAsExpr() const { switch (kind) { case Kind::expression: @@ -1236,6 +1245,13 @@ class SolutionApplicationTarget { expression.convertType = type; } + /// For a pattern initialization target, retrieve the pattern. + Pattern *getInitializationPattern() const { + assert(kind == Kind::expression); + assert(expression.contextualPurpose == CTP_Initialization); + return expression.pattern; + } + /// Whether the contextual type is only a hint, rather than a type bool contextualTypeIsOnlyAHint(bool isOpaqueReturnType) const; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index d32e92f8ca34c..32b9281a60992 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2679,44 +2679,24 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, if (isa(pattern)) { flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; - } else if (patternType && !patternType->isEqual(Context.TheUnresolvedType)) { - contextualType = TypeLoc::withoutLoc(patternType); - - // If we already had an error, don't repeat the problem. - if (contextualType.getType()->hasError()) - return true; - - // Allow the initializer expression to establish the underlying type of an - // opaque type. - if (auto opaqueType = patternType->getAs()){ - flags |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; - } - - // Only provide a TypeLoc if it makes sense to allow diagnostics. - if (auto *typedPattern = dyn_cast(pattern)) { - const Pattern *inner = typedPattern->getSemanticsProvidingPattern(); - if (isa(inner) || isa(inner)) { - contextualType = typedPattern->getTypeLoc(); - if (!contextualType.getType()) - contextualType.setType(patternType); - } - } } // Type-check the initializer. - auto resultTy = typeCheckExpression(initializer, DC, contextualType, - contextualPurpose, flags, &listener); + auto target = SolutionApplicationTarget::forInitialization( + initializer, patternType, pattern); + bool unresolvedTypeExprs = false; + auto resultTarget = typeCheckExpression(target, DC, unresolvedTypeExprs, + flags, &listener); + + if (resultTarget) { + initializer = resultTarget->getAsExpr(); - if (resultTy) { TypeResolutionOptions options = isa(initializer->getSemanticsProvidingExpr()) ? TypeResolverContext::EditorPlaceholderExpr : TypeResolverContext::InExpression; options |= TypeResolutionFlags::OverrideType; - // FIXME: initTy should be the same as resultTy; now that typeCheckExpression() - // returns a Type and not bool, we should be able to simplify the listener - // implementation here. auto initTy = listener.getPatternInitType(nullptr); if (initTy->hasDependentMember()) return true; @@ -2730,15 +2710,17 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, } else { return true; } + } else { + initializer = target.getAsExpr(); } - if (!resultTy && !initializer->getType()) + if (!resultTarget && !initializer->getType()) initializer->setType(ErrorType::get(Context)); // If the type of the pattern is inferred, assign error types to the pattern // and its variables, to prevent it from being referenced by the constraint // system. - if (!resultTy && + if (!resultTarget && (patternType->hasUnresolvedType() || patternType->hasUnboundGenericType())) { pattern->setType(ErrorType::get(Context)); @@ -2754,7 +2736,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, }); } - return !resultTy; + return !resultTarget; } bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD, From 365511cfb42bec8fe90132ae7af0cd61a2c6d0aa Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 2 Feb 2020 22:41:10 -0800 Subject: [PATCH 112/237] [Constraint system] Remove TypeCheckExprFlags::ExpressionTypeMustBeOptional. This result can be computed from the pattern of an initializer expression, so do that instead. --- lib/Sema/ConstraintSystem.h | 7 +++++++ lib/Sema/TypeCheckConstraints.cpp | 16 ++-------------- lib/Sema/TypeChecker.h | 4 ---- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 5bd446275e17b..239a03e010a98 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1252,6 +1252,13 @@ class SolutionApplicationTarget { return expression.pattern; } + /// Whether this is an initialization for an Optional.Some pattern. + bool isOptionalSomePatternInit() const { + return kind == Kind::expression && + expression.contextualPurpose == CTP_Initialization && + isa(expression.pattern); + } + /// Whether the contextual type is only a hint, rather than a type bool contextualTypeIsOnlyAHint(bool isOpaqueReturnType) const; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 32b9281a60992..7a08e2865091a 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2150,7 +2150,7 @@ TypeChecker::typeCheckExpression( Type convertTo = convertType.getType(); - if (options.contains(TypeCheckExprFlags::ExpressionTypeMustBeOptional)) { + if (target.isOptionalSomePatternInit()) { assert(!convertTo && "convertType and type check options conflict"); auto *convertTypeLocator = cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); @@ -2669,24 +2669,12 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, if (!initializer) return true; - TypeLoc contextualType; - auto contextualPurpose = CTP_Unused; - TypeCheckExprOptions flags = None; - - // Set the contextual purpose even if the pattern doesn't have a type so - // if there's an error we can use that information to inform diagnostics. - contextualPurpose = CTP_Initialization; - - if (isa(pattern)) { - flags |= TypeCheckExprFlags::ExpressionTypeMustBeOptional; - } - // Type-check the initializer. auto target = SolutionApplicationTarget::forInitialization( initializer, patternType, pattern); bool unresolvedTypeExprs = false; auto resultTarget = typeCheckExpression(target, DC, unresolvedTypeExprs, - flags, &listener); + None, &listener); if (resultTarget) { initializer = resultTarget->getAsExpr(); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index cb799f8e271b1..fa15d08247b7f 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -176,10 +176,6 @@ enum class TypeCheckExprFlags { /// not affect type checking itself. IsExprStmt = 0x20, - /// If set, a conversion constraint should be specified so that the result of - /// the expression is an optional type. - ExpressionTypeMustBeOptional = 0x200, - /// FIXME(diagnostics): Once diagnostics are completely switched to new /// framework, this flag could be removed as obsolete. /// From 9278fb8c22b0c0e98acad1011b3d01e849e97529 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 2 Feb 2020 23:41:24 -0800 Subject: [PATCH 113/237] [Constraint system] Eliminate TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType We can compute this information based solely on the SolutionApplicationTarget, so do it. --- lib/Sema/ConstraintSystem.cpp | 38 ++++++++++++++++++++++++++----- lib/Sema/ConstraintSystem.h | 30 +++++++++++++++++++----- lib/Sema/TypeCheckConstraints.cpp | 23 +++++++++---------- lib/Sema/TypeCheckStmt.cpp | 17 -------------- lib/Sema/TypeChecker.h | 8 ------- 5 files changed, 67 insertions(+), 49 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index cfeea51f9d551..5d36ff5bb3f36 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3970,7 +3970,7 @@ ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) { } SolutionApplicationTarget::SolutionApplicationTarget( - Expr *expr, ContextualTypePurpose contextualPurpose, + Expr *expr, DeclContext *dc, ContextualTypePurpose contextualPurpose, TypeLoc convertType, bool isDiscarded) { // Verify that a purpose was specified if a convertType was. Note that it is // ok to have a purpose without a convertType (which is used for call @@ -3991,13 +3991,14 @@ SolutionApplicationTarget::SolutionApplicationTarget( kind = Kind::expression; expression.expression = expr; + expression.dc = dc; expression.contextualPurpose = contextualPurpose; expression.convertType = convertType; expression.isDiscarded = isDiscarded; } SolutionApplicationTarget SolutionApplicationTarget::forInitialization( - Expr *initializer, Type patternType, Pattern *pattern) { + Expr *initializer, DeclContext *dc, Type patternType, Pattern *pattern) { // Determine the contextual type for the initialization. TypeLoc contextualType; if (!isa(pattern) && @@ -4016,18 +4017,43 @@ SolutionApplicationTarget SolutionApplicationTarget::forInitialization( } SolutionApplicationTarget target( - initializer, CTP_Initialization, contextualType, + initializer, dc, CTP_Initialization, contextualType, /*isDiscarded=*/false); target.expression.pattern = pattern; return target; } -bool SolutionApplicationTarget::contextualTypeIsOnlyAHint( - bool isOpaqueReturnType) const { +bool SolutionApplicationTarget::infersOpaqueReturnType() const { assert(kind == Kind::expression); switch (expression.contextualPurpose) { case CTP_Initialization: - return !isOpaqueReturnType; + if (Type convertType = expression.convertType.getType()) { + return convertType->is(); + } + return false; + + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + if (Type convertType = expression.convertType.getType()) { + if (auto opaqueType = convertType->getAs()) { + auto dc = getDeclContext(); + if (auto func = dyn_cast(dc)) { + return opaqueType->getDecl()->isOpaqueReturnTypeOfFunction(func); + } + } + } + return false; + + default: + return false; + } +} + +bool SolutionApplicationTarget::contextualTypeIsOnlyAHint() const { + assert(kind == Kind::expression); + switch (expression.contextualPurpose) { + case CTP_Initialization: + return !infersOpaqueReturnType(); case CTP_ForEachStmt: return true; case CTP_Unused: diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 239a03e010a98..98bf8362620f3 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1150,6 +1150,10 @@ class SolutionApplicationTarget { /// The expression being type-checked. Expr *expression; + /// The declaration context in which the expression is being + /// type-checked. + DeclContext *dc; + /// The purpose of the contextual type. ContextualTypePurpose contextualPurpose; @@ -1171,14 +1175,15 @@ class SolutionApplicationTarget { }; public: - SolutionApplicationTarget(Expr *expr, + SolutionApplicationTarget(Expr *expr, DeclContext *dc, ContextualTypePurpose contextualPurpose, Type convertType, bool isDiscarded) - : SolutionApplicationTarget(expr, contextualPurpose, + : SolutionApplicationTarget(expr, dc, contextualPurpose, TypeLoc::withoutLoc(convertType), isDiscarded) { } - SolutionApplicationTarget(Expr *expr, ContextualTypePurpose contextualPurpose, + SolutionApplicationTarget(Expr *expr, DeclContext *dc, + ContextualTypePurpose contextualPurpose, TypeLoc convertType, bool isDiscarded); SolutionApplicationTarget(AnyFunctionRef fn) @@ -1192,7 +1197,7 @@ class SolutionApplicationTarget { /// Form a target for the initialization of a pattern from an expression. static SolutionApplicationTarget forInitialization( - Expr *initializer, Type patternType, Pattern *pattern); + Expr *initializer, DeclContext *dc, Type patternType, Pattern *pattern); Expr *getAsExpr() const { switch (kind) { @@ -1204,6 +1209,16 @@ class SolutionApplicationTarget { } } + DeclContext *getDeclContext() const { + switch (kind) { + case Kind::expression: + return expression.dc; + + case Kind::function: + return function.function.getAsDeclContext(); + } + } + ContextualTypePurpose getExprContextualTypePurpose() const { assert(kind == Kind::expression); return expression.contextualPurpose; @@ -1258,9 +1273,12 @@ class SolutionApplicationTarget { expression.contextualPurpose == CTP_Initialization && isa(expression.pattern); } - + + /// Whether this context infers an opaque return type. + bool infersOpaqueReturnType() const; + /// Whether the contextual type is only a hint, rather than a type - bool contextualTypeIsOnlyAHint(bool isOpaqueReturnType) const; + bool contextualTypeIsOnlyAHint() const; bool isDiscardedExpr() const { assert(kind == Kind::expression); diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 7a08e2865091a..71f05f2a06ae6 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2066,11 +2066,11 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { SolutionApplicationTarget target( - expr, convertTypePurpose, convertType, + expr, dc, convertTypePurpose, convertType, options.contains(TypeCheckExprFlags::IsDiscarded)); bool unresolvedTypeExprs = false; auto resultTarget = typeCheckExpression( - target, dc, unresolvedTypeExprs, options, listener, baseCS); + target, unresolvedTypeExprs, options, listener, baseCS); if (!resultTarget) { expr = target.getAsExpr(); return Type(); @@ -2090,14 +2090,14 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc, Optional TypeChecker::typeCheckExpression( SolutionApplicationTarget &target, - DeclContext *dc, bool &unresolvedTypeExprs, TypeCheckExprOptions options, ExprTypeCheckListener *listener, ConstraintSystem *baseCS) { unresolvedTypeExprs = false; - auto &Context = dc->getASTContext(); Expr *expr = target.getAsExpr(); + DeclContext *dc = target.getDeclContext(); + auto &Context = dc->getASTContext(); FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr); PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); @@ -2134,12 +2134,11 @@ TypeChecker::typeCheckExpression( cs.setContextualType( contextualTypeExpr, convertType, target.getExprContextualTypePurpose(), - options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)); + target.infersOpaqueReturnType()); // If the convertType is *only* provided for that hint, then null it out so // that we don't later treat it as an actual conversion constraint. - if (target.contextualTypeIsOnlyAHint( - options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType))) + if (target.contextualTypeIsOnlyAHint()) convertType = TypeLoc(); // If the client can handle unresolved type variables, leave them in the @@ -2165,7 +2164,7 @@ TypeChecker::typeCheckExpression( // Attempt to solve the constraint system. SolutionApplicationTarget innerTarget( - expr, target.getExprContextualTypePurpose(), convertTo, + expr, dc, target.getExprContextualTypePurpose(), convertTo, target.isDiscardedExpr()); auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables); if (!viable) { @@ -2264,7 +2263,7 @@ getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, if (needClearType) expr->setType(Type()); SolutionApplicationTarget target( - expr, CTP_Unused, Type(), /*isDiscarded=*/false); + expr, dc, CTP_Unused, Type(), /*isDiscarded=*/false); auto viable = cs.solve(target, listener, allowFreeTypeVariables); if (!viable) { recoverOriginalType(); @@ -2345,7 +2344,7 @@ void TypeChecker::getPossibleTypesOfExpressionWithoutApplying( expr->setType(Type()); SolutionApplicationTarget target( - expr, CTP_Unused, Type(), /*isDiscarded=*/false); + expr, dc, CTP_Unused, Type(), /*isDiscarded=*/false); if (auto viable = cs.solve(target, listener, allowFreeTypeVariables)) { expr = target.getAsExpr(); for (auto &solution : *viable) { @@ -2671,9 +2670,9 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, // Type-check the initializer. auto target = SolutionApplicationTarget::forInitialization( - initializer, patternType, pattern); + initializer, DC, patternType, pattern); bool unresolvedTypeExprs = false; - auto resultTarget = typeCheckExpression(target, DC, unresolvedTypeExprs, + auto resultTarget = typeCheckExpression(target, unresolvedTypeExprs, None, &listener); if (resultTarget) { diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index bdceddf11f525..ff032e15ea731 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -517,23 +517,6 @@ class StmtChecker : public StmtVisitor { TypeCheckExprOptions options = {}; - // If the result type is an opaque type, this is an opportunity to resolve - // the underlying type. - auto isOpaqueReturnTypeOfCurrentFunc = [&](OpaqueTypeDecl *opaque) -> bool { - // Closures currently don't support having opaque types. - auto funcDecl = TheFunc->getAbstractFunctionDecl(); - if (!funcDecl) - return false; - - return opaque->isOpaqueReturnTypeOfFunction(funcDecl); - }; - - if (auto opaque = ResultTy->getAs()) { - if (isOpaqueReturnTypeOfCurrentFunc(opaque->getDecl())) { - options |= TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType; - } - } - if (EndTypeCheckLoc.isValid()) { assert(DiagnosticSuppression::isEnabled(getASTContext().Diags) && "Diagnosing and AllowUnresolvedTypeVariables don't seem to mix"); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index fa15d08247b7f..e2958847076b5 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -183,13 +183,6 @@ enum class TypeCheckExprFlags { /// as part of the expression diagnostics, which is attempting to narrow /// down failure location. SubExpressionDiagnostics = 0x400, - - /// If set, the 'convertType' specified to typeCheckExpression is the opaque - /// return type of the declaration being checked. The archetype should be - /// opened into a type variable to provide context to the expression, and - /// the resulting type will be a candidate for binding the underlying - /// type. - ConvertTypeIsOpaqueReturnType = 0x800, }; using TypeCheckExprOptions = OptionSet; @@ -831,7 +824,6 @@ class TypeChecker final { static Optional typeCheckExpression(constraints::SolutionApplicationTarget &target, - DeclContext *dc, bool &unresolvedTypeExprs, TypeCheckExprOptions options = TypeCheckExprOptions(), ExprTypeCheckListener *listener = nullptr, From fbc8457f2d4cf6b82039974e65698f91c5f2b6fb Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Mon, 3 Feb 2020 09:59:04 -0800 Subject: [PATCH 114/237] Add profitability check to array-property-opt (#29236) * Add profitability check to array-property-opt This change adds additional heuristics to array-property-opt to avoid code size increase in cases where the optimization may not be beneficial. With this change, we do not specialize a loop if it has any opaque function calls or the instruction count exceeds a predefined threshold. --- .../LoopTransforms/ArrayPropertyOpt.cpp | 85 ++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp index d00580b7c4c00..56acd620b4e04 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -82,15 +82,64 @@ class ArrayPropertiesAnalysis { SILBasicBlock *Preheader; DominanceInfo *DomTree; + llvm::DenseMap InstCountCache; llvm::SmallSet HoistableArray; SmallPtrSet ReachingBlocks; SmallPtrSet CachedExitingBlocks; + + // This controls the max instructions the analysis can scan before giving up + const uint32_t AnalysisThreshold = 5000; + // This controls the max threshold for instruction count in the loop + const uint32_t LoopInstCountThreshold = 500; + public: ArrayPropertiesAnalysis(SILLoop *L, DominanceAnalysis *DA) : Fun(L->getHeader()->getParent()), Loop(L), Preheader(nullptr), DomTree(DA->get(Fun)) {} + /// Check if it is profitable to specialize a loop when you see an apply + /// instruction. We consider it is not profitable to specialize the loop when: + /// 1. The callee is not found in the module, or cannot be determined + /// 2. The number of instructions the analysis scans has exceeded the AnalysisThreshold + uint32_t checkProfitabilityRecursively(SILFunction *Callee) { + if (!Callee) + return AnalysisThreshold; + + auto CacheEntry = InstCountCache.find(Callee); + if (CacheEntry != InstCountCache.end()) + return CacheEntry->second; + + InstCountCache.insert(std::make_pair(Callee, 0)); + + uint32_t InstCount = 0; + + for (auto &BB : *Callee) { + for (auto &I : BB) { + if (InstCount++ >= AnalysisThreshold) { + LLVM_DEBUG(llvm::dbgs() << "ArrayPropertyOpt: Disabled Reason - Exceeded Analysis Threshold in " + << BB.getParent()->getName() << "\n"); + InstCountCache[Callee] = AnalysisThreshold; + return AnalysisThreshold; + } + if (auto Apply = FullApplySite::isa(&I)) { + auto Callee = Apply.getReferencedFunctionOrNull(); + if (!Callee) { + LLVM_DEBUG(llvm::dbgs() << "ArrayPropertyOpt: Disabled Reason - Found opaque code in " + << BB.getParent()->getName() << "\n"); + LLVM_DEBUG(Apply.dump()); + LLVM_DEBUG(I.getOperand(0)->dump()); + } + const auto CalleeInstCount = checkProfitabilityRecursively(Callee); + InstCount += CalleeInstCount; + } + } + } + InstCountCache[Callee] = InstCount; + + return InstCount; + } + bool run() { Preheader = Loop->getLoopPreheader(); if (!Preheader) { @@ -107,10 +156,11 @@ class ArrayPropertiesAnalysis { // beneficial. This heuristic also simplifies which regions we want to // specialize on. We will specialize the outermost loopnest that has // 'array.props' instructions in its preheader. + bool FoundHoistable = false; + uint32_t LoopInstCount = 0; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { - // Can't clone alloc_stack instructions whose dealloc_stack is outside // the loop. if (!Loop->canDuplicate(&Inst)) @@ -122,11 +172,42 @@ class ArrayPropertiesAnalysis { if (!canHoistArrayPropsInst(ArrayPropsInst)) return false; + + LoopInstCount++; FoundHoistable = true; } } - return FoundHoistable; + if (!FoundHoistable) + return false; + + // If the LoopInstCount exceeds the threshold, we will disable the optimization on this loop + // For loops of deeper nesting we increase the threshold by an additional 10% + if (LoopInstCount > LoopInstCountThreshold * (1 + (Loop->getLoopDepth() - 1) / 10)) { + LLVM_DEBUG(llvm::dbgs() << "Exceeded LoopInstCountThreshold\n"); + return false; + } + + // Additionally, we don't specialize the loop if we find opaque code or + // the analysis scans instructions greater than a threshold + // Since only few loops qualify as hoistable, and the profitability check + // can run long in cases of large thresholds, these checks are not folded + // along with the legality checks above. + for (auto *BB : Loop->getBlocks()) { + for (auto &Inst : *BB) { + if (auto Apply = FullApplySite::isa(&Inst)) { + const auto Callee = Apply.getReferencedFunctionOrNull(); + auto CalleeInstCount = checkProfitabilityRecursively(Callee); + if (CalleeInstCount >= AnalysisThreshold) + return false; + } + } + } + + LLVM_DEBUG(llvm::dbgs() << "Profitable ArrayPropertyOpt in " + << Loop->getLoopPreheader()->getParent()->getName() << "\n"); + LLVM_DEBUG(Loop->dump()); + return true; } private: From c09f397ce6c9f32fa43a139d18deac2fa95a3cab Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 3 Feb 2020 10:08:37 -0800 Subject: [PATCH 115/237] Experiment, eliminate early semantic arc opts run. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index cf9775bed4ed2..f0e90f0d1fc22 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -110,7 +110,6 @@ static void addMandatoryOptPipeline(SILPassPipelinePlan &P) { #endif if (Options.shouldOptimize()) { - P.addSemanticARCOpts(); P.addDestroyHoisting(); } if (!Options.StripOwnershipAfterSerialization) From 45d35eb75cd87fd955253788a19a91480087d09f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Feb 2020 10:50:38 -0800 Subject: [PATCH 116/237] [FunctionBodyTimer] Fix `-warn-long-function-bodies`, whose behavior was accidentally flipped with `-debug-time-function-bodies` --- lib/Sema/TypeCheckStmt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index bdceddf11f525..0f5e70e541375 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -198,7 +198,7 @@ namespace { ASTContext &ctx = Function.getAsDeclContext()->getASTContext(); auto *AFD = Function.getAbstractFunctionDecl(); - if (ctx.TypeCheckerOpts.WarnLongFunctionBodies) { + if (ctx.TypeCheckerOpts.DebugTimeFunctionBodies) { // Round up to the nearest 100th of a millisecond. llvm::errs() << llvm::format("%0.2f", ceil(elapsed * 100000) / 100) << "ms\t"; Function.getLoc().print(llvm::errs(), ctx.SourceMgr); @@ -213,7 +213,7 @@ namespace { llvm::errs() << "\n"; } - const auto WarnLimit = ctx.TypeCheckerOpts.DebugTimeFunctionBodies; + const auto WarnLimit = ctx.TypeCheckerOpts.WarnLongFunctionBodies; if (WarnLimit != 0 && elapsedMS >= WarnLimit) { if (AFD) { ctx.Diags.diagnose(AFD, diag::debug_long_function_body, From 6d671020b9cfbc937e6d808990f572d81f5f5baf Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 3 Feb 2020 11:41:36 -0800 Subject: [PATCH 117/237] [TypeChecker/BuilderTransform] Make sure implicit load expression updates types in AST Can't use `ConstraintSystem::addImplicitLoadExpr` because that would only cache types in constraint system and wouldn't propagate them to AST, since that happens in `ExprRewritter::finalize` during regular solution application. `TypeChecker::addImplicitLoadExpr` should be used directly in cases like that. Resolves: rdar://problem/58972627 --- lib/Sema/BuilderTransform.cpp | 7 ++-- lib/Sema/TypeChecker.h | 11 +++-- .../Sema/SwiftUI/rdar58972627.swift | 40 +++++++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 validation-test/Sema/SwiftUI/rdar58972627.swift diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 0dff01b97cc29..f133eeae1defc 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -694,8 +694,8 @@ class BuilderClosureRewriter // Load the right-hand side if needed. if (finalCapturedExpr->getType()->is()) { - auto &cs = solution.getConstraintSystem(); - finalCapturedExpr = cs.addImplicitLoadExpr(finalCapturedExpr); + finalCapturedExpr = + TypeChecker::addImplicitLoadExpr(ctx, finalCapturedExpr); } auto assign = new (ctx) AssignExpr( @@ -840,8 +840,7 @@ class BuilderClosureRewriter // Load the condition if needed. if (finalCondExpr->getType()->is()) { - auto &cs = solution.getConstraintSystem(); - finalCondExpr = cs.addImplicitLoadExpr(finalCondExpr); + finalCondExpr = TypeChecker::addImplicitLoadExpr(ctx, finalCondExpr); } condElement.setBoolean(finalCondExpr); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 3c643d706e25e..2a432d8ac633e 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1094,10 +1094,13 @@ class TypeChecker final { /// more complicated than simplify wrapping given root in newly created /// `LoadExpr`, because `ForceValueExpr` and `ParenExpr` supposed to appear /// only at certain positions in AST. - static Expr * - addImplicitLoadExpr(ASTContext &Context, Expr *expr, - std::function getType, - std::function setType); + static Expr *addImplicitLoadExpr(ASTContext &Context, Expr *expr, + std::function getType = + [](Expr *E) { return E->getType(); }, + std::function setType = + [](Expr *E, Type type) { + E->setType(type); + }); /// Determine whether the given type contains the given protocol. /// diff --git a/validation-test/Sema/SwiftUI/rdar58972627.swift b/validation-test/Sema/SwiftUI/rdar58972627.swift new file mode 100644 index 0000000000000..bedbb006cd648 --- /dev/null +++ b/validation-test/Sema/SwiftUI/rdar58972627.swift @@ -0,0 +1,40 @@ +// RUN: %target-swift-frontend %s -target x86_64-apple-macosx10.15 -emit-sil -verify +// REQUIRES: objc_interop +// REQUIRES: OS=macosx + +import SwiftUI +import Combine + +struct A: View { + var body: some View { + Spacer() + } +} + +struct B: View { + var body: some View { + Spacer() + } +} + +class Context: ObservableObject { + @State var flag: Bool + + init() { + self.flag = false + } +} + +struct S : View { + @EnvironmentObject var context: Context + + var body: some View { + VStack { + if (context.flag) { // Ok (shouldn't trip SIL Verifier) + A() + } else { + B() + } + } + } +} From 7453d53e48f491c8b29195a05c6ae04463447169 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 3 Feb 2020 12:40:28 -0800 Subject: [PATCH 118/237] [TypeChecker/BuilderTransform] Switch to use `hasLValueType` instead of `is` to cover all cases where load is expected --- lib/Sema/BuilderTransform.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index f133eeae1defc..9f10f3094384f 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -693,7 +693,7 @@ class BuilderClosureRewriter declRef->setType(LValueType::get(temporaryVar->getType())); // Load the right-hand side if needed. - if (finalCapturedExpr->getType()->is()) { + if (finalCapturedExpr->getType()->hasLValueType()) { finalCapturedExpr = TypeChecker::addImplicitLoadExpr(ctx, finalCapturedExpr); } @@ -839,7 +839,7 @@ class BuilderClosureRewriter auto finalCondExpr = rewriteExpr(condExpr); // Load the condition if needed. - if (finalCondExpr->getType()->is()) { + if (finalCondExpr->getType()->hasLValueType()) { finalCondExpr = TypeChecker::addImplicitLoadExpr(ctx, finalCondExpr); } From 273500ec76640f6a0cd91bd7668e464273feef04 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 3 Feb 2020 13:05:21 -0800 Subject: [PATCH 119/237] Driver: add an option to avoid emitting .swiftsourceinfo files --- include/swift/Option/Options.td | 5 +++++ lib/Driver/Driver.cpp | 5 ++++- test/Driver/sourceinfo_file.swift | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index dbadf83aa6318..1a5166a51d453 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -419,6 +419,11 @@ def emit_module_interface_path : ArgumentIsPath]>, MetaVarName<"">, HelpText<"Output module interface file to ">; +def avoid_emit_module_source_info : + Flag<["-"], "avoid-emit-module-source-info">, + Flags<[NoInteractiveOption, DoesNotAffectIncrementalBuild]>, + HelpText<"don't emit Swift source info file">; + def emit_module_source_info_path : Separate<["-"], "emit-module-source-info-path">, Flags<[FrontendOption, NoInteractiveOption, DoesNotAffectIncrementalBuild, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index cac9af11f2be9..4bcf3fe8c4674 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2718,7 +2718,10 @@ Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA, if (OI.ShouldGenerateModule && (isa(JA) || isa(JA))) { chooseSwiftModuleDocOutputPath(C, OutputMap, workingDirectory, Output.get()); - chooseSwiftSourceInfoOutputPath(C, OutputMap, workingDirectory, Output.get()); + if (!C.getArgs().hasArg(options::OPT_avoid_emit_module_source_info)) { + chooseSwiftSourceInfoOutputPath(C, OutputMap, workingDirectory, + Output.get()); + } } if (C.getArgs().hasArg(options::OPT_emit_module_interface, diff --git a/test/Driver/sourceinfo_file.swift b/test/Driver/sourceinfo_file.swift index bd406d4dc1855..6b2882fa66290 100644 --- a/test/Driver/sourceinfo_file.swift +++ b/test/Driver/sourceinfo_file.swift @@ -14,3 +14,8 @@ // RUN: %swiftc_driver -driver-print-jobs -emit-module %s -emit-module-path %t/build/sourceinfo_file.swiftmodule -module-name sourceinfo_file -emit-module-source-info-path %t/build/DriverPath.swiftsourceinfo | %FileCheck %s -check-prefix CHECK-DRIVER-OPT // CHECK-DRIVER-OPT: build{{[/\\]}}DriverPath.swiftsourceinfo + +// RUN: %empty-directory(%t/build) +// RUN: %swiftc_driver -driver-print-jobs -emit-module %s -emit-module-path %t/build/sourceinfo_file.swiftmodule -module-name sourceinfo_file -avoid-emit-module-source-info | %FileCheck %s -check-prefix CHECK-DRIVER-AVOID + +// CHECK-DRIVER-AVOID-NOT: swiftsourceinfo From c096e29967e5ce8ccd831dca6aa53ac874e9af90 Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Mon, 3 Feb 2020 11:07:29 -0800 Subject: [PATCH 120/237] [Python: flake8] Update the utils/python_lint.py script to fail with a non-zero exit code if flake8 and flake8-import-order are not installed. --- utils/gyb.py | 10 ---------- utils/line-directive | 4 ---- utils/python_lint.py | 2 +- validation-test/Python/python_lint.swift | 8 +++----- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/utils/gyb.py b/utils/gyb.py index e38525338d3b9..2df9a0311dbb6 100755 --- a/utils/gyb.py +++ b/utils/gyb.py @@ -1136,16 +1136,6 @@ def execute_template( def main(): - """ - Lint this file. - >>> import sys - >>> gyb_path = os.path.realpath(__file__).replace('.pyc', '.py') - >>> sys.path.append(os.path.dirname(gyb_path)) - >>> import python_lint - >>> python_lint.lint([gyb_path], verbose=False) - 0 - """ - import argparse import sys diff --git a/utils/line-directive b/utils/line-directive index 7ef02e07e1d5d..0f6d05f07cddf 100755 --- a/utils/line-directive +++ b/utils/line-directive @@ -659,10 +659,6 @@ def run(): >>> raw_output.close() >>> os.remove(raw_output.name) - Lint this file. - >>> import python_lint - >>> python_lint.lint([os.path.realpath(__file__)], verbose=False) - 0 """ if len(sys.argv) <= 1: import doctest diff --git a/utils/python_lint.py b/utils/python_lint.py index f17ca0d74398a..112a1bca41377 100755 --- a/utils/python_lint.py +++ b/utils/python_lint.py @@ -84,7 +84,7 @@ def lint(args, verbose=False): if verbose: print(_INSTALL_FLAKE8_MESSAGE) - return 0 + return 1 return subprocess.call( [sys.executable, '-m', 'flake8'] + args, diff --git a/validation-test/Python/python_lint.swift b/validation-test/Python/python_lint.swift index 076914c4d6939..43f06a1766370 100644 --- a/validation-test/Python/python_lint.swift +++ b/validation-test/Python/python_lint.swift @@ -1,10 +1,8 @@ // Continuous integration for the OS X Platform also runs the tests in the // iPhone, Apple TV and Apple Watch simulators. We only need to run the -// build_swift module unit-tests once per OSX Platform test run, rather than -// once for each supported Apple device. +// Python lint once per OSX Platform test run, rather than once for each +// supported Apple device. -// UNSUPPORTED: OS=ios -// UNSUPPORTED: OS=tvos -// UNSUPPORTED: OS=watchos +// REQUIRES: OS=macosx // RUN: %{python} %utils/python_lint.py From 51d6243d2451dfb0a05996f902e6766834f1cad7 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Mon, 3 Feb 2020 13:27:06 -0800 Subject: [PATCH 121/237] cmake: avoid emitting .swiftsourceinfo files for overlays rdar://problem/58611222 --- cmake/modules/SwiftSource.cmake | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmake/modules/SwiftSource.cmake b/cmake/modules/SwiftSource.cmake index 004d26b0fa8d3..a68f9ef423140 100644 --- a/cmake/modules/SwiftSource.cmake +++ b/cmake/modules/SwiftSource.cmake @@ -355,13 +355,9 @@ function(_compile_swift_files if(SWIFTFILE_SDK IN_LIST SWIFT_APPLE_PLATFORMS OR SWIFTFILE_SDK STREQUAL "MACCATALYST") set(specific_module_dir "${module_base}.swiftmodule") - set(specific_module_project_dir "${specific_module_dir}/Project") - set(source_info_file "${specific_module_project_dir}/${SWIFTFILE_ARCHITECTURE}.swiftsourceinfo") set(module_base "${module_base}.swiftmodule/${SWIFTFILE_ARCHITECTURE}") else() set(specific_module_dir) - set(specific_module_project_dir) - set(source_info_file "${module_base}.swiftsourceinfo") endif() set(module_file "${module_base}.swiftmodule") set(module_doc_file "${module_base}.swiftdoc") @@ -622,11 +618,10 @@ function(_compile_swift_files COMMAND "${CMAKE_COMMAND}" "-E" "make_directory" ${module_dir} ${specific_module_dir} - ${specific_module_project_dir} COMMAND "${PYTHON_EXECUTABLE}" "${line_directive_tool}" "@${file_path}" -- "${swift_compiler_tool}" "-emit-module" "-o" "${module_file}" - "-emit-module-source-info-path" "${source_info_file}" + "-avoid-emit-module-source-info" ${swift_flags} ${swift_module_flags} "@${file_path}" ${command_touch_module_outputs} OUTPUT ${module_outputs} From 4be78281c25a5fcfcc5532905341ed1413b90602 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 3 Feb 2020 13:54:53 -0800 Subject: [PATCH 122/237] [Gardening] Const-qualify some PoundDiagnosticDecl members --- include/swift/AST/Decl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b2727cd5572fb..435710c1a8582 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2332,17 +2332,17 @@ class PoundDiagnosticDecl : public Decl { Bits.PoundDiagnosticDecl.HasBeenEmitted = false; } - DiagnosticKind getKind() { + DiagnosticKind getKind() const { return isError() ? DiagnosticKind::Error : DiagnosticKind::Warning; } - StringLiteralExpr *getMessage() { return Message; } + StringLiteralExpr *getMessage() const { return Message; } - bool isError() { + bool isError() const { return Bits.PoundDiagnosticDecl.IsError; } - bool hasBeenEmitted() { + bool hasBeenEmitted() const { return Bits.PoundDiagnosticDecl.HasBeenEmitted; } From 1e74c399563be0efc1cf889d98daefa0703f3e86 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 3 Feb 2020 15:57:43 -0800 Subject: [PATCH 123/237] [SourceKit] Expose `ExpectedTypeRelation` for the completion results So that clients can sort the results using this. rdar://problem/59066560 --- .../complete_constructor.swift.response | 1 + .../CodeComplete/complete_docbrief_1.swift | 2 + .../CodeComplete/complete_docbrief_2.swift | 1 + .../CodeComplete/complete_docbrief_3.swift | 1 + .../complete_from_clang_module.swift | 1 + .../CodeComplete/complete_member.swift | 2 + .../complete_member.swift.response | 4 ++ .../complete_optionalmethod.swift.response | 2 + .../complete_override.swift.response | 40 +++++++++++++++++++ .../complete_unresolvedmember.swift.response | 7 ++++ .../include/SourceKit/Core/LangSupport.h | 1 + .../lib/SwiftLang/SwiftCompletion.cpp | 16 ++++++++ .../sourcekitd/CodeCompletionResultsArray.h | 1 + .../lib/API/CodeCompletionResultsArray.cpp | 7 ++++ .../tools/sourcekitd/lib/API/Requests.cpp | 1 + utils/gyb_sourcekit_support/UIDs.py | 1 + 16 files changed, 88 insertions(+) diff --git a/test/SourceKit/CodeComplete/complete_constructor.swift.response b/test/SourceKit/CodeComplete/complete_constructor.swift.response index b025292fba180..5fbd8f44b5d55 100644 --- a/test/SourceKit/CodeComplete/complete_constructor.swift.response +++ b/test/SourceKit/CodeComplete/complete_constructor.swift.response @@ -7,6 +7,7 @@ key.description: "(arg1: Int, arg2: Int)", key.typename: "Foo", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:20complete_constructor3FooC4arg14arg2ACSi_Sitcfc", key.modulename: "complete_constructor" diff --git a/test/SourceKit/CodeComplete/complete_docbrief_1.swift b/test/SourceKit/CodeComplete/complete_docbrief_1.swift index 802c4b5bd5723..09410198340bb 100644 --- a/test/SourceKit/CodeComplete/complete_docbrief_1.swift +++ b/test/SourceKit/CodeComplete/complete_docbrief_1.swift @@ -35,6 +35,7 @@ func test() { // CHECK-NEXT: key.typename: "Void", // CHECK-NEXT: key.doc.brief: "This is a doc comment of P.bar", // CHECK-NEXT: key.context: source.codecompletion.context.superclass, +// CHECK-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-NEXT: key.num_bytes_to_erase: 0, // CHECK-NEXT: key.associated_usrs: "s:12DocBriefTest1PPAAE3baryyF", // CHECK-NEXT: key.modulename: "DocBriefTest" @@ -47,6 +48,7 @@ func test() { // CHECK-NEXT: key.typename: "Void", // CHECK-NEXT: key.doc.brief: "This is a doc comment of P.foo", // CHECK-NEXT: key.context: source.codecompletion.context.thisclass, +// CHECK-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-NEXT: key.num_bytes_to_erase: 0, // CHECK-NEXT: key.associated_usrs: "s:12DocBriefTest1SV3fooyyF s:12DocBriefTest1PP3fooyyF", // CHECK-NEXT: key.modulename: "DocBriefTest" diff --git a/test/SourceKit/CodeComplete/complete_docbrief_2.swift b/test/SourceKit/CodeComplete/complete_docbrief_2.swift index 568c7800b3264..b65f0d356955a 100644 --- a/test/SourceKit/CodeComplete/complete_docbrief_2.swift +++ b/test/SourceKit/CodeComplete/complete_docbrief_2.swift @@ -39,6 +39,7 @@ func test() { // CHECK-NEXT: key.typename: "Void", // CHECK-NEXT: key.doc.brief: "This is a doc comment of P.foo", // CHECK-NEXT: key.context: source.codecompletion.context.thisclass, +// CHECK-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-NEXT: key.num_bytes_to_erase: 0, // CHECK-NEXT: key.associated_usrs: "s:12DocBriefUser1SV3fooyyF s:12DocBriefTest1PP3fooyyF", // CHECK-NEXT: key.modulename: "DocBriefUser" diff --git a/test/SourceKit/CodeComplete/complete_docbrief_3.swift b/test/SourceKit/CodeComplete/complete_docbrief_3.swift index f1ca2f96c8972..a329824cbe56b 100644 --- a/test/SourceKit/CodeComplete/complete_docbrief_3.swift +++ b/test/SourceKit/CodeComplete/complete_docbrief_3.swift @@ -40,6 +40,7 @@ func test() { // CHECK-NEXT: key.typename: "Void", // CHECK-NEXT: key.doc.brief: "This is a doc comment of P.foo", // CHECK-NEXT: key.context: source.codecompletion.context.thisclass, +// CHECK-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-NEXT: key.num_bytes_to_erase: 0, // CHECK-NEXT: key.associated_usrs: "s:12DocBriefTest1SV3fooyyF s:12DocBriefTest1PP3fooyyF", // CHECK-NEXT: key.modulename: "DocBriefTest" diff --git a/test/SourceKit/CodeComplete/complete_from_clang_module.swift b/test/SourceKit/CodeComplete/complete_from_clang_module.swift index 264472482ccea..93f2294aeb52a 100644 --- a/test/SourceKit/CodeComplete/complete_from_clang_module.swift +++ b/test/SourceKit/CodeComplete/complete_from_clang_module.swift @@ -9,6 +9,7 @@ import Foo // CHECK-NEXT: key.typename: "Int32", // CHECK-NEXT: key.doc.brief: "Aaa. fooIntVar. Bbb.", // CHECK-NEXT: key.context: source.codecompletion.context.othermodule, +// CHECK-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-NEXT: key.num_bytes_to_erase: 0, // CHECK-NEXT: key.associated_usrs: "c:@fooIntVar", // CHECK-NEXT: key.modulename: "Foo" diff --git a/test/SourceKit/CodeComplete/complete_member.swift b/test/SourceKit/CodeComplete/complete_member.swift index 121a817bacdff..b6d1142dbaccc 100644 --- a/test/SourceKit/CodeComplete/complete_member.swift +++ b/test/SourceKit/CodeComplete/complete_member.swift @@ -51,6 +51,7 @@ func testOverrideUSR() { // CHECK-OPTIONAL-NEXT: key.description: "fooInstanceFunc1(a: Int)", // CHECK-OPTIONAL-NEXT: key.typename: "Double", // CHECK-OPTIONAL-NEXT: key.context: source.codecompletion.context.thisclass, +// CHECK-OPTIONAL-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-OPTIONAL-NEXT: key.num_bytes_to_erase: 1, // CHECK-OPTIONAL-NEXT: key.associated_usrs: "s:15complete_member11FooProtocolP16fooInstanceFunc1ySdSiF", // CHECK-OPTIONAL-NEXT: key.modulename: "complete_member" @@ -72,6 +73,7 @@ func testOverrideUSR() { // CHECK-OVERRIDE_USR-NEXT: key.description: "foo()", // CHECK-OVERRIDE_USR-NEXT: key.typename: "Void", // CHECK-OVERRIDE_USR-NEXT: key.context: source.codecompletion.context.thisclass, +// CHECK-OVERRIDE_USR-NEXT: key.typerelation: source.codecompletion.typerelation.unrelated, // CHECK-OVERRIDE_USR-NEXT: key.num_bytes_to_erase: 0, // CHECK-OVERRIDE_USR-NEXT: key.associated_usrs: "s:15complete_member7DerivedC3fooyyF s:15complete_member4BaseC3fooyyF", // CHECK-OVERRIDE_USR-NEXT: key.modulename: "complete_member" diff --git a/test/SourceKit/CodeComplete/complete_member.swift.response b/test/SourceKit/CodeComplete/complete_member.swift.response index 8f4a9ec5f11df..db3bba88fdafb 100644 --- a/test/SourceKit/CodeComplete/complete_member.swift.response +++ b/test/SourceKit/CodeComplete/complete_member.swift.response @@ -7,6 +7,7 @@ key.description: "fooInstanceFunc0()", key.typename: "Double", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:15complete_member11FooProtocolP16fooInstanceFunc0SdyF", key.modulename: "complete_member" @@ -18,6 +19,7 @@ key.description: "fooInstanceFunc1(a: Int)", key.typename: "Double", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:15complete_member11FooProtocolP16fooInstanceFunc1ySdSiF", key.modulename: "complete_member" @@ -30,6 +32,7 @@ key.typename: "Int", key.doc.brief: "fooInstanceVar Aaa. Bbb.", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:15complete_member11FooProtocolP14fooInstanceVarSivp", key.modulename: "complete_member" @@ -41,6 +44,7 @@ key.description: "self", key.typename: "FooProtocol", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 } ] diff --git a/test/SourceKit/CodeComplete/complete_optionalmethod.swift.response b/test/SourceKit/CodeComplete/complete_optionalmethod.swift.response index 098b1afbe973a..9c79ffe20de57 100644 --- a/test/SourceKit/CodeComplete/complete_optionalmethod.swift.response +++ b/test/SourceKit/CodeComplete/complete_optionalmethod.swift.response @@ -7,6 +7,7 @@ key.description: "optionalMethod?()", key.typename: "Int", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "c:@M@complete_optionalmethod@objc(pl)Proto(im)optionalMethod", key.modulename: "complete_optionalmethod" @@ -18,6 +19,7 @@ key.description: "self", key.typename: "T", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 } ] diff --git a/test/SourceKit/CodeComplete/complete_override.swift.response b/test/SourceKit/CodeComplete/complete_override.swift.response index 4ec04873a57b3..d25002ef73931 100644 --- a/test/SourceKit/CodeComplete/complete_override.swift.response +++ b/test/SourceKit/CodeComplete/complete_override.swift.response @@ -7,6 +7,7 @@ key.description: "associatedtype", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -16,6 +17,7 @@ key.description: "class", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -25,6 +27,7 @@ key.description: "convenience", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -34,6 +37,7 @@ key.description: "deinit", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -43,6 +47,7 @@ key.description: "dynamic", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -52,6 +57,7 @@ key.description: "enum", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -61,6 +67,7 @@ key.description: "extension", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -70,6 +77,7 @@ key.description: "f(getMe a: Int, b: Double)", key.typename: "", key.context: source.codecompletion.context.superclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:17complete_override4BaseC1f5getMe1bySi_SdtF", key.modulename: "complete_override" @@ -81,6 +89,7 @@ key.description: "fileprivate", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -90,6 +99,7 @@ key.description: "final", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -99,6 +109,7 @@ key.description: "func", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -108,6 +119,7 @@ key.description: "import", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -117,6 +129,7 @@ key.description: "indirect", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -126,6 +139,7 @@ key.description: "infix", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -135,6 +149,7 @@ key.description: "init", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -144,6 +159,7 @@ key.description: "init()", key.typename: "", key.context: source.codecompletion.context.superclass, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:17complete_override4BaseCACycfc", key.modulename: "complete_override" @@ -155,6 +171,7 @@ key.description: "inout", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -164,6 +181,7 @@ key.description: "internal", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -173,6 +191,7 @@ key.description: "lazy", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -182,6 +201,7 @@ key.description: "let", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -191,6 +211,7 @@ key.description: "mutating", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -200,6 +221,7 @@ key.description: "nonmutating", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -209,6 +231,7 @@ key.description: "open", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -218,6 +241,7 @@ key.description: "operator", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -227,6 +251,7 @@ key.description: "optional", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -236,6 +261,7 @@ key.description: "override", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -245,6 +271,7 @@ key.description: "postfix", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -254,6 +281,7 @@ key.description: "precedencegroup", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -263,6 +291,7 @@ key.description: "prefix", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -272,6 +301,7 @@ key.description: "private", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -281,6 +311,7 @@ key.description: "protocol", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -290,6 +321,7 @@ key.description: "public", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -299,6 +331,7 @@ key.description: "required", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -308,6 +341,7 @@ key.description: "static", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -317,6 +351,7 @@ key.description: "struct", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -326,6 +361,7 @@ key.description: "subscript", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -335,6 +371,7 @@ key.description: "typealias", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -344,6 +381,7 @@ key.description: "unowned", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -353,6 +391,7 @@ key.description: "var", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 }, { @@ -362,6 +401,7 @@ key.description: "weak", key.typename: "", key.context: source.codecompletion.context.none, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0 } ] diff --git a/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response b/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response index dbd5c3da92dfc..ee2d1f510a44e 100644 --- a/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response +++ b/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response @@ -7,6 +7,7 @@ key.description: "create()", key.typename: "Foo", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO6createACyFZ", key.modulename: "complete_unresolvedmember" @@ -18,6 +19,7 @@ key.description: "east", key.typename: "Foo", key.context: source.codecompletion.context.exprspecific, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO4eastyA2CmF", key.modulename: "complete_unresolvedmember" @@ -29,6 +31,7 @@ key.description: "init(i: Int)", key.typename: "Foo", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO1iACSi_tcfc", key.modulename: "complete_unresolvedmember" @@ -40,6 +43,7 @@ key.description: "init(s: String)", key.typename: "Foo", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO1sACSS_tcfc", key.modulename: "complete_unresolvedmember" @@ -51,6 +55,7 @@ key.description: "instance", key.typename: "Foo", key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO8instanceACvpZ", key.modulename: "complete_unresolvedmember" @@ -62,6 +67,7 @@ key.description: "other(String)", key.typename: "Foo", key.context: source.codecompletion.context.exprspecific, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO5otheryACSScACmF", key.modulename: "complete_unresolvedmember" @@ -73,6 +79,7 @@ key.description: "west", key.typename: "Foo", key.context: source.codecompletion.context.exprspecific, + key.typerelation: source.codecompletion.typerelation.unrelated, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO4westyA2CmF", key.modulename: "complete_unresolvedmember" diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 5341716393419..0a05e9c66bd27 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -89,6 +89,7 @@ struct CodeCompletionInfo { StringRef DocBrief; StringRef AssocUSRs; UIdent SemanticContext; + UIdent TypeRelation; Optional ModuleImportDepth; bool NotRecommended; unsigned NumBytesToErase; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 2ca913da9b417..9d070b0620d32 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -502,6 +502,22 @@ bool SwiftToSourceKitCompletionAdapter::handleResult( Info.SemanticContext = CCCtxOtherModule; break; } + static UIdent CCTypeRelUnrelated("source.codecompletion.typerelation.unrelated"); + static UIdent CCTypeRelInvalid("source.codecompletion.typerelation.invalid"); + static UIdent CCTypeRelConvertible("source.codecompletion.typerelation.convertible"); + static UIdent CCTypeRelIdentical("source.codecompletion.typerelation.identical"); + + switch (Result->getExpectedTypeRelation()) { + case CodeCompletionResult::Unrelated: + Info.TypeRelation = CCTypeRelUnrelated; break; + case CodeCompletionResult::Invalid: + Info.TypeRelation = CCTypeRelInvalid; break; + case CodeCompletionResult::Convertible: + Info.TypeRelation = CCTypeRelConvertible; break; + case CodeCompletionResult::Identical: + Info.TypeRelation = CCTypeRelIdentical; break; + } + Info.ModuleName = Result->getModuleName(); Info.DocBrief = Result->getBriefDocComment(); Info.NotRecommended = Result->isNotRecommended(); diff --git a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/CodeCompletionResultsArray.h b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/CodeCompletionResultsArray.h index 1393fe8fe8d2c..9643ceaeee900 100644 --- a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/CodeCompletionResultsArray.h +++ b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/CodeCompletionResultsArray.h @@ -33,6 +33,7 @@ class CodeCompletionResultsArrayBuilder { Optional DocBrief, Optional AssocUSRs, SourceKit::UIdent SemanticContext, + SourceKit::UIdent TypeRelation, bool NotRecommended, unsigned NumBytesToErase); diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/CodeCompletionResultsArray.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/CodeCompletionResultsArray.cpp index be6974aa41d0e..70f2be704fd1c 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/CodeCompletionResultsArray.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/CodeCompletionResultsArray.cpp @@ -31,6 +31,7 @@ struct CodeCompletionResultsArrayBuilder::Implementation { Optional, Optional, UIdent, + UIdent, uint8_t> Builder; }; @@ -53,6 +54,7 @@ void CodeCompletionResultsArrayBuilder::add( Optional DocBrief, Optional AssocUSRs, UIdent SemanticContext, + UIdent TypeRelation, bool NotRecommended, unsigned NumBytesToErase) { @@ -67,6 +69,7 @@ void CodeCompletionResultsArrayBuilder::add( DocBrief, AssocUSRs, SemanticContext, + TypeRelation, BytesAndNotRecommended); } @@ -88,6 +91,7 @@ class CodeCompletionResultsArray { const char *, const char *, sourcekitd_uid_t, + sourcekitd_uid_t, uint8_t> CompactArrayReaderTy; static bool @@ -105,6 +109,7 @@ class CodeCompletionResultsArray { const char *DocBrief; const char *AssocUSRs; sourcekitd_uid_t SemanticContext; + sourcekitd_uid_t TypeRelation; uint8_t BytesAndNotRecommended; Reader.readEntries(Index, @@ -117,6 +122,7 @@ class CodeCompletionResultsArray { DocBrief, AssocUSRs, SemanticContext, + TypeRelation, BytesAndNotRecommended); unsigned NumBytesToErase = BytesAndNotRecommended >> 1; @@ -144,6 +150,7 @@ class CodeCompletionResultsArray { APPLY(KeyAssociatedUSRs, String, AssocUSRs); } APPLY(KeyContext, UID, SemanticContext); + APPLY(KeyTypeRelation, UID, TypeRelation); APPLY(KeyNumBytesToErase, Int, NumBytesToErase); if (NotRecommended) { APPLY(KeyNotRecommended, Bool, NotRecommended); diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp index ccade323104fe..0283b48ae67a5 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp @@ -1982,6 +1982,7 @@ bool SKCodeCompletionConsumer::handleResult(const CodeCompletionInfo &R) { DocBriefOpt, AssocUSRsOpt, R.SemanticContext, + R.TypeRelation, R.NotRecommended, R.NumBytesToErase); return true; diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index ad0c1d54018c9..c2f0eaf776ccf 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -65,6 +65,7 @@ def __init__(self, internal_name, external_name): 'key.fully_annotated_generic_signature'), KEY('DocBrief', 'key.doc.brief'), KEY('Context', 'key.context'), + KEY('TypeRelation', 'key.typerelation'), KEY('ModuleImportDepth', 'key.moduleimportdepth'), KEY('NumBytesToErase', 'key.num_bytes_to_erase'), KEY('NotRecommended', 'key.not_recommended'), From 71aeffdf1738724df758b41c7a4b9d54a88ebff5 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 3 Feb 2020 17:20:50 -0800 Subject: [PATCH 124/237] [CodeCompletion] Calculate type relation for EnumElementDecl --- lib/IDE/CodeCompletion.cpp | 5 ++ test/IDE/complete_assignment.swift | 8 ++-- test/IDE/complete_call_arg.swift | 6 +-- test/IDE/complete_constrained.swift | 4 +- test/IDE/complete_enum_elements.swift | 44 +++++++++--------- test/IDE/complete_in_closures.swift | 4 +- ...complete_property_delegate_attribute.swift | 6 +-- .../complete_single_expression_return.swift | 10 ++-- test/IDE/complete_stmt_controlling_expr.swift | 2 +- test/IDE/complete_unresolved_members.swift | 46 +++++++++---------- .../complete_unresolvedmember.swift.response | 6 +-- 11 files changed, 73 insertions(+), 68 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index f8e20c52b1e38..c1b1e4c0d32bc 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -886,6 +886,11 @@ calculateTypeRelationForDecl(const Decl *D, Type ExpectedType, return relation; } } + if (auto EED = dyn_cast(VD)) { + return calculateTypeRelation( + EED->getParentEnum()->TypeDecl::getDeclaredInterfaceType(), + ExpectedType, DC); + } if (auto NTD = dyn_cast(VD)) { return std::max( calculateTypeRelation(NTD->getInterfaceType(), ExpectedType, DC), diff --git a/test/IDE/complete_assignment.swift b/test/IDE/complete_assignment.swift index 50c58a4dbef6b..980af14c2a25b 100644 --- a/test/IDE/complete_assignment.swift +++ b/test/IDE/complete_assignment.swift @@ -128,16 +128,16 @@ func f2() { } // ASSIGN_5: Begin completions, 2 items -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case2[#D1#]; name=case2 -// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific: case1[#D1#]; name=case1 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case2[#D1#]; name=case2 +// ASSIGN_5-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case1[#D1#]; name=case1 func f6() { var d : D2 d = .#^ASSIGN_6^# } // ASSIGN_6: Begin completions, 2 items -// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific: case3[#D2#]; name=case3 -// ASSIGN_6-DAG:Decl[EnumElement]/ExprSpecific: case4[#D2#]; name=case4 +// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case3[#D2#]; name=case3 +// ASSIGN_6-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: case4[#D2#]; name=case4 func f7 (C : C2) { var i : Int diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 050118eb993ea..f456803359d03 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -555,9 +555,9 @@ func testTupleShuffle() { // SHUFFLE_2-DAG: Decl[GlobalVar]/CurrModule/TypeRelation[Identical]: s2[#String#]; name=s2 // SHUFFLE_3: Begin completions, 3 items -// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific: foo[#SimpleEnum#]; name=foo -// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific: bar[#SimpleEnum#]; name=bar -// SHUFFLE_3-DAG: Decl[EnumElement]/ExprSpecific: baz[#SimpleEnum#]; name=baz +// 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 class HasSubscript { subscript(idx: Int) -> String {} diff --git a/test/IDE/complete_constrained.swift b/test/IDE/complete_constrained.swift index 2f86a5a234466..6dd90b7cf3b68 100644 --- a/test/IDE/complete_constrained.swift +++ b/test/IDE/complete_constrained.swift @@ -118,8 +118,8 @@ struct Vegetarian: EatsFruit, EatsVegetables { } func testVegetarian(chef: Chef) { chef.cook(.#^CONDITIONAL_OVERLOAD_ARG^#) // CONDITIONAL_OVERLOAD_ARG: Begin completions, 2 items -// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/ExprSpecific: apple[#Fruit#]; name=apple -// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/ExprSpecific: broccoli[#Vegetable#]; name=broccoli +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: apple[#Fruit#]; name=apple +// CONDITIONAL_OVERLOAD_ARG-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: broccoli[#Vegetable#]; name=broccoli // 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 8c121d1196f3c..7e1a5e38f1562 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -89,8 +89,8 @@ enum FooEnum: CaseIterable { } // FOO_ENUM_TYPE_CONTEXT: Begin completions -// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Foo1[#FooEnum#]{{; name=.+$}} -// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Foo2[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Foo1[#FooEnum#]{{; name=.+$}} +// FOO_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Foo2[#FooEnum#]{{; name=.+$}} // FOO_ENUM_TYPE_CONTEXT-DAG: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: .alias1[#FooEnum#]; name=alias1 // FOO_ENUM_TYPE_CONTEXT: End completions @@ -128,8 +128,8 @@ enum FooEnum: CaseIterable { // FOO_ENUM_DOT_INVALID-NEXT: End completions // FOO_ENUM_DOT_ELEMENTS: Begin completions, 3 items -// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific: Foo1[#FooEnum#]{{; name=.+$}} -// FOO_ENUM_DOT_ELEMENTS-NEXT: Decl[EnumElement]/ExprSpecific: Foo2[#FooEnum#]{{; name=.+$}} +// 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]/CurrNominal/TypeRelation[Identical]: alias1[#FooEnum#]; name=alias1 // FOO_ENUM_DOT_ELEMENTS-NEXT: End completions @@ -154,18 +154,18 @@ enum BarEnum { } // BAR_ENUM_TYPE_CONTEXT: Begin completions -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar1[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar2()[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar3({#Int#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar4({#a: Int#}, {#b: Float#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar5({#a: Int#}, {#(Float)#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar6({#a: Int#}, {#b: (Float)#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar7({#a: Int#}, {#(b: Float, c: Double)#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar8({#a: Int#}, {#b: (c: Float, d: Double)#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar9({#Int#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar10({#Int#}, {#Float#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar11({#Int#}, {#(Float)#})[#BarEnum#]{{; name=.+$}} -// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Bar12({#Int#}, {#(Float, Double)#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar1[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar2()[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar3({#Int#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar4({#a: Int#}, {#b: Float#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar5({#a: Int#}, {#(Float)#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar6({#a: Int#}, {#b: (Float)#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar7({#a: Int#}, {#(b: Float, c: Double)#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar8({#a: Int#}, {#b: (c: Float, d: Double)#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar9({#Int#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar10({#Int#}, {#Float#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar11({#Int#}, {#(Float)#})[#BarEnum#]{{; name=.+$}} +// BAR_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Bar12({#Int#}, {#(Float, Double)#})[#BarEnum#]{{; name=.+$}} // BAR_ENUM_TYPE_CONTEXT: End completions // BAR_ENUM_NO_DOT: Begin completions @@ -274,8 +274,8 @@ enum QuxEnum : Int { } // QUX_ENUM_TYPE_CONTEXT: Begin completions -// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Qux1[#QuxEnum#]{{; name=.+$}} -// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific: .Qux2[#QuxEnum#]{{; name=.+$}} +// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Qux1[#QuxEnum#]{{; name=.+$}} +// QUX_ENUM_TYPE_CONTEXT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: .Qux2[#QuxEnum#]{{; name=.+$}} // QUX_ENUM_TYPE_CONTEXT: End completions // QUX_ENUM_NO_DOT: Begin completions, 7 items @@ -436,16 +436,16 @@ func testWithInvalid1() { // UNRESOLVED_1: Begin completions // UNRESOLVED_1-NOT: Baz // UNRESOLVED_1-NOT: Bar -// UNRESOLVED_1-DAG: Decl[EnumElement]/ExprSpecific: Qux1[#QuxEnum#]; name=Qux1 -// UNRESOLVED_1-DAG: Decl[EnumElement]/ExprSpecific: Qux2[#QuxEnum#]; name=Qux2 +// UNRESOLVED_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux1[#QuxEnum#]; name=Qux1 +// UNRESOLVED_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: Qux2[#QuxEnum#]; name=Qux2 // UNRESOLVED_1-NOT: Okay } func testUnqualified1(x: QuxEnum) { _ = x == .Qux1 || x == .#^UNRESOLVED_2^#Qux2 // UNRESOLVED_2: Begin completions, 2 items - // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific: Qux1[#QuxEnum#]; name=Qux1 - // UNRESOLVED_2-DAG: Decl[EnumElement]/ExprSpecific: Qux2[#QuxEnum#]; name=Qux2 + // 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: End completions _ = (x == .Qux1#^UNRESOLVED_3^#) diff --git a/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 7c2e45911211d..4b2c68a308a00 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -372,8 +372,8 @@ func testIIFE() { }() } // IN_IIFE_1: Begin completions -// IN_IIFE_1-DAG: Decl[EnumElement]/ExprSpecific: north[#SomeEnum#] -// IN_IIFE_1-DAG: Decl[EnumElement]/ExprSpecific: south[#SomeEnum#] +// IN_IIFE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: north[#SomeEnum#] +// IN_IIFE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: south[#SomeEnum#] extension Error { var myErrorNumber: Int { return 0 } diff --git a/test/IDE/complete_property_delegate_attribute.swift b/test/IDE/complete_property_delegate_attribute.swift index e6fae0d8010c8..831844db463ab 100644 --- a/test/IDE/complete_property_delegate_attribute.swift +++ b/test/IDE/complete_property_delegate_attribute.swift @@ -35,11 +35,11 @@ struct TestStruct { @MyStruct(arg1: .#^ARG_MyEnum_DOT^# var test3 // ARG_MyEnum_DOT: Begin completions, 2 items -// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/ExprSpecific: east[#MyEnum#]; name=east -// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/ExprSpecific: west[#MyEnum#]; name=west +// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: east[#MyEnum#]; name=east +// ARG_MyEnum_DOT-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: west[#MyEnum#]; name=west // ARG_MyEnum_DOT: End completions - @MyStruct(arg1: MyEnum.#^ARG_MyEnum_NOBINDING^#) + @MyStruct(arg1: MyEnum.#^ARG_MyEnum_NOBINDING^#) // ARG_MyEnum_NOBINDING: Begin completions // ARG_MyEnum_NOBINDING-DAG: Decl[EnumElement]/CurrNominal: east[#MyEnum#]; // ARG_MyEnum_NOBINDING-DAG: Decl[EnumElement]/CurrNominal: west[#MyEnum#]; diff --git a/test/IDE/complete_single_expression_return.swift b/test/IDE/complete_single_expression_return.swift index 1df478228eb2d..9ee3fc6357ec9 100644 --- a/test/IDE/complete_single_expression_return.swift +++ b/test/IDE/complete_single_expression_return.swift @@ -98,7 +98,7 @@ struct TestSingleExprClosureRetUnresolved { // TestSingleExprClosureRetUnresolved: Begin completions // TestSingleExprClosureRetUnresolved-NOT: notMine -// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; +// TestSingleExprClosureRetUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprClosureRetUnresolved-NOT: notMine // TestSingleExprClosureRetUnresolved: End completions } @@ -263,7 +263,7 @@ struct TestSingleExprFuncUnresolved { // TestSingleExprFuncUnresolved: Begin completions // TestSingleExprFuncUnresolved-NOT: notMine -// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; +// TestSingleExprFuncUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprFuncUnresolved-NOT: notMine // TestSingleExprFuncUnresolved: End completions } @@ -373,7 +373,7 @@ struct TestSingleExprAccessorUnresolved { // TestSingleExprAccessorUnresolved: Begin completions // TestSingleExprAccessorUnresolved-NOT: notMine -// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; +// TestSingleExprAccessorUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprAccessorUnresolved-NOT: notMine // TestSingleExprAccessorUnresolved: End completions } @@ -525,7 +525,7 @@ struct TestSingleExprSubscriptUnresolved { // TestSingleExprSubscriptUnresolved: Begin completions // TestSingleExprSubscriptUnresolved-NOT: notMine -// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific: myEnum[#MyEnum#]; +// TestSingleExprSubscriptUnresolved: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myEnum[#MyEnum#]; // TestSingleExprSubscriptUnresolved-NOT: notMine // TestSingleExprSubscriptUnresolved: End completions } @@ -630,7 +630,7 @@ enum TopLevelEnum { case foo } -// TopLevelEnum: Decl[EnumElement]/ExprSpecific: foo[#TopLevelEnum#]; +// TopLevelEnum: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#TopLevelEnum#]; var testAccessorUnresolvedTopLevel: TopLevelEnum { .#^testAccessorUnresolvedTopLevel^# diff --git a/test/IDE/complete_stmt_controlling_expr.swift b/test/IDE/complete_stmt_controlling_expr.swift index 37a3a5d039f8a..432a2fec2a824 100644 --- a/test/IDE/complete_stmt_controlling_expr.swift +++ b/test/IDE/complete_stmt_controlling_expr.swift @@ -519,7 +519,7 @@ func testSwitchCaseWhereExprIJ1(_ fooObject: FooStruct) { enum A { case aaa } enum B { case bbb } // UNRESOLVED_B-NOT: aaa -// UNRESOLVED_B: Decl[EnumElement]/ExprSpecific: bbb[#B#]; name=bbb +// UNRESOLVED_B: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bbb[#B#]; name=bbb // UNRESOLVED_B-NOT: aaa struct AA { diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 9feb9e6e2b4b2..a5a5aeb657ed1 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -262,25 +262,25 @@ class C4 { } } // UNRESOLVED_3: Begin completions, 2 items -// UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; name=North -// UNRESOLVED_3-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; name=South +// 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-NOT: SomeOptions1 // UNRESOLVED_3-NOT: SomeOptions2 // UNRESOLVED_3-NOT: none // UNRESOLVED_3-NOT: some( // UNRESOLVED_3_OPT: Begin completions, 5 items -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; +// 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: Keyword[nil]/ExprSpecific/Erase[1]: nil[#SomeEnum1?#]; name=nil -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none -// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1#})[#Optional#]; +// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none +// UNRESOLVED_3_OPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1#})[#Optional#]; // UNRESOLVED_3_OPT-NOT: init({#(some): // UNRESOLVED_3_OPT-NOT: init({#nilLiteral: // UNRESOLVED_3_OPTOPTOPT: Begin completions, 5 items -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; -// UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; +// 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: Keyword[nil]/ExprSpecific/Erase[1]: nil[#SomeEnum1???#]; name=nil // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none // UNRESOLVED_3_OPTOPTOPT-DAG: Decl[EnumElement]/ExprSpecific: some({#SomeEnum1??#})[#Optional#]; @@ -297,8 +297,8 @@ extension Optional where Wrapped == Somewhere { func testOptionalWithCustomExtension() { var _: Somewhere? = .#^UNRESOLVED_OPT_4^# // UNRESOLVED_OPT_4: Begin completions, 7 items -// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: earth[#Somewhere#]; -// UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: mars[#Somewhere#]; +// 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: Keyword[nil]/ExprSpecific/Erase[1]: nil[#Somewhere?#]; name=nil // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: none[#Optional#]; name=none // UNRESOLVED_OPT_4-DAG: Decl[EnumElement]/ExprSpecific: some({#Somewhere#})[#Optional#]; @@ -414,8 +414,8 @@ func testAvail1(_ x: EnumAvail1) { } // ENUM_AVAIL_1: Begin completions, 2 items // ENUM_AVAIL_1-NOT: AAA -// ENUM_AVAIL_1-DAG: Decl[EnumElement]/ExprSpecific: aaa[#EnumAvail1#]; -// ENUM_AVAIL_1-DAG: Decl[EnumElement]/ExprSpecific/NotRecommended: BBB[#EnumAvail1#]; +// 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-NOT: AAA // ENUM_AVAIL_1: End completions @@ -440,7 +440,7 @@ func testWithLiteral1() { let s: S _ = s.takeEnum(thing: .#^WITH_LITERAL_1^#, other: 1.0) // WITH_LITERAL_1: Begin completions, 1 items -// WITH_LITERAL_1-NEXT: Decl[EnumElement]/ExprSpecific: myCase[#S.MyEnum#]; +// WITH_LITERAL_1-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myCase[#S.MyEnum#]; // WITH_LITERAL_1-NEXT: End completions } func testWithLiteral2() { @@ -464,7 +464,7 @@ func testWithLiteral3() { func test(s: S) { _ = s.takeEnum(thing: .#^WITH_LITERAL_3^#, other: 1.0) // WITH_LITERAL_3: Begin completions, 1 items -// WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific: myCase[#MyEnum#]; +// WITH_LITERAL_3-NEXT: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: myCase[#MyEnum#]; // WITH_LITERAL_3-NEXT: End completions } } @@ -480,9 +480,9 @@ func enumFromOtherFile() -> EnumFromOtherFile { return .#^OTHER_FILE_1^# // Don't crash. } // OTHER_FILE_1: Begin completions -// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific: b({#String#})[#EnumFromOtherFile#]; -// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific: a({#Int#})[#EnumFromOtherFile#]; -// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific: c[#EnumFromOtherFile#]; +// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: b({#String#})[#EnumFromOtherFile#]; +// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: a({#Int#})[#EnumFromOtherFile#]; +// OTHER_FILE_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: c[#EnumFromOtherFile#]; // OTHER_FILE_1: End completions struct NonOptSet { @@ -522,8 +522,8 @@ func testInStringInterpolation() { let y = "enum: \(.#^STRING_INTERPOLATION_INVALID^#)" // Dont'crash. } // STRING_INTERPOLATION_1: Begin completions -// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific: foo[#MyEnum#]; -// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific: bar[#MyEnum#]; +// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#MyEnum#]; +// STRING_INTERPOLATION_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#MyEnum#]; // STRING_INTERPOLATION_1: End completions class BaseClass { @@ -607,10 +607,10 @@ struct HasOverloaded { func testOverload(val: HasOverloaded) { let _ = val.takeEnum(.#^OVERLOADED_METHOD_1^#) // OVERLOADED_METHOD_1: Begin completions, 4 items -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific: South[#SomeEnum1#]; name=South -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific: North[#SomeEnum1#]; name=North -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific: East[#SomeEnum2#]; name=East -// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific: West[#SomeEnum2#]; name=West +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: South[#SomeEnum1#]; name=South +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: North[#SomeEnum1#]; name=North +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: East[#SomeEnum2#]; name=East +// OVERLOADED_METHOD_1-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: West[#SomeEnum2#]; name=West // OVERLOADED_METHOD_1: End completions let _ = HasOverloaded.init(e: .#^OVERLOADED_INIT_1^#) diff --git a/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response b/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response index ee2d1f510a44e..0b64d25037126 100644 --- a/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response +++ b/test/SourceKit/CodeComplete/complete_unresolvedmember.swift.response @@ -19,7 +19,7 @@ key.description: "east", key.typename: "Foo", key.context: source.codecompletion.context.exprspecific, - key.typerelation: source.codecompletion.typerelation.unrelated, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO4eastyA2CmF", key.modulename: "complete_unresolvedmember" @@ -67,7 +67,7 @@ key.description: "other(String)", key.typename: "Foo", key.context: source.codecompletion.context.exprspecific, - key.typerelation: source.codecompletion.typerelation.unrelated, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO5otheryACSScACmF", key.modulename: "complete_unresolvedmember" @@ -79,7 +79,7 @@ key.description: "west", key.typename: "Foo", key.context: source.codecompletion.context.exprspecific, - key.typerelation: source.codecompletion.typerelation.unrelated, + key.typerelation: source.codecompletion.typerelation.identical, key.num_bytes_to_erase: 0, key.associated_usrs: "s:25complete_unresolvedmember3FooO4westyA2CmF", key.modulename: "complete_unresolvedmember" From 4aca39e18e5ffbee6defc895bf7b421075fc53cd Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Mon, 3 Feb 2020 17:40:03 -0800 Subject: [PATCH 125/237] [Diagnostics] Provide a better fix-it when trying to boolean-negate an optional --- include/swift/AST/DiagnosticsSema.def | 2 +- include/swift/AST/KnownIdentifiers.def | 1 + lib/Sema/CSDiagnostics.cpp | 35 ++++++++++++++++++++++---- test/Constraints/diagnostics.swift | 4 +-- test/Constraints/fixes.swift | 8 +++--- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 02438f31c3bd5..652bdec909eca 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3177,7 +3177,7 @@ NOTE(note_explicitly_compare_cftypeid,none, ERROR(optional_used_as_boolean,none, "optional type %0 cannot be used as a boolean; " - "test for '!= nil' instead", (Type)) + "test for '%select{!|=}1= nil' instead", (Type, bool)) ERROR(interpolation_missing_proto,none, "string interpolation requires the protocol 'ExpressibleByStringInterpolation' to be defined", diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index fe8511ad4756f..47be9b7800087 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -143,6 +143,7 @@ IDENTIFIER_WITH_NAME(NativeClassLayout, "_NativeClass") // Operators IDENTIFIER_WITH_NAME(MatchOperator, "~=") IDENTIFIER_WITH_NAME(EqualsOperator, "==") +IDENTIFIER_WITH_NAME(NegationOperator, "!") IDENTIFIER_WITH_NAME(derived_enum_equals, "__derived_enum_equals") IDENTIFIER_WITH_NAME(derived_struct_equals, "__derived_struct_equals") diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index b85e37691bb76..e5ecda9d9b313 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2306,14 +2306,37 @@ bool ContextualFailure::diagnoseConversionToBool() const { return true; } + // Determine if the boolean negation operator was applied to the anchor. This + // upwards traversal of the AST is somewhat fragile, but enables much better + // diagnostics if someone attempts to use an optional or integer as a boolean + // condition. + SourceLoc notOperatorLoc; + if (Expr *parent = findParentExpr(getAnchor())) { + if (isa(parent) && parent->isImplicit()) { + if ((parent = findParentExpr(parent))) { + auto parentOperatorApplication = dyn_cast(parent); + if (parentOperatorApplication) { + auto operatorRefExpr = + dyn_cast(parentOperatorApplication->getFn()); + if (operatorRefExpr && operatorRefExpr->getDecl()->getBaseName() == + getASTContext().Id_NegationOperator) { + notOperatorLoc = operatorRefExpr->getLoc(); + } + } + } + } + } + // If we're trying to convert something from optional type to Bool, then a // comparison against nil was probably expected. - // TODO: It would be nice to handle "!x" --> x == false, but we have no way - // to get to the parent expr at present. auto fromType = getFromType(); if (fromType->getOptionalObjectType()) { StringRef prefix = "(("; - StringRef suffix = ") != nil)"; + StringRef suffix; + if (notOperatorLoc.isValid()) + suffix = ") == nil)"; + else + suffix = ") != nil)"; // Check if we need the inner parentheses. // Technically we only need them if there's something in 'expr' with @@ -2325,9 +2348,11 @@ bool ContextualFailure::diagnoseConversionToBool() const { } // FIXME: The outer parentheses may be superfluous too. - emitDiagnostic(expr->getLoc(), diag::optional_used_as_boolean, fromType) + emitDiagnostic(expr->getLoc(), diag::optional_used_as_boolean, fromType, + notOperatorLoc.isValid()) .fixItInsert(expr->getStartLoc(), prefix) - .fixItInsertAfter(expr->getEndLoc(), suffix); + .fixItInsertAfter(expr->getEndLoc(), suffix) + .fixItRemove(notOperatorLoc); return true; } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index c603df2c04b76..a3ddfbab59303 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -634,11 +634,11 @@ func r18397777(_ d : r21447318?) { if d { // expected-error {{optional type 'r21447318?' cannot be used as a boolean; test for '!= nil' instead}} {{6-6=(}} {{7-7= != nil)}} } - if !d { // expected-error {{optional type 'r21447318?' cannot be used as a boolean; test for '!= nil' instead}} {{7-7=(}} {{8-8= != nil)}} + if !d { // expected-error {{optional type 'r21447318?' cannot be used as a boolean; test for '== nil' instead}} {{6-7=}} {{7-7=(}} {{8-8= == nil)}} } - if !Optional(c) { // expected-error {{optional type 'Optional' cannot be used as a boolean; test for '!= nil' instead}} {{7-7=(}} {{18-18= != nil)}} + if !Optional(c) { // expected-error {{optional type 'Optional' cannot be used as a boolean; test for '== nil' instead}} {{6-7=}} {{7-7=(}} {{18-18= == nil)}} } } diff --git a/test/Constraints/fixes.swift b/test/Constraints/fixes.swift index 088672386864f..1d34efad2d1d5 100644 --- a/test/Constraints/fixes.swift +++ b/test/Constraints/fixes.swift @@ -129,11 +129,11 @@ var ciuo: C! = nil if co {} // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{4-4=(}} {{6-6= != nil)}} if ciuo {} // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{4-4=(}} {{8-8= != nil)}} co ? true : false // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{1-1=(}} {{3-3= != nil)}} -!co ? false : true // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{2-2=(}} {{4-4= != nil)}} +!co ? false : true // expected-error{{optional type 'C?' cannot be used as a boolean; test for '== nil' instead}} {{1-2=}} {{2-2=(}} {{4-4= == nil)}} ciuo ? true : false // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{1-1=(}} {{5-5= != nil)}} -!ciuo ? false : true // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{2-2=(}} {{6-6= != nil)}} -!co // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{2-2=(}} {{4-4= != nil)}} -!ciuo // expected-error{{optional type 'C?' cannot be used as a boolean; test for '!= nil' instead}}{{2-2=(}} {{6-6= != nil)}} +!ciuo ? false : true // expected-error{{optional type 'C?' cannot be used as a boolean; test for '== nil' instead}} {{1-2=}} {{2-2=(}} {{6-6= == nil)}} +!co // expected-error{{optional type 'C?' cannot be used as a boolean; test for '== nil' instead}} {{1-2=}} {{2-2=(}} {{4-4= == nil)}} +!ciuo // expected-error{{optional type 'C?' cannot be used as a boolean; test for '== nil' instead}} {{1-2=}} {{2-2=(}} {{6-6= == nil)}} // Forgotten ! or ? var someInt = co.a // expected-error{{value of optional type 'C?' must be unwrapped to refer to member 'a' of wrapped base type 'C'}} From 0c0018fb9b3eafd9ef1571d193487966d57340c0 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 3 Feb 2020 17:40:49 -0800 Subject: [PATCH 126/237] [AutoDiff] Improve `getConstrainedDerivativeGenericSignature` helper. (#29620) Move `getConstrainedDerivativeGenericSignature` under `autodiff` namespace. Improve naming and documentation. --- include/swift/AST/AutoDiff.h | 13 +++++++++++++ lib/AST/AutoDiff.cpp | 27 +++++++++++++++++++++++++++ lib/SIL/SILFunctionType.cpp | 34 ++-------------------------------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index f3c74e9b1d632..3bf60d0d77e03 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -29,6 +29,7 @@ namespace swift { class AnyFunctionType; +class SILFunctionType; class TupleType; /// A function type differentiability kind. @@ -231,6 +232,18 @@ void getSubsetParameterTypes(IndexSubset *indices, AnyFunctionType *type, SmallVectorImpl &results, bool reverseCurryLevels = false); +/// "Constrained" derivative generic signatures require all differentiability +/// parameters to conform to the `Differentiable` protocol. +/// +/// Returns the "constrained" derivative generic signature given: +/// - An original SIL function type. +/// - Differentiability parameter indices. +/// - A possibly "unconstrained" derivative generic signature. +GenericSignature +getConstrainedDerivativeGenericSignature(SILFunctionType *originalFnTy, + IndexSubset *diffParamIndices, + GenericSignature derivativeGenSig); + } // end namespace autodiff } // end namespace swift diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp index fa99620c4ab64..dbfd72bc1f626 100644 --- a/lib/AST/AutoDiff.cpp +++ b/lib/AST/AutoDiff.cpp @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// #include "swift/AST/AutoDiff.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" using namespace swift; @@ -67,6 +69,31 @@ void autodiff::getSubsetParameterTypes(IndexSubset *subset, } } +GenericSignature autodiff::getConstrainedDerivativeGenericSignature( + SILFunctionType *originalFnTy, IndexSubset *diffParamIndices, + GenericSignature derivativeGenSig) { + if (!derivativeGenSig) + derivativeGenSig = originalFnTy->getSubstGenericSignature(); + if (!derivativeGenSig) + return nullptr; + // Constrain all differentiability parameters to `Differentiable`. + auto &ctx = originalFnTy->getASTContext(); + auto *diffableProto = ctx.getProtocol(KnownProtocolKind::Differentiable); + SmallVector requirements; + for (unsigned paramIdx : diffParamIndices->getIndices()) { + auto paramType = originalFnTy->getParameters()[paramIdx].getInterfaceType(); + Requirement req(RequirementKind::Conformance, paramType, + diffableProto->getDeclaredType()); + requirements.push_back(req); + } + return evaluateOrDefault( + ctx.evaluator, + AbstractGenericSignatureRequest{derivativeGenSig.getPointer(), + /*addedGenericParams*/ {}, + std::move(requirements)}, + nullptr); +} + Type TangentSpace::getType() const { switch (kind) { case Kind::TangentVector: diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index 8b667f595bdb2..f31d86ad3b51f 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -191,36 +191,6 @@ SILFunctionType::getWitnessMethodClass(SILModule &M) const { return nullptr; } -// Returns the canonical generic signature for an autodiff derivative function -// given an existing derivative function generic signature. All -// differentiability parameters are required to conform to `Differentiable`. -static CanGenericSignature getAutoDiffDerivativeFunctionGenericSignature( - CanGenericSignature derivativeFnGenSig, - ArrayRef originalParameters, - IndexSubset *parameterIndices, ModuleDecl *module) { - if (!derivativeFnGenSig) - return nullptr; - auto &ctx = module->getASTContext(); - GenericSignatureBuilder builder(ctx); - // Add derivative function generic signature. - builder.addGenericSignature(derivativeFnGenSig); - // All differentiability parameters are required to conform to - // `Differentiable`. - auto source = - GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); - auto *differentiableProtocol = - ctx.getProtocol(KnownProtocolKind::Differentiable); - for (unsigned paramIdx : parameterIndices->getIndices()) { - auto paramType = originalParameters[paramIdx].getInterfaceType(); - Requirement req(RequirementKind::Conformance, paramType, - differentiableProtocol->getDeclaredType()); - builder.addRequirement(req, source, module); - } - return std::move(builder) - .computeGenericSignature(SourceLoc(), /*allowConcreteGenericParams*/ true) - ->getCanonicalSignature(); -} - CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( IndexSubset *parameterIndices, unsigned resultIndex, AutoDiffDerivativeFunctionKind kind, TypeConverter &TC, @@ -243,8 +213,8 @@ CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( // Get the canonical derivative function generic signature. if (!derivativeFnGenSig) derivativeFnGenSig = getSubstGenericSignature(); - derivativeFnGenSig = getAutoDiffDerivativeFunctionGenericSignature( - derivativeFnGenSig, getParameters(), parameterIndices, &TC.M); + derivativeFnGenSig = autodiff::getConstrainedDerivativeGenericSignature( + this, parameterIndices, derivativeFnGenSig).getCanonicalSignature(); // Given a type, returns its formal SIL parameter info. auto getTangentParameterInfoForOriginalResult = From 7d498b1d8134066305f461e486aa8a4e8e7d9cf2 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Mon, 3 Feb 2020 17:45:22 -0800 Subject: [PATCH 127/237] [SourceKit] Add test cases for type relation in code completion --- .../CodeComplete/complete_typerelation.swift | 24 +++++ ...te_typerelation.swift.convertible.response | 98 +++++++++++++++++++ ...lete_typerelation.swift.identical.response | 98 +++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 test/SourceKit/CodeComplete/complete_typerelation.swift create mode 100644 test/SourceKit/CodeComplete/complete_typerelation.swift.convertible.response create mode 100644 test/SourceKit/CodeComplete/complete_typerelation.swift.identical.response diff --git a/test/SourceKit/CodeComplete/complete_typerelation.swift b/test/SourceKit/CodeComplete/complete_typerelation.swift new file mode 100644 index 0000000000000..c62b0d05d96ce --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_typerelation.swift @@ -0,0 +1,24 @@ +protocol MyProto {} +enum MyEnum : MyProto { + case foo + case bar(Int) + + static func staticReturnVoid() {} + static func staticReturnMyEnum() -> MyEnum { return .foo } + func intanceReturnVoid() {} + func intanceReturnMyEnum() -> MyEnum { return .foo } +} + +func testIdenticalContext() -> MyEnum { + return MyEnum. +} + +func testConvertibleContext() -> MyProto { + return MyEnum. +} + +// RUN: %sourcekitd-test -req=complete -pos=13:17 %s -- %s > %t.identical.response +// RUN: diff -u %s.identical.response %t.identical.response + +// RUN: %sourcekitd-test -req=complete -pos=17:17 %s -- %s > %t.convertible.response +// RUN: diff -u %s.convertible.response %t.convertible.response diff --git a/test/SourceKit/CodeComplete/complete_typerelation.swift.convertible.response b/test/SourceKit/CodeComplete/complete_typerelation.swift.convertible.response new file mode 100644 index 0000000000000..fe2a7d94a0eee --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_typerelation.swift.convertible.response @@ -0,0 +1,98 @@ +{ + key.results: [ + { + key.kind: source.lang.swift.decl.enumelement, + key.name: "bar()", + key.sourcetext: "bar(<#T##Int#>)", + key.description: "bar(Int)", + key.typename: "MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.convertible, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO3baryACSicACmF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.enumelement, + key.name: "foo", + key.sourcetext: "foo", + key.description: "foo", + key.typename: "MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.convertible, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO3fooyA2CmF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "intanceReturnMyEnum(:)", + key.sourcetext: "intanceReturnMyEnum(<#T##self: MyEnum##MyEnum#>)", + key.description: "intanceReturnMyEnum(self: MyEnum)", + key.typename: "() -> MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.convertible, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO013intanceReturncD0ACyF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "intanceReturnVoid(:)", + key.sourcetext: "intanceReturnVoid(<#T##self: MyEnum##MyEnum#>)", + key.description: "intanceReturnVoid(self: MyEnum)", + key.typename: "() -> Void", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.invalid, + key.num_bytes_to_erase: 0, + key.not_recommended: 1, + key.associated_usrs: "s:21complete_typerelation6MyEnumO17intanceReturnVoidyyF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.keyword, + key.name: "self", + key.sourcetext: "self", + key.description: "self", + key.typename: "MyEnum.Type", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, + key.num_bytes_to_erase: 0 + }, + { + key.kind: source.lang.swift.decl.function.method.class, + key.name: "staticReturnMyEnum()", + key.sourcetext: "staticReturnMyEnum()", + key.description: "staticReturnMyEnum()", + key.typename: "MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.convertible, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO012staticReturncD0ACyFZ", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.function.method.class, + key.name: "staticReturnVoid()", + key.sourcetext: "staticReturnVoid()", + key.description: "staticReturnVoid()", + key.typename: "Void", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.invalid, + key.num_bytes_to_erase: 0, + key.not_recommended: 1, + key.associated_usrs: "s:21complete_typerelation6MyEnumO16staticReturnVoidyyFZ", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.keyword, + key.name: "Type", + key.sourcetext: "Type", + key.description: "Type", + key.typename: "MyEnum.Type", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, + key.num_bytes_to_erase: 0 + } + ] +} diff --git a/test/SourceKit/CodeComplete/complete_typerelation.swift.identical.response b/test/SourceKit/CodeComplete/complete_typerelation.swift.identical.response new file mode 100644 index 0000000000000..10611516790f1 --- /dev/null +++ b/test/SourceKit/CodeComplete/complete_typerelation.swift.identical.response @@ -0,0 +1,98 @@ +{ + key.results: [ + { + key.kind: source.lang.swift.decl.enumelement, + key.name: "bar()", + key.sourcetext: "bar(<#T##Int#>)", + key.description: "bar(Int)", + key.typename: "MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO3baryACSicACmF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.enumelement, + key.name: "foo", + key.sourcetext: "foo", + key.description: "foo", + key.typename: "MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO3fooyA2CmF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "intanceReturnMyEnum(:)", + key.sourcetext: "intanceReturnMyEnum(<#T##self: MyEnum##MyEnum#>)", + key.description: "intanceReturnMyEnum(self: MyEnum)", + key.typename: "() -> MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO013intanceReturncD0ACyF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.function.method.instance, + key.name: "intanceReturnVoid(:)", + key.sourcetext: "intanceReturnVoid(<#T##self: MyEnum##MyEnum#>)", + key.description: "intanceReturnVoid(self: MyEnum)", + key.typename: "() -> Void", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.invalid, + key.num_bytes_to_erase: 0, + key.not_recommended: 1, + key.associated_usrs: "s:21complete_typerelation6MyEnumO17intanceReturnVoidyyF", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.keyword, + key.name: "self", + key.sourcetext: "self", + key.description: "self", + key.typename: "MyEnum.Type", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, + key.num_bytes_to_erase: 0 + }, + { + key.kind: source.lang.swift.decl.function.method.class, + key.name: "staticReturnMyEnum()", + key.sourcetext: "staticReturnMyEnum()", + key.description: "staticReturnMyEnum()", + key.typename: "MyEnum", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.identical, + key.num_bytes_to_erase: 0, + key.associated_usrs: "s:21complete_typerelation6MyEnumO012staticReturncD0ACyFZ", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.decl.function.method.class, + key.name: "staticReturnVoid()", + key.sourcetext: "staticReturnVoid()", + key.description: "staticReturnVoid()", + key.typename: "Void", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.invalid, + key.num_bytes_to_erase: 0, + key.not_recommended: 1, + key.associated_usrs: "s:21complete_typerelation6MyEnumO16staticReturnVoidyyFZ", + key.modulename: "complete_typerelation" + }, + { + key.kind: source.lang.swift.keyword, + key.name: "Type", + key.sourcetext: "Type", + key.description: "Type", + key.typename: "MyEnum.Type", + key.context: source.codecompletion.context.thisclass, + key.typerelation: source.codecompletion.typerelation.unrelated, + key.num_bytes_to_erase: 0 + } + ] +} From 77da6bd3a7c3c2ec02bb0f13e7341cf867cab0d3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Feb 2020 17:51:54 -0800 Subject: [PATCH 128/237] [Constraint graph] Don't drop unidirectional constraints. Ye olde "break should have been continue" bug. This undoubtedly has problems elsewhere, but I don't have a specific case until the next commit. --- lib/Sema/ConstraintGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/ConstraintGraph.cpp b/lib/Sema/ConstraintGraph.cpp index fca49c43e1212..c32244d02ec68 100644 --- a/lib/Sema/ConstraintGraph.cpp +++ b/lib/Sema/ConstraintGraph.cpp @@ -942,7 +942,7 @@ namespace { for (auto lhsTypeRep : lhsTypeReps) { for (auto rhsTypeRep : rhsTypeReps) { if (lhsTypeRep == rhsTypeRep) - break; + continue; insertIfUnique(oneWayDigraph[rhsTypeRep].outAdjacencies,lhsTypeRep); insertIfUnique(oneWayDigraph[lhsTypeRep].inAdjacencies,rhsTypeRep); From f8eee503108c50ee2ea13f43a8a740ffc0207542 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Feb 2020 17:52:44 -0800 Subject: [PATCH 129/237] [Constraint solver] Fix function builders with single-expression closures Fix a few related issues involving the interaction with single-expression closures: * A single-expression closure can have a "return" in it; in such cases, disable the function-builder transform. * Have the function builder constraint generator look through the "return" statement in a single-expression closure the same way as solution application does Fixes rdar://problem/59045763, where we rejected some well-formed code involving a single-expression closure with a "return" keyword. --- lib/Sema/BuilderTransform.cpp | 32 +++++++++---------- lib/Sema/CSSimplify.cpp | 9 +++--- lib/Sema/ConstraintSystem.h | 5 ++- test/Constraints/function_builder_diags.swift | 15 +++++++++ 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 0dff01b97cc29..90b024696a533 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -244,7 +244,15 @@ class BuilderClosureVisitor expressions.push_back(buildVarRef(childVar, childVar->getLoc())); }; - for (const auto &node : braceStmt->getElements()) { + for (auto node : braceStmt->getElements()) { + // Implicit returns in single-expression function bodies are treated + // as the expression. + if (auto returnStmt = + dyn_cast_or_null(node.dyn_cast())) { + assert(returnStmt->isImplicit()); + node = returnStmt->getResult(); + } + if (auto stmt = node.dyn_cast()) { addChild(visit(stmt)); continue; @@ -291,14 +299,9 @@ class BuilderClosureVisitor } VarDecl *visitReturnStmt(ReturnStmt *stmt) { - // Allow implicit returns due to 'return' elision. - if (!stmt->isImplicit() || !stmt->hasResult()) { - if (!unhandledNode) - unhandledNode = stmt; - return nullptr; - } - - return captureExpr(stmt->getResult(), /*oneWay=*/true); + if (!unhandledNode) + unhandledNode = stmt; + return nullptr; } VarDecl *visitDoStmt(DoStmt *doStmt) { @@ -1117,7 +1120,8 @@ Optional TypeChecker::applyFunctionBuilderBodyTransform( return nullptr; } -ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( +Optional +ConstraintSystem::matchFunctionBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator) { @@ -1141,7 +1145,7 @@ ConstraintSystem::TypeMatchResult ConstraintSystem::matchFunctionBuilder( case FunctionBuilderBodyPreCheck::HasReturnStmt: // If the body has a return statement, suppress the transform but // continue solving the constraint system. - return getTypeMatchSuccess(); + return None; } // Check the form of this body to see if we can apply the @@ -1287,12 +1291,6 @@ class PreCheckFunctionBuilderApplication : public ASTWalker { llvm::Expected PreCheckFunctionBuilderRequest::evaluate(Evaluator &eval, AnyFunctionRef fn) const { - // Single-expression closures should already have been pre-checked. - if (auto closure = fn.getAbstractClosureExpr()) { - if (closure->hasSingleExpressionBody()) - return FunctionBuilderBodyPreCheck::Okay; - } - return PreCheckFunctionBuilderApplication(fn, false).run(); } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 71d01c19f9c19..746d02c41334c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -6635,10 +6635,11 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, auto *calleeLocator = getCalleeLocator(getConstraintLocator(locator)); if (auto functionBuilderType = getFunctionBuilderTypeFor( *this, argToParam->getParamIdx(), calleeLocator)) { - auto result = matchFunctionBuilder( - closure, functionBuilderType, closureType->getResult(), - ConstraintKind::Conversion, calleeLocator, locator); - return result.isSuccess(); + if (auto result = matchFunctionBuilder( + closure, functionBuilderType, closureType->getResult(), + ConstraintKind::Conversion, calleeLocator, locator)) { + return result->isSuccess(); + } } } } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 7d733b80df614..b68ab47317084 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3726,7 +3726,10 @@ class ConstraintSystem { void simplifyDisjunctionChoice(Constraint *choice); /// Apply the given function builder to the closure expression. - TypeMatchResult matchFunctionBuilder( + /// + /// \returns \c None when the function builder cannot be applied at all, + /// otherwise the result of applying the function builder. + Optional matchFunctionBuilder( AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, ConstraintLocator *calleeLocator, ConstraintLocatorBuilder locator); diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index 8769496a8bb7b..4fca9f41406f6 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -284,3 +284,18 @@ func checkConditions(cond: Bool) { } } } + +// Check that a closure with a single "return" works with function builders. +func checkSingleReturn(cond: Bool) { + tuplify(cond) { value in + return (value, 17) + } + + tuplify(cond) { value in + (value, 17) + } + + tuplify(cond) { + ($0, 17) + } +} From 676436640ca3148317873038de1ffe325f6517d2 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 3 Feb 2020 18:26:17 -0800 Subject: [PATCH 130/237] Add SILGenSourceFileRequest Replace SILGenModule::emitSourceFile with SILGenSourceFileRequest so we have an explicit source file to associate any lookups with. --- include/swift/AST/SILGenRequests.h | 25 +++++++++++++++++++++++++ include/swift/AST/SILGenTypeIDZone.def | 3 +++ lib/SILGen/SILGen.cpp | 22 +++++++++++++--------- lib/SILGen/SILGen.h | 3 --- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/SILGenRequests.h b/include/swift/AST/SILGenRequests.h index 3567673172577..876a5efebbc8a 100644 --- a/include/swift/AST/SILGenRequests.h +++ b/include/swift/AST/SILGenRequests.h @@ -31,6 +31,7 @@ class SourceFile; namespace Lowering { class TypeConverter; + class SILGenModule; } /// Report that a request of the given kind is being evaluated, so it @@ -96,6 +97,30 @@ void simple_display(llvm::raw_ostream &out, const SILGenDescriptor &d); SourceLoc extractNearestSourceLoc(const SILGenDescriptor &desc); +class SILGenSourceFileRequest : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, + Lowering::SILGenModule *SGM, SourceFile *SF) const; + +public: + bool isCached() const { return true; } +}; + +inline void +simple_display(llvm::raw_ostream &out, const Lowering::SILGenModule *SGM) { + // No-op +} + /// The zone number for SILGen. #define SWIFT_TYPEID_ZONE SILGen #define SWIFT_TYPEID_HEADER "swift/AST/SILGenTypeIDZone.def" diff --git a/include/swift/AST/SILGenTypeIDZone.def b/include/swift/AST/SILGenTypeIDZone.def index b80a7e49664e7..7bb5ec3443aa7 100644 --- a/include/swift/AST/SILGenTypeIDZone.def +++ b/include/swift/AST/SILGenTypeIDZone.def @@ -17,3 +17,6 @@ SWIFT_REQUEST(SILGen, GenerateSILRequest, std::unique_ptr(SILGenDescriptor), Uncached, NoLocationInfo) +SWIFT_REQUEST(SILGen, SILGenSourceFileRequest, + bool(Lowering::SILGenModule *, SourceFile *), + Uncached, NoLocationInfo) diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 4060f7c25c5a2..2786fdf9e7d66 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1669,22 +1669,24 @@ class SourceFileScope { } // end anonymous namespace -void SILGenModule::emitSourceFile(SourceFile *sf) { - SourceFileScope scope(*this, sf); - FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-file", sf); +llvm::Expected +SILGenSourceFileRequest::evaluate(Evaluator &evaluator, + SILGenModule *SGM, SourceFile *sf) const { + SourceFileScope scope(*SGM, sf); for (Decl *D : sf->getTopLevelDecls()) { - FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-decl", D); - visit(D); + FrontendStatsTracer StatsTracer(SGM->getASTContext().Stats, "SILgen-decl", D); + SGM->visit(D); } for (TypeDecl *TD : sf->LocalTypeDecls) { - FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-tydecl", TD); + FrontendStatsTracer StatsTracer(SGM->getASTContext().Stats, "SILgen-tydecl", TD); // FIXME: Delayed parsing would prevent these types from being added to the // module in the first place. if (TD->getDeclContext()->getInnermostSkippedFunctionContext()) continue; - visit(TD); + SGM->visit(TD); } + return true; } //===----------------------------------------------------------------------===// @@ -1708,7 +1710,8 @@ SILModule::constructSIL(ModuleDecl *mod, TypeConverter &tc, if (SF) { if (auto *file = dyn_cast(SF)) { - SGM.emitSourceFile(file); + (void)evaluateOrDefault(file->getASTContext().evaluator, + SILGenSourceFileRequest{&SGM, file}, false); } else if (auto *file = dyn_cast(SF)) { if (file->isSIB()) M->getSILLoader()->getAllForModule(mod->getName(), file); @@ -1718,7 +1721,8 @@ SILModule::constructSIL(ModuleDecl *mod, TypeConverter &tc, auto nextSF = dyn_cast(file); if (!nextSF || nextSF->ASTStage != SourceFile::TypeChecked) continue; - SGM.emitSourceFile(nextSF); + (void)evaluateOrDefault(nextSF->getASTContext().evaluator, + SILGenSourceFileRequest{&SGM, nextSF}, false); } // Also make sure to process any intermediate files that may contain SIL diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 021a16e5de469..c4c9d1ed92b8f 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -208,9 +208,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { void emitAbstractFuncDecl(AbstractFunctionDecl *AFD); - /// Generate code for a source file of the module. - void emitSourceFile(SourceFile *sf); - /// Generates code for the given FuncDecl and adds the /// SILFunction to the current SILModule under the name SILDeclRef(decl). For /// curried functions, curried entry point Functions are also generated and From 377b7b131edacf3309fa7095797c8cdec3915857 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 3 Feb 2020 20:58:13 -0800 Subject: [PATCH 131/237] [pattern-match] Make sure that when we are extracting a tuple that we fail if we have a non-instruction result. The specific problem here is this: ``` sil @builtin_array_opt_index_raw_pointer_to_index_addr_no_crash : $@convention(thin) (Builtin.RawPointer, Builtin. Word) -> Builtin.Word { bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): %2 = index_raw_pointer %0 : $Builtin.RawPointer, %1 : $Builtin.Word %3 = pointer_to_address %2 : $Builtin.RawPointer to [strict] $*Builtin.Word %4 = load %3 : $*Builtin.Word return %4 : $Builtin.Word } ``` before this commit, we unconditionally called getDefiningInstruction when looking at %1. This of course returned a null value causing the compiler to crash in a dyn_cast(). rdar://59138369 --- include/swift/SIL/PatternMatch.h | 7 ++++++- test/SILOptimizer/sil_combine.sil | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/swift/SIL/PatternMatch.h b/include/swift/SIL/PatternMatch.h index ef75ce6fa1237..8d451f9e73db8 100644 --- a/include/swift/SIL/PatternMatch.h +++ b/include/swift/SIL/PatternMatch.h @@ -397,7 +397,12 @@ template struct tupleextractoperation_ty { unsigned index; tupleextractoperation_ty(const LTy &Left, unsigned i) : L(Left), index(i) {} - bool match(SILValue v) { return match(v->getDefiningInstruction()); } + bool match(SILValue v) { + auto *inst = v->getDefiningInstruction(); + if (!inst) + return false; + return match(inst); + } template bool match(ITy *V) { if (auto *TEI = dyn_cast(V)) { diff --git a/test/SILOptimizer/sil_combine.sil b/test/SILOptimizer/sil_combine.sil index 34b01f4113b5f..0d859b08892f7 100644 --- a/test/SILOptimizer/sil_combine.sil +++ b/test/SILOptimizer/sil_combine.sil @@ -2422,6 +2422,16 @@ bb0(%0 : $Builtin.Int64, %1 : $Builtin.RawPointer, %2 : $Builtin.RawPointer): return %13 : $() } +// Make sure we do not crash here. This ensures that when checking for +// tuple_extract, we check if we have an argument or not. +sil @builtin_array_opt_index_raw_pointer_to_index_addr_no_crash : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> Builtin.Word { +bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word): + %2 = index_raw_pointer %0 : $Builtin.RawPointer, %1 : $Builtin.Word + %3 = pointer_to_address %2 : $Builtin.RawPointer to [strict] $*Builtin.Word + %4 = load %3 : $*Builtin.Word + return %4 : $Builtin.Word +} + // CHECK-LABEL: sil @cmp_zext_peephole // CHECK: bb0([[Arg1:%.*]] : $Builtin.Word, [[Arg2:%.*]] : $Builtin.Word): // CHECK: [[ZA1:%.*]] = builtin "zextOrBitCast_Word_Int64"([[Arg1]] : $Builtin.Word) : $Builtin.Int64 From 166555c34fb57b7597e29090962d30ca157b8027 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Mon, 3 Feb 2020 18:17:00 -0800 Subject: [PATCH 132/237] [Diagnostics] Better diagnostic for integer used as a boolean condition --- include/swift/AST/DiagnosticsSema.def | 3 ++ include/swift/AST/KnownProtocols.def | 1 + lib/IRGen/GenMeta.cpp | 1 + lib/Sema/CSDiagnostics.cpp | 31 +++++++++++++++++++ .../private_frameworks_modules.swift | 8 ++--- test/Constraints/diagnostics.swift | 4 +-- test/Constraints/fixes.swift | 13 ++++++++ test/Constraints/if_expr.swift | 6 ++-- test/Misc/misc_diagnostics.swift | 6 ++-- .../import-resolution-overload.swift | 2 +- test/Parse/switch.swift | 2 +- test/Sema/pound_assert.swift | 4 +-- test/expr/expressions.swift | 12 +++---- test/stmt/statements.swift | 4 +-- 14 files changed, 73 insertions(+), 24 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 652bdec909eca..b748e09f83a92 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3178,6 +3178,9 @@ NOTE(note_explicitly_compare_cftypeid,none, ERROR(optional_used_as_boolean,none, "optional type %0 cannot be used as a boolean; " "test for '%select{!|=}1= nil' instead", (Type, bool)) +ERROR(integer_used_as_boolean,none, + "type %0 cannot be used as a boolean; " + "test for '%select{!|=}1= 0' instead", (Type, bool)) ERROR(interpolation_missing_proto,none, "string interpolation requires the protocol 'ExpressibleByStringInterpolation' to be defined", diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 42c55f634cdf3..363344f1c95b9 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -69,6 +69,7 @@ PROTOCOL_(ErrorCodeProtocol) PROTOCOL(OptionSet) PROTOCOL(CaseIterable) PROTOCOL(SIMDScalar) +PROTOCOL(BinaryInteger) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 392b6c4159f10..d494eaeb84ee8 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -4370,6 +4370,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::CaseIterable: case KnownProtocolKind::Comparable: case KnownProtocolKind::SIMDScalar: + case KnownProtocolKind::BinaryInteger: case KnownProtocolKind::ObjectiveCBridgeable: case KnownProtocolKind::DestructorSafeContainer: case KnownProtocolKind::SwiftNewtypeWrapper: diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index e5ecda9d9b313..888dc7899ddb6 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2356,6 +2356,37 @@ bool ContextualFailure::diagnoseConversionToBool() const { return true; } + // If we're trying to convert something from optional type to an integer, then + // a comparison against nil was probably expected. + auto &cs = getConstraintSystem(); + if (conformsToKnownProtocol(cs, fromType, KnownProtocolKind::BinaryInteger) && + conformsToKnownProtocol(cs, fromType, + KnownProtocolKind::ExpressibleByIntegerLiteral)) { + StringRef prefix = "(("; + StringRef suffix; + if (notOperatorLoc.isValid()) + suffix = ") == 0)"; + else + suffix = ") != 0)"; + + // Check if we need the inner parentheses. + // Technically we only need them if there's something in 'expr' with + // lower precedence than '!=', but the code actually comes out nicer + // in most cases with parens on anything non-trivial. + if (expr->canAppendPostfixExpression()) { + prefix = prefix.drop_back(); + suffix = suffix.drop_front(); + } + // FIXME: The outer parentheses may be superfluous too. + + emitDiagnostic(expr->getLoc(), diag::integer_used_as_boolean, fromType, + notOperatorLoc.isValid()) + .fixItInsert(expr->getStartLoc(), prefix) + .fixItInsertAfter(expr->getEndLoc(), suffix) + .fixItRemove(notOperatorLoc); + return true; + } + return false; } diff --git a/test/ClangImporter/private_frameworks_modules.swift b/test/ClangImporter/private_frameworks_modules.swift index bf8f5a4d71a37..659f4a984f26c 100644 --- a/test/ClangImporter/private_frameworks_modules.swift +++ b/test/ClangImporter/private_frameworks_modules.swift @@ -13,7 +13,7 @@ import PrivateAsParallel_Private #error("OLD or NEW must be defined") #endif -let _: Bool = PSGlobal // expected-error {{cannot convert value of type 'Int32'}} -let _: Bool = PSPrivateGlobal // expected-error {{cannot convert value of type 'Int32'}} -let _: Bool = PPGlobal // expected-error {{cannot convert value of type 'Int32'}} -let _: Bool = PPPrivateGlobal // expected-error {{cannot convert value of type 'Int32'}} +let _: Bool = PSGlobal // expected-error {{type 'Int32' cannot be used as a boolean}} +let _: Bool = PSPrivateGlobal // expected-error {{type 'Int32' cannot be used as a boolean}} +let _: Bool = PPGlobal // expected-error {{type 'Int32' cannot be used as a boolean}} +let _: Bool = PPPrivateGlobal // expected-error {{type 'Int32' cannot be used as a boolean}} diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index a3ddfbab59303..043775a16017c 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -195,10 +195,10 @@ func r17224804(_ monthNumber : Int) { // QoI: Operand of postfix '!' should have optional type; type is 'Int?' func r17020197(_ x : Int?, y : Int) { - if x! { } // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + if x! { } // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} // QoI: diagnostic for using an integer in a condition is utterly terrible - if y {} // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + if y {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} } // QoI: Boolean expr not treated as Bool type when function return type is different diff --git a/test/Constraints/fixes.swift b/test/Constraints/fixes.swift index 1d34efad2d1d5..d61b81f4287a3 100644 --- a/test/Constraints/fixes.swift +++ b/test/Constraints/fixes.swift @@ -135,6 +135,19 @@ ciuo ? true : false // expected-error{{optional type 'C?' cannot be used as a bo !co // expected-error{{optional type 'C?' cannot be used as a boolean; test for '== nil' instead}} {{1-2=}} {{2-2=(}} {{4-4= == nil)}} !ciuo // expected-error{{optional type 'C?' cannot be used as a boolean; test for '== nil' instead}} {{1-2=}} {{2-2=(}} {{6-6= == nil)}} +// Used an integer in a conditional expression +var n1: Int = 1 +var n2: UInt8 = 0 +var n3: Int16 = 2 + +if n1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} {{4-4=(}} {{6-6= != 0)}} +if !n1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '== 0' instead}} {{4-5=}} {{5-5=(}} {{7-7= == 0)}} +n1 ? true : false // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} {{1-1=(}} {{3-3= != 0)}} +!n1 ? false : true // expected-error {{type 'Int' cannot be used as a boolean; test for '== 0' instead}} {{1-2=}} {{2-2=(}} {{4-4= == 0)}} +!n1 // expected-error {{type 'Int' cannot be used as a boolean; test for '== 0' instead}} {{1-2=}} {{2-2=(}} {{4-4= == 0)}} +if n2 {} // expected-error {{type 'UInt8' cannot be used as a boolean; test for '!= 0' instead}} {{4-4=(}} {{6-6= != 0)}} +!n3 // expected-error {{type 'Int16' cannot be used as a boolean; test for '== 0' instead}} {{1-2=}} {{2-2=(}} {{4-4= == 0)}} + // Forgotten ! or ? var someInt = co.a // expected-error{{value of optional type 'C?' must be unwrapped to refer to member 'a' of wrapped base type 'C'}} // expected-note@-1{{chain the optional using '?' to access member 'a' only for non-'nil' base values}}{{17-17=?}} diff --git a/test/Constraints/if_expr.swift b/test/Constraints/if_expr.swift index d8aa0fd62a898..b2df46efba9d0 100644 --- a/test/Constraints/if_expr.swift +++ b/test/Constraints/if_expr.swift @@ -28,7 +28,7 @@ useDouble(c) useDouble(d) var z = true ? a : b // expected-error{{result values in '? :' expression have mismatching types 'Int' and 'Double'}} -var _ = a ? b : b // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} +var _ = a ? b : b // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} @@ -51,9 +51,9 @@ useD1(i) // expected-error{{cannot convert value of type 'B' to expected argumen useD2(i) // expected-error{{cannot convert value of type 'B' to expected argument type 'D2'}} var x = true ? 1 : 0 -var y = 22 ? 1 : 0 // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} +var y = 22 ? 1 : 0 // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} -_ = x ? x : x // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} +_ = x ? x : x // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} _ = true ? x : 1.2 // expected-error {{result values in '? :' expression have mismatching types 'Int' and 'Double'}} _ = (x: true) ? true : false // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}} diff --git a/test/Misc/misc_diagnostics.swift b/test/Misc/misc_diagnostics.swift index 9dcd2e5b70051..c7d3e72f94738 100644 --- a/test/Misc/misc_diagnostics.swift +++ b/test/Misc/misc_diagnostics.swift @@ -22,8 +22,8 @@ let total = 15.0 let count = 7 let median = total / count // expected-error {{binary operator '/' cannot be applied to operands of type 'Double' and 'Int'}} expected-note {{overloads for '/' exist with these partially matching parameter lists:}} -if (1) {} // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} -if 1 {} // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} +if (1) {} // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} +if 1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} var a: [String] = [1] // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}} var b: Int = [1, 2, 3] // expected-error{{cannot convert value of type '[Int]' to specified type 'Int'}} @@ -34,7 +34,7 @@ var f2: Float = 3.0 var dd: Double = f1 - f2 // expected-error{{cannot convert value of type 'Float' to specified type 'Double'}} func f() -> Bool { - return 1 + 1 // expected-error{{cannot convert return expression of type 'Int' to return type 'Bool'}} + return 1 + 1 // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} } // Test that nested diagnostics are properly surfaced. diff --git a/test/NameBinding/import-resolution-overload.swift b/test/NameBinding/import-resolution-overload.swift index 3e466103e1ac7..9f5a89eb13247 100644 --- a/test/NameBinding/import-resolution-overload.swift +++ b/test/NameBinding/import-resolution-overload.swift @@ -32,7 +32,7 @@ ambiguousWithVar(true) // no-warning var localVar : Bool localVar = false -localVar = 42 // expected-error {{cannot assign value of type 'Int' to type 'Bool'}} +localVar = 42 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} localVar(42) // expected-error {{cannot call value of non-function type 'Bool'}} var _ : localVar // should still work diff --git a/test/Parse/switch.swift b/test/Parse/switch.swift index 92c4184172122..f0d601042890f 100644 --- a/test/Parse/switch.swift +++ b/test/Parse/switch.swift @@ -64,7 +64,7 @@ case _ where x % 2 == 0, x = 1 case var y where y % 2 == 0: x = y + 1 -case _ where 0: // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} +case _ where 0: // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} x = 0 default: x = 1 diff --git a/test/Sema/pound_assert.swift b/test/Sema/pound_assert.swift index 9f826e251ed1a..51c651e69566e 100644 --- a/test/Sema/pound_assert.swift +++ b/test/Sema/pound_assert.swift @@ -8,6 +8,6 @@ #assert(false, "error message") -#assert(123) // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} +#assert(123) // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} -#assert(123, "error message") // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}} +#assert(123, "error message") // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index e63fe024f7fa4..8ed310fc818e3 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -36,7 +36,7 @@ func basictest() { var x4 : Bool = true var x5 : Bool = - 4 // expected-error {{cannot convert value of type 'Int' to specified type 'Bool'}} + 4 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} //var x6 : Float = 4+5 @@ -295,7 +295,7 @@ func fib(_ n: Int) -> Int { // FIXME: Should warn about integer constants being too large var - il_a: Bool = 4 // expected-error {{cannot convert value of type 'Int' to specified type 'Bool'}} + il_a: Bool = 4 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} var il_b: Int8 = 123123 var il_c: Int8 = 4 // ok @@ -804,13 +804,13 @@ func testParenExprInTheWay() { let x = 42 if x & 4.0 {} // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} - // expected-error@-1 {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} if (x & 4.0) {} // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} - // expected-error@-1 {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} if !(x & 4.0) {} // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} - // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Bool'}} + // expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} - if x & x {} // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + if x & x {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} } // Mixed method/property overload groups can cause a crash during constraint optimization diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 8b79a3b24c47f..085a554189cec 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -59,7 +59,7 @@ func funcdecl5(_ a: Int, y: Int) { } // This diagnostic is terrible - rdar://12939553 - if x {} // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + if x {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} if true { if (B) { @@ -583,7 +583,7 @@ func fn(x: Int) { } func bad_if() { - if 1 {} // expected-error {{cannot convert value of type 'Int' to expected condition type 'Bool'}} + if 1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}} if (x: false) {} // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}} if (x: 1) {} // expected-error {{cannot convert value of type '(x: Int)' to expected condition type 'Bool'}} if nil {} // expected-error {{'nil' is not compatible with expected condition type 'Bool'}} From 36366bc3e456c3f91219286cb7dc238917468e01 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Feb 2020 22:02:21 -0800 Subject: [PATCH 133/237] [Constraint solver] One SolutionApplicationTarget instance to rule them all. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture the peculiarities of contextual types vs. types used to generate conversion constraints, as well as the behavior of “optional some” patterns as used by if let / while let, within SolutionApplicationTarget. This allows us to use a single target throughout setup / solving / application, rather than mapping between two similar-but-disjoint targets. --- lib/Sema/CSApply.cpp | 3 ++- lib/Sema/CSSolver.cpp | 10 +++------ lib/Sema/ConstraintSystem.cpp | 2 +- lib/Sema/ConstraintSystem.h | 6 ++++++ lib/Sema/TypeCheckConstraints.cpp | 36 ++++++++++--------------------- 5 files changed, 23 insertions(+), 34 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 5c08b584144e5..e25dd2f721a18 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7320,9 +7320,10 @@ Optional ConstraintSystem::applySolution( // We are supposed to use contextual type only if it is present and // this expression doesn't represent the implicit return of the single // expression function which got deduced to be `Never`. - Type convertType = target.getExprConversionType(); + Type convertType = target.getExprConversionTypeForConstraint(); auto shouldCoerceToContextualType = [&]() { return convertType && + !target.isOptionalSomePatternInit() && !(getType(resultExpr)->isUninhabited() && getContextualTypePurpose(target.getAsExpr()) == CTP_ReturnSingleExpr); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 9e8c3678a7f2f..e5c8864b56974 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1259,14 +1259,10 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, // If there is a type that we're expected to convert to, add the conversion // constraint. - if (Type convertType = target.getExprConversionType()) { + if (Type convertType = target.getExprConversionTypeForConstraint()) { // Determine whether we know more about the contextual type. - ContextualTypePurpose ctp = CTP_Unused; - bool isOpaqueReturnType = false; - if (auto contextualInfo = getContextualTypeInfo(origExpr)) { - ctp = contextualInfo->purpose; - isOpaqueReturnType = contextualInfo->isOpaqueReturnType; - } + ContextualTypePurpose ctp = target.getExprContextualTypePurpose(); + bool isOpaqueReturnType = target.infersOpaqueReturnType(); // Substitute type variables in for unresolved types. if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 5d36ff5bb3f36..b81236a8e163e 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4053,7 +4053,7 @@ bool SolutionApplicationTarget::contextualTypeIsOnlyAHint() const { assert(kind == Kind::expression); switch (expression.contextualPurpose) { case CTP_Initialization: - return !infersOpaqueReturnType(); + return !infersOpaqueReturnType() && !isOptionalSomePatternInit(); case CTP_ForEachStmt: return true; case CTP_Unused: diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 98bf8362620f3..a82d8d44138eb 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1241,6 +1241,12 @@ class SolutionApplicationTarget { return expression.convertType; } + Type getExprConversionTypeForConstraint() const { + if (contextualTypeIsOnlyAHint()) + return Type(); + return getExprConversionType(); + } + /// Returns the autoclosure parameter type, or \c nullptr if the /// expression has a different kind of context. FunctionType *getAsAutoclosureParamType() const { diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 71f05f2a06ae6..1f44f1a7c2fe5 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2107,6 +2107,7 @@ TypeChecker::typeCheckExpression( target.setExpr(expr); return None; } + target.setExpr(expr); // Construct a constraint system from this expression. ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes; @@ -2127,46 +2128,34 @@ TypeChecker::typeCheckExpression( // diagnostics and is a hint for various performance optimizations. // FIXME: Look through LoadExpr. This is an egregious hack due to the // way typeCheckExprIndependently works. - TypeLoc convertType = target.getExprConversionTypeLoc(); Expr *contextualTypeExpr = expr; if (auto loadExpr = dyn_cast_or_null(contextualTypeExpr)) contextualTypeExpr = loadExpr->getSubExpr(); cs.setContextualType( - contextualTypeExpr, convertType, + contextualTypeExpr, + target.getExprConversionTypeLoc(), target.getExprContextualTypePurpose(), target.infersOpaqueReturnType()); - // If the convertType is *only* provided for that hint, then null it out so - // that we don't later treat it as an actual conversion constraint. - if (target.contextualTypeIsOnlyAHint()) - convertType = TypeLoc(); - // If the client can handle unresolved type variables, leave them in the // system. auto allowFreeTypeVariables = FreeTypeVariableBinding::Disallow; if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables)) allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType; - Type convertTo = convertType.getType(); - + // If the target requires an optional of some type, form a new appropriate + // type variable and update the target's type with an optional of that + // type variable. if (target.isOptionalSomePatternInit()) { - assert(!convertTo && "convertType and type check options conflict"); + assert(!target.getExprConversionType() && "convertType and type check options conflict"); auto *convertTypeLocator = cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); Type var = cs.createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape); - convertTo = getOptionalType(expr->getLoc(), var); - } else if (target.getExprContextualTypePurpose() - == CTP_AutoclosureDefaultParameter) { - // FIXME: Hack around the convertTo adjustment below, which we want to - // eliminate. - convertTo = Type(target.getAsAutoclosureParamType()); + target.setExprConversionType(getOptionalType(expr->getLoc(), var)); } // Attempt to solve the constraint system. - SolutionApplicationTarget innerTarget( - expr, dc, target.getExprContextualTypePurpose(), convertTo, - target.isDiscardedExpr()); - auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables); + auto viable = cs.solve(target, listener, allowFreeTypeVariables); if (!viable) { target.setExpr(expr); return None; @@ -2177,7 +2166,8 @@ TypeChecker::typeCheckExpression( // because they will leak out into arbitrary places in the resultant AST. if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && (viable->size() != 1 || - (convertType.getType() && convertType.getType()->hasUnresolvedType()))) { + (target.getExprConversionTypeForConstraint() && + target.getExprConversionTypeForConstraint()->hasUnresolvedType()))) { // FIXME: This hack should only be needed for CSDiag. unresolvedTypeExprs = true; return target; @@ -2191,10 +2181,6 @@ TypeChecker::typeCheckExpression( // Apply the solution to the expression. bool performingDiagnostics = options.contains(TypeCheckExprFlags::SubExpressionDiagnostics); - // FIXME: HACK! Copy over the inner target's expression info. - target.setExpr(innerTarget.getAsExpr()); - if (convertTo.isNull()) - target.setExprConversionType(convertTo); auto resultTarget = cs.applySolution(solution, target, performingDiagnostics); if (!resultTarget) { // Failure already diagnosed, above, as part of applying the solution. From 95697143344ed70b5384e44904254e77015e9cab Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Feb 2020 22:11:03 -0800 Subject: [PATCH 134/237] Clarify method names in SolutionApplicationTarget. --- lib/Sema/CSApply.cpp | 2 +- lib/Sema/CSSolver.cpp | 2 +- lib/Sema/ConstraintSystem.h | 12 +++++++----- lib/Sema/TypeCheckConstraints.cpp | 9 +++++---- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index e25dd2f721a18..bbe25a5303ada 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7320,7 +7320,7 @@ Optional ConstraintSystem::applySolution( // We are supposed to use contextual type only if it is present and // this expression doesn't represent the implicit return of the single // expression function which got deduced to be `Never`. - Type convertType = target.getExprConversionTypeForConstraint(); + Type convertType = target.getExprConversionType(); auto shouldCoerceToContextualType = [&]() { return convertType && !target.isOptionalSomePatternInit() && diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index e5c8864b56974..72bbcb2592256 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1259,7 +1259,7 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, // If there is a type that we're expected to convert to, add the conversion // constraint. - if (Type convertType = target.getExprConversionTypeForConstraint()) { + if (Type convertType = target.getExprConversionType()) { // Determine whether we know more about the contextual type. ContextualTypePurpose ctp = target.getExprContextualTypePurpose(); bool isOpaqueReturnType = target.infersOpaqueReturnType(); diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index a82d8d44138eb..6919c2a46e512 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1224,11 +1224,11 @@ class SolutionApplicationTarget { return expression.contextualPurpose; } - Type getExprConversionType() const { - return getExprConversionTypeLoc().getType(); + Type getExprContextualType() const { + return getExprContextualTypeLoc().getType(); } - TypeLoc getExprConversionTypeLoc() const { + TypeLoc getExprContextualTypeLoc() const { assert(kind == Kind::expression); // For an @autoclosure parameter, the conversion type is @@ -1241,10 +1241,12 @@ class SolutionApplicationTarget { return expression.convertType; } - Type getExprConversionTypeForConstraint() const { + /// Retrieve the type to which an expression should be converted, or + /// a NULL type if no conversion constraint should be generated. + Type getExprConversionType() const { if (contextualTypeIsOnlyAHint()) return Type(); - return getExprConversionType(); + return getExprContextualType(); } /// Returns the autoclosure parameter type, or \c nullptr if the diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 1f44f1a7c2fe5..017bf4dd8dba5 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2133,7 +2133,7 @@ TypeChecker::typeCheckExpression( contextualTypeExpr = loadExpr->getSubExpr(); cs.setContextualType( contextualTypeExpr, - target.getExprConversionTypeLoc(), + target.getExprContextualTypeLoc(), target.getExprContextualTypePurpose(), target.infersOpaqueReturnType()); @@ -2147,7 +2147,8 @@ TypeChecker::typeCheckExpression( // type variable and update the target's type with an optional of that // type variable. if (target.isOptionalSomePatternInit()) { - assert(!target.getExprConversionType() && "convertType and type check options conflict"); + assert(!target.getExprContextualType() && + "some pattern cannot have contextual type pre-configured"); auto *convertTypeLocator = cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); Type var = cs.createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape); @@ -2166,8 +2167,8 @@ TypeChecker::typeCheckExpression( // because they will leak out into arbitrary places in the resultant AST. if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) && (viable->size() != 1 || - (target.getExprConversionTypeForConstraint() && - target.getExprConversionTypeForConstraint()->hasUnresolvedType()))) { + (target.getExprConversionType() && + target.getExprConversionType()->hasUnresolvedType()))) { // FIXME: This hack should only be needed for CSDiag. unresolvedTypeExprs = true; return target; From bbc0a95723d294014078ea11b3a7b83fe074f869 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Feb 2020 22:38:24 -0800 Subject: [PATCH 135/237] [Constraint system] Simplify creation of contextual conversion constraint. We were storing more state than necessary in the constraint system. --- lib/Sema/CSGen.cpp | 3 +- lib/Sema/CSSimplify.cpp | 78 +++++++++++++++---------------- lib/Sema/CSSolver.cpp | 10 ++-- lib/Sema/ConstraintSystem.h | 9 ++-- lib/Sema/TypeCheckConstraints.cpp | 3 +- 5 files changed, 48 insertions(+), 55 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 2cb35341bde68..071767fe08891 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3831,8 +3831,7 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition, case StmtConditionElement::CK_Boolean: { Expr *condExpr = condElement.getBoolean(); - setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition, - /*isOpaqueReturnType=*/false); + setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition); condExpr = generateConstraints(condExpr, dc); if (!condExpr) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index e017aeb2cee63..bdb9de33c83e0 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9095,57 +9095,57 @@ void ConstraintSystem::addConstraint(ConstraintKind kind, Type first, } void ConstraintSystem::addContextualConversionConstraint( - Expr *expr, ContextualTypeInfo contextualType) { - Type convertType = contextualType.getType(); - if (convertType.isNull()) + Expr *expr, Type conversionType, ContextualTypePurpose purpose, + bool isOpaqueReturnType) { + if (conversionType.isNull()) return; // Determine the type of the constraint. auto constraintKind = ConstraintKind::Conversion; - switch (contextualType.purpose) { - case CTP_ReturnStmt: - case CTP_ReturnSingleExpr: - case CTP_Initialization: - if (contextualType.isOpaqueReturnType) - constraintKind = ConstraintKind::OpaqueUnderlyingType; - break; + switch (purpose) { + case CTP_ReturnStmt: + case CTP_ReturnSingleExpr: + case CTP_Initialization: + if (isOpaqueReturnType) + constraintKind = ConstraintKind::OpaqueUnderlyingType; + break; - case CTP_CallArgument: - constraintKind = ConstraintKind::ArgumentConversion; - break; + case CTP_CallArgument: + constraintKind = ConstraintKind::ArgumentConversion; + break; - case CTP_YieldByReference: - // In a by-reference yield, we expect the contextual type to be an - // l-value type, so the result must be bound to that. - constraintKind = ConstraintKind::Bind; - break; + case CTP_YieldByReference: + // In a by-reference yield, we expect the contextual type to be an + // l-value type, so the result must be bound to that. + constraintKind = ConstraintKind::Bind; + break; - case CTP_ArrayElement: - case CTP_AssignSource: - case CTP_CalleeResult: - case CTP_CannotFail: - case CTP_Condition: - case CTP_Unused: - case CTP_YieldByValue: - case CTP_ThrowStmt: - case CTP_EnumCaseRawValue: - case CTP_DefaultParameter: - case CTP_AutoclosureDefaultParameter: - case CTP_ClosureResult: - case CTP_DictionaryKey: - case CTP_DictionaryValue: - case CTP_CoerceOperand: - case CTP_SubscriptAssignSource: - case CTP_ForEachStmt: - break; + case CTP_ArrayElement: + case CTP_AssignSource: + case CTP_CalleeResult: + case CTP_CannotFail: + case CTP_Condition: + case CTP_Unused: + case CTP_YieldByValue: + case CTP_ThrowStmt: + case CTP_EnumCaseRawValue: + case CTP_DefaultParameter: + case CTP_AutoclosureDefaultParameter: + case CTP_ClosureResult: + case CTP_DictionaryKey: + case CTP_DictionaryValue: + case CTP_CoerceOperand: + case CTP_SubscriptAssignSource: + case CTP_ForEachStmt: + break; } // Add the constraint. - bool isForSingleExprFunction = (contextualType.purpose == CTP_ReturnSingleExpr); + bool isForSingleExprFunction = (purpose == CTP_ReturnSingleExpr); auto *convertTypeLocator = getConstraintLocator( expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); - addConstraint(constraintKind, getType(expr), convertType, - convertTypeLocator, /*isFavored*/ true); + addConstraint(constraintKind, getType(expr), conversionType, + convertTypeLocator, /*isFavored*/ true); } Type ConstraintSystem::addJoinConstraint( diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 72bbcb2592256..2e509e4f6ba15 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -240,8 +240,7 @@ void ConstraintSystem::applySolution(const Solution &solution) { for (const auto &contextualType : solution.contextualTypes) { if (!getContextualTypeInfo(contextualType.first)) { setContextualType(contextualType.first, contextualType.second.typeLoc, - contextualType.second.purpose, - contextualType.second.isOpaqueReturnType); + contextualType.second.purpose); } } @@ -1243,8 +1242,6 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, Expr *expr = target.getAsExpr(); Timer.emplace(expr, *this); - Expr *origExpr = expr; - // Try to shrink the system by reducing disjunction domains. This // goes through every sub-expression and generate its own sub-system, to // try to reduce the domains of those subexpressions. @@ -1277,9 +1274,8 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, }); } - ContextualTypeInfo info{ - TypeLoc::withoutLoc(convertType), ctp, isOpaqueReturnType}; - addContextualConversionConstraint(expr, info); + addContextualConversionConstraint(expr, convertType, ctp, + isOpaqueReturnType); } // Notify the listener that we've built the constraint system. diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 6919c2a46e512..13af1e1bd582d 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -792,7 +792,6 @@ using OpenedTypeMap = struct ContextualTypeInfo { TypeLoc typeLoc; ContextualTypePurpose purpose; - bool isOpaqueReturnType = false; Type getType() const { return typeLoc.getType(); } }; @@ -2336,12 +2335,11 @@ class ConstraintSystem { } void setContextualType( - const Expr *expr, TypeLoc T, ContextualTypePurpose purpose, - bool isOpaqueReturnType) { + const Expr *expr, TypeLoc T, ContextualTypePurpose purpose) { assert(expr != nullptr && "Expected non-null expression!"); assert(contextualTypes.count(expr) == 0 && "Already set this contextual type"); - contextualTypes[expr] = { T, purpose, isOpaqueReturnType }; + contextualTypes[expr] = { T, purpose }; } Optional getContextualTypeInfo(const Expr *expr) const { @@ -2573,7 +2571,8 @@ class ConstraintSystem { /// Add the appropriate constraint for a contextual conversion. void addContextualConversionConstraint( - Expr *expr, ContextualTypeInfo contextualType); + Expr *expr, Type conversionType, ContextualTypePurpose purpose, + bool isOpaqueReturnType); /// Add a "join" constraint between a set of types, producing the common /// supertype. diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 017bf4dd8dba5..ba95206b01675 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2134,8 +2134,7 @@ TypeChecker::typeCheckExpression( cs.setContextualType( contextualTypeExpr, target.getExprContextualTypeLoc(), - target.getExprContextualTypePurpose(), - target.infersOpaqueReturnType()); + target.getExprContextualTypePurpose()); // If the client can handle unresolved type variables, leave them in the // system. From c8f990b80010e236ce4780967ba643db99766401 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 21 Jan 2020 18:08:01 -0500 Subject: [PATCH 136/237] ClangImporter: Don't force loading of all superclass members in loadNamedMembers() --- lib/ClangImporter/ClangImporter.cpp | 34 ++++++++++------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 3bc3c4b5d1e3d..036fa2bd58e31 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3730,27 +3730,6 @@ void ClangImporter::Implementation::lookupAllObjCMembers( } } -// Force the members of the entire inheritance hierarchy to be loaded and -// deserialized before loading the named member of this class. This allows the -// decl members table to be warmed up and enables the correct identification of -// overrides. -// -// FIXME: Very low hanging fruit: Loading everything is extremely wasteful. We -// should be able to just load the name lazy member loading is asking for. -static void ensureSuperclassMembersAreLoaded(const ClassDecl *CD) { - if (!CD) - return; - - CD = CD->getSuperclassDecl(); - if (!CD || !CD->hasClangNode()) - return; - - CD->loadAllMembers(); - - for (auto *ED : const_cast(CD)->getExtensions()) - ED->loadAllMembers(); -} - Optional> ClangImporter::Implementation::loadNamedMembers( const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) { @@ -3799,7 +3778,16 @@ ClangImporter::Implementation::loadNamedMembers( assert(isa(CD) || isa(CD)); - ensureSuperclassMembersAreLoaded(dyn_cast(D)); + // Force the members of the entire inheritance hierarchy to be loaded and + // deserialized before loading the named member of a class. This warms up + // ClangImporter::Implementation::MembersForNominal, used for computing + // property overrides. + // + // FIXME: If getOverriddenDecl() kicked off a request for imported decls, + // we could postpone this until overrides are actually requested. + if (auto *classDecl = dyn_cast(D)) + if (auto *superclassDecl = classDecl->getSuperclassDecl()) + (void) const_cast(superclassDecl)->lookupDirect(N); TinyPtrVector Members; for (auto entry : table->lookup(SerializedSwiftName(N), @@ -3829,7 +3817,7 @@ ClangImporter::Implementation::loadNamedMembers( auto member = entry.get(); if (!isVisibleClangEntry(member)) continue; - // Skip Decls from different clang::DeclContexts + // Skip Decls from different clang::DeclContexts if (member->getDeclContext() != CDC) continue; SmallVector tmp; From 95d9aa0f29b88d4fab609852e4e41e09417d6d1e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 22 Jan 2020 00:31:08 -0500 Subject: [PATCH 137/237] ClangImporter: Refactor importInheritedConstructors() The lambda here was completely unnecessary. --- lib/ClangImporter/ImportDecl.cpp | 180 +++++++++++++++---------------- 1 file changed, 88 insertions(+), 92 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 505eeb8e29c8c..8f97be45e735e 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -7175,112 +7175,108 @@ void SwiftDeclConverter::importNonOverriddenMirroredMethods(DeclContext *dc, void SwiftDeclConverter::importInheritedConstructors( const ClassDecl *classDecl, SmallVectorImpl &newMembers) { - if (!classDecl->hasSuperclass()) + auto superclassDecl = classDecl->getSuperclassDecl(); + if (!superclassDecl) + return; + + auto superclassClangDecl = superclassDecl->getClangDecl(); + if (!superclassClangDecl || + !isa(superclassClangDecl)) return; auto curObjCClass = cast(classDecl->getClangDecl()); - auto inheritConstructors = [&](TinyPtrVector members, - Optional kind) { - const auto &languageVersion = - Impl.SwiftContext.LangOpts.EffectiveLanguageVersion; + // The kind of initializer to import. If this class has designated + // initializers, everything it inherits is a convenience initializer. + Optional kind; + if (curObjCClass->hasDesignatedInitializers()) + kind = CtorInitializerKind::Convenience; - for (auto member : members) { - auto ctor = dyn_cast(member); - if (!ctor) - continue; + const auto &languageVersion = + Impl.SwiftContext.LangOpts.EffectiveLanguageVersion; - // Don't inherit compatibility stubs. - if (ctor->getAttrs().isUnavailableInSwiftVersion(languageVersion)) - continue; + auto members = superclassDecl->lookupDirect( + DeclBaseName::createConstructor()); - // Don't inherit (non-convenience) factory initializers. - // Note that convenience factories return instancetype and can be - // inherited. - switch (ctor->getInitKind()) { - case CtorInitializerKind::Factory: - continue; - case CtorInitializerKind::ConvenienceFactory: - case CtorInitializerKind::Convenience: - case CtorInitializerKind::Designated: - break; - } + for (auto member : members) { + auto ctor = dyn_cast(member); + if (!ctor) + continue; - auto objcMethod = - dyn_cast_or_null(ctor->getClangDecl()); - if (!objcMethod) - continue; + // Don't inherit compatibility stubs. + if (ctor->getAttrs().isUnavailableInSwiftVersion(languageVersion)) + continue; - auto &clangSourceMgr = Impl.getClangASTContext().getSourceManager(); - clang::PrettyStackTraceDecl trace(objcMethod, clang::SourceLocation(), - clangSourceMgr, - "importing (inherited)"); - - // If this initializer came from a factory method, inherit - // it as an initializer. - if (objcMethod->isClassMethod()) { - assert(ctor->getInitKind() == CtorInitializerKind::ConvenienceFactory); - - Optional correctSwiftName; - ImportedName importedName = - importFullName(objcMethod, correctSwiftName); - assert( - !correctSwiftName && - "Import inherited initializers never references correctSwiftName"); - importedName.setHasCustomName(); - bool redundant; - if (auto newCtor = - importConstructor(objcMethod, classDecl, - /*implicit=*/true, ctor->getInitKind(), - /*required=*/false, ctor->getObjCSelector(), - importedName, objcMethod->parameters(), - objcMethod->isVariadic(), redundant)) { - // If this is a compatibility stub, mark it as such. - if (correctSwiftName) - markAsVariant(newCtor, *correctSwiftName); - - Impl.importAttributes(objcMethod, newCtor, curObjCClass); - newMembers.push_back(newCtor); - } - continue; - } + // Don't inherit (non-convenience) factory initializers. + // Note that convenience factories return instancetype and can be + // inherited. + switch (ctor->getInitKind()) { + case CtorInitializerKind::Factory: + continue; + case CtorInitializerKind::ConvenienceFactory: + case CtorInitializerKind::Convenience: + case CtorInitializerKind::Designated: + break; + } - // Figure out what kind of constructor this will be. - CtorInitializerKind myKind; - bool isRequired = false; - if (ctor->isRequired()) { - // Required initializers are always considered designated. - isRequired = true; - myKind = CtorInitializerKind::Designated; - } else if (kind) { - myKind = *kind; - } else { - myKind = ctor->getInitKind(); - } + auto objcMethod = + dyn_cast_or_null(ctor->getClangDecl()); + if (!objcMethod) + continue; - // Import the constructor into this context. + auto &clangSourceMgr = Impl.getClangASTContext().getSourceManager(); + clang::PrettyStackTraceDecl trace(objcMethod, clang::SourceLocation(), + clangSourceMgr, + "importing (inherited)"); + + // If this initializer came from a factory method, inherit + // it as an initializer. + if (objcMethod->isClassMethod()) { + assert(ctor->getInitKind() == CtorInitializerKind::ConvenienceFactory); + + Optional correctSwiftName; + ImportedName importedName = + importFullName(objcMethod, correctSwiftName); + assert( + !correctSwiftName && + "Import inherited initializers never references correctSwiftName"); + importedName.setHasCustomName(); + bool redundant; if (auto newCtor = importConstructor(objcMethod, classDecl, - /*implicit=*/true, myKind, isRequired)) { + /*implicit=*/true, ctor->getInitKind(), + /*required=*/false, ctor->getObjCSelector(), + importedName, objcMethod->parameters(), + objcMethod->isVariadic(), redundant)) { + // If this is a compatibility stub, mark it as such. + if (correctSwiftName) + markAsVariant(newCtor, *correctSwiftName); + Impl.importAttributes(objcMethod, newCtor, curObjCClass); newMembers.push_back(newCtor); } + continue; } - }; - - // The kind of initializer to import. If this class has designated - // initializers, everything it inherits is a convenience initializer. - Optional kind; - if (curObjCClass->hasDesignatedInitializers()) - kind = CtorInitializerKind::Convenience; + // Figure out what kind of constructor this will be. + CtorInitializerKind myKind; + bool isRequired = false; + if (ctor->isRequired()) { + // Required initializers are always considered designated. + isRequired = true; + myKind = CtorInitializerKind::Designated; + } else if (kind) { + myKind = *kind; + } else { + myKind = ctor->getInitKind(); + } - // If we have a superclass, import from it. - auto superclass = classDecl->getSuperclassDecl(); - if (auto superclassClangDecl = superclass->getClangDecl()) { - if (isa(superclassClangDecl)) { - inheritConstructors(superclass->lookupDirect(DeclBaseName::createConstructor()), - kind); + // Import the constructor into this context. + if (auto newCtor = + importConstructor(objcMethod, classDecl, + /*implicit=*/true, myKind, isRequired)) { + Impl.importAttributes(objcMethod, newCtor, curObjCClass); + newMembers.push_back(newCtor); } } } @@ -8660,11 +8656,11 @@ void ClangImporter::Implementation::insertMembersAndAlternates( void ClangImporter::Implementation::importInheritedConstructors( const clang::ObjCInterfaceDecl *curObjCClass, const ClassDecl *classDecl, SmallVectorImpl &newMembers) { - if (curObjCClass->getName() != "Protocol") { - SwiftDeclConverter converter(*this, CurrentVersion); - converter.importInheritedConstructors(classDecl, newMembers); - } - } + if (curObjCClass->getName() != "Protocol") { + SwiftDeclConverter converter(*this, CurrentVersion); + converter.importInheritedConstructors(classDecl, newMembers); + } +} void ClangImporter::Implementation::collectMembersToAdd( const clang::ObjCContainerDecl *objcContainer, Decl *D, DeclContext *DC, From 7e7b6e8b2757cf7430fa1012fe39204a0a79e79a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 4 Feb 2020 00:27:35 -0500 Subject: [PATCH 138/237] ClangImporter: Fix up importSubscript() to not depend on import order Once lazy loading supports mirrored protocol members, we can end up calling importSubscript() for a setter method before we've mirrored the getter. In this case, we call findCounterpart() with the setter, which finds the imported getter from the ProtocolDecl itself. Stop processing the potential subscript in this case, since when we later mirror the protocol member, we'll go and build it again. This should be NFC without the next change I'm working on. --- lib/ClangImporter/ImportDecl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 8f97be45e735e..192835092486b 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -6585,6 +6585,13 @@ SwiftDeclConverter::importSubscript(Decl *decl, if (!counterpart) return nullptr; + // If we're looking at a class but the getter was found in a protocol, + // we're going to build the subscript later when we mirror the protocol + // member. Bail out here, otherwise we'll build it twice. + if (interface && + isa(counterpart->getDeclContext())) + return nullptr; + return cast_or_null( Impl.importDecl(counterpart, getActiveSwiftVersion())); }; From 6ba379f3b9c7a9e157896aa79dfe3437783c23d3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 4 Feb 2020 00:43:01 -0500 Subject: [PATCH 139/237] ClangImporter: Refactor loadObjCMethods() to not call CollectMultipleMethodsInGlobalPool() Let's try calling ObjCInterface::lookupMethod() instead. This also eliminates an order dependency between selector conflict checking and protocol member mirroring, which fixes a diagnostics regression once lazy loading is enabled for mirrored protocol members. --- lib/ClangImporter/ClangImporter.cpp | 33 +++++++------------- test/ClangImporter/objc_init_redundant.swift | 4 +-- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 036fa2bd58e31..cde13ed69d50c 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3031,33 +3031,25 @@ void ClangImporter::loadObjCMethods( // Collect the set of visible Objective-C methods with this selector. clang::Selector clangSelector = Impl.exportSelector(selector); - SmallVector objcMethods; - auto &sema = Impl.Instance->getSema(); - sema.CollectMultipleMethodsInGlobalPool(clangSelector, objcMethods, - isInstanceMethod, - /*CheckTheOther=*/false); - // Check whether this method is in the class we care about. - SmallVector foundMethods; - for (auto objcMethod : objcMethods) { - // Find the owner of this method and determine whether it is the class - // we're looking for. - if (objcMethod->getClassInterface() != objcClass) - continue; + AbstractFunctionDecl *method = nullptr; + auto *objcMethod = objcClass->lookupMethod( + clangSelector, isInstanceMethod, + /*shallowCategoryLookup=*/false, + /*followSuper=*/false); + if (objcMethod) { // If we found a property accessor, import the property. if (objcMethod->isPropertyAccessor()) (void)Impl.importDecl(objcMethod->findPropertyDecl(true), Impl.CurrentVersion); - if (auto method = dyn_cast_or_null( - Impl.importDecl(objcMethod, Impl.CurrentVersion))) { - foundMethods.push_back(method); - } + method = dyn_cast_or_null( + Impl.importDecl(objcMethod, Impl.CurrentVersion)); } // If we didn't find anything, we're done. - if (foundMethods.empty()) + if (method == nullptr) return; // If we did find something, it might be a duplicate of something we found @@ -3066,10 +3058,9 @@ void ClangImporter::loadObjCMethods( // FIXME: We shouldn't need to do this. llvm::SmallPtrSet known; known.insert(methods.begin(), methods.end()); - for (auto method : foundMethods) { - if (known.insert(method).second) - methods.push_back(method); - } + + if (known.insert(method).second) + methods.push_back(method); } void diff --git a/test/ClangImporter/objc_init_redundant.swift b/test/ClangImporter/objc_init_redundant.swift index 3afcd8cc291f1..eba9bbad0ab51 100644 --- a/test/ClangImporter/objc_init_redundant.swift +++ b/test/ClangImporter/objc_init_redundant.swift @@ -10,12 +10,12 @@ import Foundation extension NSObject { @objc convenience init() { self.init() } // expected-error{{initializer 'init()' with Objective-C selector 'init' conflicts with previous declaration with the same Objective-C selector}} // CHECK: objc_init_redundant.swift:[[@LINE-1]]:21: error: initializer 'init()' with Objective-C selector 'init' conflicts -// CHECK: ObjectiveC.NSObject{{.*}}note: 'init' previously declared here +// CHECK: ObjectiveC.NSObject:{{.*}}note: 'init' previously declared here } extension NSObject { @objc(class) func foo() { } // expected-error{{method 'foo()' with Objective-C selector 'class' conflicts with method 'class()' with the same Objective-C selector}} // CHECK: objc_init_redundant.swift:[[@LINE-1]]:21: error: method 'foo()' with Objective-C selector 'class' conflicts -// CHECK: ObjectiveC.NSObject{{.*}}note: method 'class()' declared here +// CHECK: ObjectiveC.NSObject:{{.*}}note: method 'class()' declared here } From 8280b20d81e46bb76dd27206a152a2f2c8d66e72 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 4 Feb 2020 00:51:10 -0500 Subject: [PATCH 140/237] Sema: Trigger lazy loading when recording potential selector conflicts --- lib/Sema/TypeCheckDeclObjC.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 54fe278987e52..d363dfb1d72ac 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -1619,6 +1619,9 @@ void markAsObjC(ValueDecl *D, ObjCReason reason, // Record the method in the class, if it's a member of one. if (auto classDecl = D->getDeclContext()->getSelfClassDecl()) { + // Trigger lazy loading of any imported members with the same selector. + (void) classDecl->lookupDirect(selector, !method->isStatic()); + classDecl->recordObjCMethod(method, selector); } From d76a8ce9201791de57bf683caaccc58ff078d1e1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 9 Jan 2020 22:10:10 -0500 Subject: [PATCH 141/237] ClangImporter: Lazily load mirrored protocol members in classes --- lib/ClangImporter/ClangImporter.cpp | 24 ++++---- lib/ClangImporter/ImportDecl.cpp | 59 ++++++++++++++----- lib/ClangImporter/ImporterImpl.h | 5 +- test/ClangImporter/objc_init_redundant.swift | 2 +- .../NamedLazyMembers.apinotes | 11 ++++ .../NamedLazyMembers/NamedLazyMembers.h | 18 ++++++ ...d_lazy_member_loading_objc_interface.swift | 2 + ...zy_member_loading_protocol_mirroring.swift | 20 ++++++- 8 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.apinotes diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index cde13ed69d50c..09575f46b201e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3726,7 +3726,7 @@ ClangImporter::Implementation::loadNamedMembers( const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) { auto *D = IDC->getDecl(); - auto *DC = cast(D); + auto *DC = D->getInnermostDeclContext(); auto *CD = D->getClangDecl(); auto *CDC = cast(CD); assert(CD && "loadNamedMembers on a Decl without a clangDecl"); @@ -3734,18 +3734,6 @@ ClangImporter::Implementation::loadNamedMembers( auto *nominal = DC->getSelfNominalTypeDecl(); auto effectiveClangContext = getEffectiveClangContext(nominal); - // FIXME: The legacy of mirroring protocol members rears its ugly head, - // and as a result we have to bail on any @interface or @category that - // has a declared protocol conformance. - if (auto *ID = dyn_cast(CD)) { - if (ID->protocol_begin() != ID->protocol_end()) - return None; - } - if (auto *CCD = dyn_cast(CD)) { - if (CCD->protocol_begin() != CCD->protocol_end()) - return None; - } - // There are 3 cases: // // - The decl is from a bridging header, CMO is Some(nullptr) @@ -3832,6 +3820,16 @@ ClangImporter::Implementation::loadNamedMembers( Members.push_back(cast(ctor)); } } + + if (!isa(D)) { + if (auto *OCD = dyn_cast(CD)) { + SmallVector newMembers; + importMirroredProtocolMembers(OCD, DC, N, newMembers); + for (auto member : newMembers) + Members.push_back(cast(member)); + } + } + return Members; } diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 192835092486b..239b8ba88310d 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4522,7 +4522,8 @@ namespace { /// methods become class methods on NSObject). void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl, DeclContext *dc, - SmallVectorImpl &members); + Optional name, + SmallVectorImpl &newMembers); void importNonOverriddenMirroredMethods(DeclContext *dc, MutableArrayRef entries, @@ -6936,9 +6937,16 @@ Optional SwiftDeclConverter::importObjCGenericParams( genericParams, Impl.importSourceLoc(typeParamList->getRAngleLoc())); } +void ClangImporter::Implementation::importMirroredProtocolMembers( + const clang::ObjCContainerDecl *decl, DeclContext *dc, + Optional name, SmallVectorImpl &members) { + SwiftDeclConverter converter(*this, CurrentVersion); + converter.importMirroredProtocolMembers(decl, dc, name, members); +} + void SwiftDeclConverter::importMirroredProtocolMembers( const clang::ObjCContainerDecl *decl, DeclContext *dc, - SmallVectorImpl &members) { + Optional name, SmallVectorImpl &members) { assert(dc); const clang::ObjCInterfaceDecl *interfaceDecl = nullptr; const ClangModuleUnit *declModule; @@ -6978,16 +6986,16 @@ void SwiftDeclConverter::importMirroredProtocolMembers( const auto &languageVersion = Impl.SwiftContext.LangOpts.EffectiveLanguageVersion; - for (auto member : proto->getMembers()) { + auto importProtocolRequirement = [&](Decl *member) { // Skip compatibility stubs; there's no reason to mirror them. if (member->getAttrs().isUnavailableInSwiftVersion(languageVersion)) - continue; + return; if (auto prop = dyn_cast(member)) { auto objcProp = dyn_cast_or_null(prop->getClangDecl()); if (!objcProp) - continue; + return; // We can't import a property if there's already a method with this // name. (This also covers other properties with that same name.) @@ -6995,7 +7003,7 @@ void SwiftDeclConverter::importMirroredProtocolMembers( // not already there. clang::Selector sel = objcProp->getGetterName(); if (interfaceDecl->getInstanceMethod(sel)) - continue; + return; bool inNearbyCategory = std::any_of(interfaceDecl->visible_categories_begin(), @@ -7012,7 +7020,7 @@ void SwiftDeclConverter::importMirroredProtocolMembers( return category->getInstanceMethod(sel); }); if (inNearbyCategory) - continue; + return; if (auto imported = Impl.importMirroredDecl(objcProp, dc, getVersion(), proto)) { @@ -7021,24 +7029,38 @@ void SwiftDeclConverter::importMirroredProtocolMembers( // metatype. } - continue; + return; } auto afd = dyn_cast(member); if (!afd) - continue; + return; if (isa(afd)) - continue; + return; auto objcMethod = dyn_cast_or_null(member->getClangDecl()); if (!objcMethod) - continue; + return; // For now, just remember that we saw this method. methodsByName[objcMethod->getSelector()] .push_back(MirroredMethodEntry{objcMethod, proto}); + }; + + if (name) { + // If we're asked to import a specific name only, look for that in the + // protocol. + auto results = proto->lookupDirect(*name); + for (auto *member : results) + if (member->getDeclContext() == proto) + importProtocolRequirement(member); + + } else { + // Otherwise, import all mirrored members. + for (auto *member : proto->getMembers()) + importProtocolRequirement(member); } } @@ -8680,7 +8702,12 @@ void ClangImporter::Implementation::collectMembersToAdd( insertMembersAndAlternates(nd, members); } - SwiftDeclConverter converter(*this, CurrentVersion); + // Objective-C protocols don't require any special handling. + if (isa(objcContainer)) + return; + + // Objective-C interfaces can inherit constructors from their superclass, + // which we must model explicitly. if (auto clangClass = dyn_cast(objcContainer)) { objcContainer = clangClass = clangClass->getDefinition(); importInheritedConstructors(clangClass, cast(D), members); @@ -8688,10 +8715,12 @@ void ClangImporter::Implementation::collectMembersToAdd( = dyn_cast(objcContainer)) { objcContainer = clangProto->getDefinition(); } - // Import mirrored declarations for protocols to which this category - // or extension conforms. + + // Interfaces and categories can declare protocol conformances, and + // members of those protocols are mirrored into the interface or + // category. // FIXME: This is supposed to be a short-term hack. - converter.importMirroredProtocolMembers(objcContainer, DC, members); + importMirroredProtocolMembers(objcContainer, DC, None, members); } void ClangImporter::Implementation::loadAllConformances( diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 6c851d5468d8d..517fde6fce0bc 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -818,7 +818,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation void importInheritedConstructors(const clang::ObjCInterfaceDecl *curObjCClass, const ClassDecl *classDecl, SmallVectorImpl &newMembers); - + void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl, + DeclContext *dc, Optional name, + SmallVectorImpl &members); + /// Utility function for building simple generic signatures. GenericSignature buildGenericSignature(GenericParamList *genericParams, DeclContext *dc); diff --git a/test/ClangImporter/objc_init_redundant.swift b/test/ClangImporter/objc_init_redundant.swift index eba9bbad0ab51..d6366a467ee4a 100644 --- a/test/ClangImporter/objc_init_redundant.swift +++ b/test/ClangImporter/objc_init_redundant.swift @@ -16,6 +16,6 @@ extension NSObject { extension NSObject { @objc(class) func foo() { } // expected-error{{method 'foo()' with Objective-C selector 'class' conflicts with method 'class()' with the same Objective-C selector}} // CHECK: objc_init_redundant.swift:[[@LINE-1]]:21: error: method 'foo()' with Objective-C selector 'class' conflicts -// CHECK: ObjectiveC.NSObject:{{.*}}note: method 'class()' declared here +// CHECK: ObjectiveC.NSObjectProtocol:{{.*}}note: method 'class()' declared here } diff --git a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.apinotes b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.apinotes new file mode 100644 index 0000000000000..0db3efa3fe8f4 --- /dev/null +++ b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.apinotes @@ -0,0 +1,11 @@ +--- +Name: NamedLazyMembers +Classes: +- Name: PrivateDoer + Methods: + - Selector: "objectForKey:" + MethodKind: Instance + SwiftPrivate: true + - Selector: "count" + MethodKind: Instance + SwiftPrivate: true diff --git a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h index 86d93cc55e0d0..09aebbd57be52 100644 --- a/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h +++ b/test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h @@ -39,6 +39,10 @@ - (void)simplyDoSomeWorkWithSpeed:(int)s alacrity:(int)a NS_SWIFT_NAME(simplyDoSomeWorkWithSpeed(speed:levelOfAlacrity:)); +// Make sure that swift_private correctly adds the '__' prefix. +- (void)count __attribute__((swift_private)); +- (void)objectForKey:(NSObject *)key __attribute__((swift_private)); + // These we are generally trying to not-import, via laziness. - (void)simplyGoForWalk; - (void)simplyTakeNap; @@ -72,6 +76,7 @@ - (void)categoricallyReadBook; - (void)categoricallyAttendLecture; - (void)categoricallyWriteLetter; + @end @@ -120,3 +125,16 @@ - (void)exuberantlyAttendLecture; - (void)exuberantlyWriteLetter; @end + +@protocol PrivateMethods +- (void)count; +- (void)objectForKey:(NSObject *)key; +@end + +@interface PrivateDoer +- (void)count; +- (void)objectForKey:(NSObject *)key; +@end + +@interface PrivateDoer(Category) +@end diff --git a/test/NameBinding/named_lazy_member_loading_objc_interface.swift b/test/NameBinding/named_lazy_member_loading_objc_interface.swift index d4201213bc328..ce9c245daf273 100644 --- a/test/NameBinding/named_lazy_member_loading_objc_interface.swift +++ b/test/NameBinding/named_lazy_member_loading_objc_interface.swift @@ -23,6 +23,8 @@ public func foo() { let _ = d.simplyDoSomeWork(withSpeed:10) let _ = d.simplyDoVeryImportantWork(speed:10, thoroughness:12) let _ = d.simplyDoSomeWorkWithSpeed(speed:10, levelOfAlacrity:12) + let _ = d.__count + let _ = d.__object(forKey: nil) } // Make sure that simply subclassing an imported subclass doesn't page in all diff --git a/test/NameBinding/named_lazy_member_loading_protocol_mirroring.swift b/test/NameBinding/named_lazy_member_loading_protocol_mirroring.swift index c9933cb56304c..63c11e641e91b 100644 --- a/test/NameBinding/named_lazy_member_loading_protocol_mirroring.swift +++ b/test/NameBinding/named_lazy_member_loading_protocol_mirroring.swift @@ -3,11 +3,12 @@ // RUN: rm -rf %t && mkdir -p %t/stats-pre && mkdir -p %t/stats-post // // Prime module cache -// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -typecheck %s +// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -typecheck %s -swift-version 5 // // Check that named-lazy-member-loading reduces the number of Decls deserialized -// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s -// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s +// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s -swift-version 5 +// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -swift-version 5 +// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post import NamedLazyMembers @@ -24,3 +25,16 @@ public func foo(d: DerivedFromMirroringDoer) { let _ = d.mirroredBaseInstanceMethod() let _ = d.mirroredDerivedInstanceMethod() } + +extension PrivateDoer { + public var count: Int { return 0 } + public func object(forKey: NSObject?) {} +} + +public func foo(d: PrivateDoer) { + _ = d.count + _ = d.object + + let _ = d.__count + let _ = d.__object(forKey: nil) +} \ No newline at end of file From e112581f766ad49944b5ed10d4faa1b1a4d9724a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Tue, 4 Feb 2020 08:28:50 -0800 Subject: [PATCH 142/237] [run-test] Fix --target and --param parsing. For argparse, the type value of each argument must be a callable. The previous type was simply pointing to the class that implements the callable, and not to an instance of it. This should recover the previous functionality for the --target and the --param arguments. Seems that this is already applied in the build-script, so no changes are necessary there. --- utils/run-test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/run-test b/utils/run-test index b06b74bbcc110..db5ea62ef2906 100755 --- a/utils/run-test +++ b/utils/run-test @@ -129,7 +129,7 @@ def main(): help="build test dependencies before running tests " "(default: true)") parser.add_argument("--target", - type=argparse.types.ShellSplitType, + type=argparse.types.ShellSplitType(), action=argparse.actions.AppendAction, dest="targets", help="stdlib deployment targets to test. Accept " @@ -141,7 +141,7 @@ def main(): choices=TEST_SUBSETS, default='primary', help="test subset (default: primary)") parser.add_argument("--param", - type=argparse.types.ShellSplitType, + type=argparse.types.ShellSplitType(), action=argparse.actions.AppendAction, default=[], help="key=value parameters they are directly passed " From e631c66e0dc55261fb41f993534a2594d7129ca8 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Mon, 3 Feb 2020 18:23:27 -0800 Subject: [PATCH 143/237] [stdlib/private][os log] Annotate tiny helper functions in the new os log overlay @_transparent so that they will be inlined in their callers even annotated as @_optimize(none). Make the OSLogPrototypeCompileTest.swift test suite check only the output of the OSLogOptimization pass instead of the output of the Onone pipeline. This will make the tes more resilient to adding mandatory optimizations later in the pass pipeline. Also, remove a duplicate test from the OSLogPrototypeExecTest suite. --- stdlib/private/OSLog/OSLogIntegerTypes.swift | 20 +- stdlib/private/OSLog/OSLogMessage.swift | 7 +- stdlib/private/OSLog/OSLogStringTypes.swift | 8 +- .../OSLogPrototypeCompileTest.swift | 198 ++++++++++++------ test/stdlib/OSLogPrototypeExecTest.swift | 24 --- 5 files changed, 154 insertions(+), 103 deletions(-) diff --git a/stdlib/private/OSLog/OSLogIntegerTypes.swift b/stdlib/private/OSLog/OSLogIntegerTypes.swift index 673a22ce7ac0f..0ba0e31fc6cdf 100644 --- a/stdlib/private/OSLog/OSLogIntegerTypes.swift +++ b/stdlib/private/OSLog/OSLogIntegerTypes.swift @@ -58,6 +58,17 @@ extension OSLogInterpolation { appendInteger(number, format: format, privacy: privacy) } + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public mutating func appendInterpolation( + _ number: @autoclosure @escaping () -> UInt, + format: IntFormat = .decimal, + privacy: Privacy = .public + ) { + appendInteger(number, format: format, privacy: privacy) + } + /// Given an integer, create and append a format specifier for the integer to the /// format string property. Also, append the integer along with necessary headers /// to the OSLogArguments property. @@ -154,10 +165,11 @@ extension OSLogArguments { } /// Return the number of bytes needed for serializing an integer argument as -/// specified by os_log. This function must be constant evaluable. -@_semantics("constant_evaluable") -@inlinable -@_optimize(none) +/// specified by os_log. This function must be constant evaluable. Note that +/// it is marked transparent instead of @inline(__always) as it is used in +/// optimize(none) functions. +@_transparent +@usableFromInline internal func sizeForEncoding( _ type: T.Type ) -> Int where T : FixedWidthInteger { diff --git a/stdlib/private/OSLog/OSLogMessage.swift b/stdlib/private/OSLog/OSLogMessage.swift index 7d27b797700ba..1fa5352808a10 100644 --- a/stdlib/private/OSLog/OSLogMessage.swift +++ b/stdlib/private/OSLog/OSLogMessage.swift @@ -48,9 +48,10 @@ public enum Privacy { @_optimize(none) public var maxOSLogArgumentCount: UInt8 { return 48 } -@_semantics("constant_evaluable") -@inlinable -@_optimize(none) +/// Note that this is marked transparent instead of @inline(__always) as it is +/// used in optimize(none) functions. +@_transparent +@usableFromInline internal var logBitsPerByte: Int { return 3 } /// Represents a string interpolation passed to the log APIs. diff --git a/stdlib/private/OSLog/OSLogStringTypes.swift b/stdlib/private/OSLog/OSLogStringTypes.swift index 00226082cb843..6a616ea221204 100644 --- a/stdlib/private/OSLog/OSLogStringTypes.swift +++ b/stdlib/private/OSLog/OSLogStringTypes.swift @@ -101,10 +101,10 @@ extension OSLogArguments { /// bitWidth property, and since MemoryLayout is not supported by the constant /// evaluator, this function returns the byte size of Int, which must equal the /// word length of the target architecture and hence the pointer size. -/// This function must be constant evaluable. -@_semantics("constant_evaluable") -@inlinable -@_optimize(none) +/// This function must be constant evaluable. Note that it is marked transparent +/// instead of @inline(__always) as it is used in optimize(none) functions. +@_transparent +@usableFromInline internal func pointerSizeInBytes() -> Int { return Int.bitWidth &>> logBitsPerByte } diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index f51c732e97500..3901cb43b589a 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -swift-version 5 -emit-sil -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize -// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -swift-version 5 -emit-sil -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// RUN: %target-swift-frontend -swift-version 5 -emit-sil -primary-file %s -Xllvm -sil-print-after=OSLogOptimization -o /dev/null 2>&1 | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// RUN: %target-swift-frontend -enable-ownership-stripping-after-serialization -swift-version 5 -emit-sil -primary-file %s -Xllvm -sil-print-after=OSLogOptimization -o /dev/null 2>&1 | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize // REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos // Tests for the OSLogOptimization pass that performs compile-time analysis @@ -12,7 +12,7 @@ import Foundation if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest23testSimpleInterpolationL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testSimpleInterpolationL_ func testSimpleInterpolation(h: Logger) { h.log(level: .debug, "Minimum integer value: \(Int.min)") @@ -20,9 +20,13 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG is used here as it is easier to perform the checks backwards // from uses to the definitions. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // We need to wade through some borrows and copy values here. + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Minimum integer value: %{public}lld" // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Minimum integer value: %{public}d" @@ -51,22 +55,29 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // We need to wade through some borrows and copy values here. + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest34testInterpolationWithFormatOptionsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testInterpolationWithFormatOptionsL_ func testInterpolationWithFormatOptions(h: Logger) { h.log(level: .info, "Maximum integer value: \(Int.max, format: .hex)") // Check if there is a call to _os_log_impl with a literal format string. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Maximum integer value: %{public}llx" // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Maximum integer value: %{public}x" @@ -95,14 +106,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest44testInterpolationWithFormatOptionsAndPrivacyL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testInterpolationWithFormatOptionsAndPrivacyL_ func testInterpolationWithFormatOptionsAndPrivacy(h: Logger) { let privateID = 0x79abcdef h.log( @@ -111,9 +125,12 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // Check if there is a call to _os_log_impl with a literal format string. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Private Identifier: %{private}llx" // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Private Identifier: %{private}x" @@ -142,14 +159,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 3 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest38testInterpolationWithMultipleArgumentsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testInterpolationWithMultipleArgumentsL_ func testInterpolationWithMultipleArguments(h: Logger) { let privateID = 0x79abcdef let filePermissions = 0o777 @@ -164,9 +184,12 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // Check if there is a call to _os_log_impl with a literal format string. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Access prevented: process %{public}lld initiated by user: %{private}lld attempted resetting permissions to %{public}llo" // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Access prevented: process %{public}d initiated by user: %{private}d attempted resetting permissions to %{public}o" @@ -195,14 +218,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 9 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest25testLogMessageWithoutDataL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testLogMessageWithoutDataL_ func testLogMessageWithoutData(h: Logger) { // FIXME: here `ExpressibleByStringLiteral` conformance of OSLogMessage // is used. In this case, the constant evaluation begins from the apply of @@ -214,9 +240,12 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // Check if there is a call to _os_log_impl with a literal format string. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "A message with no data" // Check if the size of the argument buffer is a constant. @@ -242,44 +271,56 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 0 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testEscapingOfPercentsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testEscapingOfPercentsL_ func testEscapingOfPercents(h: Logger) { h.log("Process failed after 99% completion") - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "Process failed after 99%% completion" } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest18testDoublePercentsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testDoublePercentsL_ func testDoublePercents(h: Logger) { h.log("Double percents: %%") - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "Double percents: %%%%" } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testSmallFormatStringsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testSmallFormatStringsL_ func testSmallFormatStrings(h: Logger) { h.log("a") - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "a" } /// A stress test that checks whether the optimizer handle messages with more /// than 48 interpolated expressions. Interpolated expressions beyond this /// limit must be ignored. - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest31testMessageWithTooManyArgumentsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testMessageWithTooManyArgumentsL_ func testMessageWithTooManyArguments(h: Logger) { h.log( level: .error, @@ -292,9 +333,12 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // Check if there is a call to _os_log_impl with a literal format string. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-64-DAG: [[LIT]] = string_literal utf8 "%{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld " // CHECK-32-DAG: [[LIT]] = string_literal utf8 "%{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d " @@ -322,22 +366,28 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 144 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest22testInt32InterpolationL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testInt32InterpolationL_ func testInt32Interpolation(h: Logger) { h.log("32-bit integer value: \(Int32.min)") // Check if there is a call to _os_log_impl with a literal format string. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "32-bit integer value: %{public}d" // Check if the size of the argument buffer is a constant. @@ -361,7 +411,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 1 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest26testDynamicStringArgumentsL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testDynamicStringArgumentsL_ func testDynamicStringArguments(h: Logger) { let concatString = "hello" + " - " + "world" let interpolatedString = "\(31) trillion digits of pi are known so far" @@ -375,9 +425,12 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG is used here as it is easier to perform the checks backwards // from uses to the definitions. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "concat: %{public}s interpolated: %{private}s" // Check if the size of the argument buffer is a constant. @@ -405,14 +458,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest25testNSObjectInterpolationL_1hy0aB06LoggerV_tF + // CHECK-LABEL: @${{.*}}testNSObjectInterpolationL_ func testNSObjectInterpolation(h: Logger) { let nsArray: NSArray = [0, 1, 2] let nsDictionary: NSDictionary = [1 : ""] @@ -424,9 +480,12 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG is used here as it is easier to perform the checks backwards // from uses to the definitions. - // CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c) - // CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}}) - // CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer ([[LIT:%[0-9]+]] : $Builtin.RawPointer) + // CHECK-DAG: builtin "globalStringTablePointer"([[STRING:%[0-9]+]] : $String) + // CHECK-DAG: [[STRING]] = begin_borrow [[STRING2:%[0-9]+]] + // CHECK-DAG: [[STRING2]] = copy_value [[STRING3:%[0-9]+]] + // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] + // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], + // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC // CHECK-DAG: [[LIT]] = string_literal utf8 "NSArray: %{public}@ NSDictionary: %{private}@" // Check if the size of the argument buffer is a constant. @@ -454,14 +513,17 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF // CHECK-DAG: try_apply [[FOREACH]], inout Array) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]]) - // CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]] - // CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer, inout Array) -> ()>, Builtin.RawPointer), 0 + // CHECK-DAG: store_borrow [[ARGSARRAY2:%[0-9]+]] to [[ARGSARRAYADDR]] + // CHECK-DAG: [[ARGSARRAY2]] = begin_borrow [[ARGSARRAY3:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY3]] = copy_value [[ARGSARRAY4:%[0-9]+]] + // CHECK-DAG: [[ARGSARRAY4]] = begin_borrow [[ARGSARRAY:%[0-9]+]] + // CHECK-DAG: ([[ARGSARRAY]], {{%.*}}) = destructure_tuple [[ARRAYINITRES:%[0-9]+]] // CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer, inout Array) -> ()>([[ARRAYSIZE:%[0-9]+]]) // CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF // CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6 } - // CHECK-LABEL: @$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF + // CHECK-LABEL: @${{.*}}testDeadCodeEliminationL_ func testDeadCodeElimination( h: Logger, number: Int, @@ -488,7 +550,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { h.log("\(concatString)") // CHECK-NOT: OSLogMessage // CHECK-NOT: OSLogInterpolation - // CHECK-LABEL: end sil function '$s25OSLogPrototypeCompileTest23testDeadCodeEliminationL_1h6number8num32bit6stringy0aB06LoggerV_Sis5Int32VSStF' + // CHECK-LABEL: end sil function '${{.*}}testDeadCodeEliminationL_ } } diff --git a/test/stdlib/OSLogPrototypeExecTest.swift b/test/stdlib/OSLogPrototypeExecTest.swift index 2bebd07ee5533..0e3e0c04fa5e5 100644 --- a/test/stdlib/OSLogPrototypeExecTest.swift +++ b/test/stdlib/OSLogPrototypeExecTest.swift @@ -405,30 +405,6 @@ InterpolationTestSuite.test("integer with privacy and formatting") { }) } -InterpolationTestSuite.test("integer with privacy and formatting") { - let addr = 0x7afebabe - _checkFormatStringAndBuffer( - "Access to invalid address: \(addr, format: .hex, privacy: .private)", - with: { (formatString, buffer) in - expectEqual( - "Access to invalid address: %{private}\(intPrefix)x", - formatString) - - let bufferChecker = OSLogBufferChecker(buffer) - bufferChecker.checkSummaryBytes( - argumentCount: 1, - hasPrivate: true, - hasNonScalar: false) - - bufferChecker.checkArguments({ - bufferChecker.checkInt( - startIndex: $0, - flag: .privateFlag, - expectedInt: addr) - }) - }) -} - InterpolationTestSuite.test("test multiple arguments") { let filePerms = 0o777 let pid = 122225 From 2f36375a28ced5837ee407fa86723787bf188dce Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 24 Jan 2020 17:00:25 -0800 Subject: [PATCH 144/237] [metadata prespecialization] Standardize checks to ref tables. Previously, some ad hoc checks were done in order to determine whether the metadata access for a generic type was trivial. Now, standard predicates are used, specifically IRGenModule's member functions isDependentConformance and isResilientConformance. --- lib/IRGen/MetadataRequest.cpp | 58 ++++---- test/IRGen/dynamic_self_metadata.swift | 2 +- test/IRGen/dynamic_self_metadata_future.swift | 78 +++++++++++ ...dule-1argument-1distinct_generic_use.swift | 127 +++++++++++++++++- 4 files changed, 234 insertions(+), 31 deletions(-) create mode 100644 test/IRGen/dynamic_self_metadata_future.swift diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index c9270a2ee1b67..5dcb3cdd05d02 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -701,36 +701,40 @@ bool irgen::isNominalGenericContextTypeMetadataAccessTrivial( auto substitutions = type->getContextSubstitutionMap(IGM.getSwiftModule(), &nominal); - return llvm::all_of(environment->getGenericParams(), [&](auto parameter) { - auto conformances = - environment->getGenericSignature()->getConformsTo(parameter); - auto witnessTablesAreReferenceable = - llvm::all_of(conformances, [&](ProtocolDecl *conformance) { - return conformance->getModuleContext() == IGM.getSwiftModule() && - !conformance->isResilient(IGM.getSwiftModule(), - ResilienceExpansion::Minimal); - }); + auto allWitnessTablesAreReferenceable = llvm::all_of(environment->getGenericParams(), [&](auto parameter) { + auto signature = environment->getGenericSignature(); + auto protocols = signature->getConformsTo(parameter); auto argument = ((Type *)parameter)->subst(substitutions); - auto genericArgument = argument->getAnyGeneric(); - // For now, to avoid statically specializing generic protocol witness - // tables, don't statically specialize metadata for types any of whose - // arguments are generic. - // - // TODO: This is more pessimistic than necessary. Specialize even in - // the face of generic arguments so long as those arguments - // aren't required to conform to any protocols. - // + auto canonicalType = argument->getCanonicalType(); + auto witnessTablesAreReferenceable = [&]() { + return llvm::all_of(protocols, [&](ProtocolDecl *protocol) { + auto conformance = + signature->lookupConformance(canonicalType, protocol); + if (!conformance.isConcrete()) { + return false; + } + auto rootConformance = conformance.getConcrete()->getRootConformance(); + return !IGM.isDependentConformance(rootConformance) && + !IGM.isResilientConformance(rootConformance); + }); + }; // TODO: Once witness tables are statically specialized, check whether the // ConformanceInfo returns nullptr from tryGetConstantTable. - // early return. - auto isGeneric = genericArgument && genericArgument->isGenericContext(); - auto isNominal = argument->getNominalOrBoundGenericNominal(); - auto isExistential = argument->isExistentialType(); - return isNominal && !isGeneric && !isExistential && - witnessTablesAreReferenceable && - irgen::isTypeMetadataAccessTrivial(IGM, - argument->getCanonicalType()); - }) && IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal); + auto isGenericWithoutPrespecializedConformance = [&]() { + auto genericArgument = argument->getAnyGeneric(); + return genericArgument && genericArgument->isGenericContext() && + (protocols.size() > 0); + }; + auto isExistential = [&]() { return argument->isExistentialType(); }; + auto metadataAccessIsTrivial = [&]() { + return irgen::isTypeMetadataAccessTrivial(IGM, + argument->getCanonicalType()); + }; + return !isGenericWithoutPrespecializedConformance() && !isExistential() && + metadataAccessIsTrivial() && witnessTablesAreReferenceable(); + }); + return allWitnessTablesAreReferenceable + && IGM.getTypeInfoForUnlowered(type).isFixedSize(ResilienceExpansion::Maximal); } /// Is it basically trivial to access the given metadata? If so, we don't diff --git a/test/IRGen/dynamic_self_metadata.swift b/test/IRGen/dynamic_self_metadata.swift index d4e228cf3814c..9e068aaad0e18 100644 --- a/test/IRGen/dynamic_self_metadata.swift +++ b/test/IRGen/dynamic_self_metadata.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend %s -emit-ir -parse-as-library | %FileCheck %s +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization %s -emit-ir -parse-as-library | %FileCheck %s // REQUIRES: CPU=x86_64 diff --git a/test/IRGen/dynamic_self_metadata_future.swift b/test/IRGen/dynamic_self_metadata_future.swift new file mode 100644 index 0000000000000..1fd737f852a43 --- /dev/null +++ b/test/IRGen/dynamic_self_metadata_future.swift @@ -0,0 +1,78 @@ +// RUN: %target-swift-frontend %s -target %module-target-future -emit-ir -parse-as-library | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + + +// REQUIRES: CPU=x86_64 + +// FIXME: Not a SIL test because we can't parse dynamic Self in SIL. +// + +// CHECK: [[TYPE:%.+]] = type <{ [8 x i8] }> + +@inline(never) func id(_ t: T) -> T { + return t +} +// CHECK-LABEL: define hidden swiftcc void @"$s28dynamic_self_metadata_future2idyxxlF" + +protocol P { + associatedtype T +} + +extension P { + func f() {} +} + +struct G : P { + var t: T +} + +class C { + class func fromMetatype() -> Self? { return nil } + // CHECK-LABEL: define hidden swiftcc i64 @"$s28dynamic_self_metadata_future1CC12fromMetatypeACXDSgyFZ"(%swift.type* swiftself) + // CHECK: ret i64 0 + + func fromInstance() -> Self? { return nil } + // CHECK-LABEL: define hidden swiftcc i64 @"$s28dynamic_self_metadata_future1CC12fromInstanceACXDSgyF"(%T28dynamic_self_metadata_future1CC* swiftself) + // CHECK: ret i64 0 + + func dynamicSelfArgument() -> Self? { + return id(nil) + } + // CHECK-LABEL: define hidden swiftcc i64 @"$s28dynamic_self_metadata_future1CC0A12SelfArgumentACXDSgyF"(%T28dynamic_self_metadata_future1CC* swiftself) + // CHECK: [[GEP1:%.+]] = getelementptr {{.*}} %0 + // CHECK: [[TYPE1:%.+]] = load {{.*}} [[GEP1]] + // CHECK: [[T0:%.+]] = call swiftcc %swift.metadata_response @"$sSqMa"(i64 0, %swift.type* [[TYPE1]]) + // CHECK: [[TYPE2:%.+]] = extractvalue %swift.metadata_response [[T0]], 0 + // CHECK: call swiftcc void @"$s28dynamic_self_metadata_future2idyxxlF"({{.*}}, %swift.type* [[TYPE2]]) + + func dynamicSelfConformingType() -> Self? { + _ = G(t: self).f() + return nil + } + // CHECK-LABEL: define hidden swiftcc i64 @"$s28dynamic_self_metadata_future1CC0A18SelfConformingTypeACXDSgyF"(%T28dynamic_self_metadata_future1CC* swiftself) + // CHECK: [[SELF_GEP:%.+]] = getelementptr {{.*}} %0 + // CHECK: [[SELF_TYPE:%.+]] = load {{.*}} [[SELF_GEP]] + // CHECK: call i8** @swift_getWitnessTable( + // CHECK-SAME: %swift.protocol_conformance_descriptor* bitcast ( + // CHECK-SAME: {{.*}} @"$s28dynamic_self_metadata_future1GVyxGAA1PAAMc" + // CHECK-SAME: to %swift.protocol_conformance_descriptor* + // CHECK-SAME: ), + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, + // CHECK-SAME: %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i32, + // CHECK-SAME: {{(\[4 x i8\])?}}, + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s28dynamic_self_metadata_future1GVyAA1CCXDGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ), + // CHECK-SAME: i8*** undef + // CHECK-SAME: ) +} diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift index dfd9a6c74cc9d..048a8db3dc457 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -22,8 +22,27 @@ func consume(_ t: T) { // TODO: Once prespecialization is done for generic arguments which are // themselves generic (Outer>, here), a direct reference to // the prespecialized metadata should be emitted here. -// CHECK: [[TYPE:%[0-9]+]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledName({ i32, i32 }* @"$s4main5OuterVyAA5InnerVySiGGMD") -// CHECK: call swiftcc void @"$s4main7consumeyyxlF"(%swift.opaque* noalias nocapture %{{[0-9]+}}, %swift.type* [[TYPE]]) +// CHECK: call swiftcc void @"$s4main5OuterV5firstACyxGx_tcfC"( +// CHECK-SAME: %T4main5OuterV* noalias nocapture sret %13, +// CHECK-SAME: %swift.opaque* noalias nocapture %14, +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\],)?}} +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s4main5InnerVySiGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) +// CHECK-SAME: ) // CHECK: } func doit() { consume( Outer(first: Inner(first: 13)) ) @@ -34,6 +53,108 @@ doit() // CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5OuterVMa"([[INT]], %swift.type*) #{{[0-9]+}} { // CHECK: entry: // CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* -// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[ERASED_TYPE]], i8* undef, i8* undef, %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* @"$s4main5OuterVMn" to %swift.type_descriptor*)) #{{[0-9]+}} +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast ( +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\],)?}} +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s4main5InnerVySiGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) +// CHECK-SAME: to i8* +// CHECK-SAME: ), +// CHECK-SAME: [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\],)?}} +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s4main5OuterVyAA5InnerVySiGGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: [[INT]] 0 +// CHECK-SAME: } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata( +// CHECK-SAME: [[INT]] %0, +// CHECK-SAME: i8* [[ERASED_TYPE]], +// CHECK-SAME: i8* undef, +// CHECK-SAME: i8* undef, +// CHECK-SAME: %swift.type_descriptor* bitcast ( +// CHECK-SAME: <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* +// CHECK-SAME: @"$s4main5OuterVMn" +// CHECK-SAME: to %swift.type_descriptor* +// CHECK-SAME: ) +// CHECK-SAME: ) #{{[0-9]+}} +// CHECK: ret %swift.metadata_response {{%[0-9]+}} +// CHECK: } + +// CHECK: ; Function Attrs: noinline nounwind readnone +// CHECK: define hidden swiftcc %swift.metadata_response @"$s4main5InnerVMa"([[INT]], %swift.type*) #{{[0-9]+}} { +// CHECK: entry: +// CHECK: [[ERASED_TYPE:%[0-9]+]] = bitcast %swift.type* %1 to i8* +// CHECK: br label %[[TYPE_COMPARISON_LABEL:[0-9]+]] +// CHECK: [[TYPE_COMPARISON_LABEL]]: +// CHECK: [[EQUAL_TYPE:%[0-9]+]] = icmp eq i8* bitcast (%swift.type* @"$sSiN" to i8*), [[ERASED_TYPE]] +// CHECK: [[EQUAL_TYPES:%[0-9]+]] = and i1 true, [[EQUAL_TYPE]] +// CHECK: br i1 [[EQUAL_TYPES]], label %[[EXIT_PRESPECIALIZED:[0-9]+]], label %[[EXIT_NORMAL:[0-9]+]] +// CHECK: [[EXIT_PRESPECIALIZED]]: +// CHECK: ret %swift.metadata_response { +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i32, +// CHECK-SAME: {{(\[4 x i8\],)?}} +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s4main5InnerVySiGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: [[INT]] 0 +// CHECK-SAME: } +// CHECK: [[EXIT_NORMAL]]: +// CHECK: {{%[0-9]+}} = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata( +// CHECK-SAME: [[INT]] %0, +// CHECK-SAME: i8* [[ERASED_TYPE]], +// CHECK-SAME: i8* undef, +// CHECK-SAME: i8* undef, +// CHECK-SAME: %swift.type_descriptor* bitcast ( +// CHECK-SAME: <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i16, i16, i8, i8, i8, i8 }>* +// CHECK-SAME: @"$s4main5InnerVMn" +// CHECK-SAME: to %swift.type_descriptor* +// CHECK-SAME: ) +// CHECK-SAME: ) #{{[0-9]+}} // CHECK: ret %swift.metadata_response {{%[0-9]+}} // CHECK: } From 319cc795d8fc6c479bb7f87692bbb73e328e8c09 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Jan 2020 17:05:38 -0800 Subject: [PATCH 145/237] Add a simple TaggedUnion defined in terms of ExternalUnion. NFC. --- include/swift/Basic/ExternalUnion.h | 7 +- include/swift/Basic/TaggedUnion.h | 259 ++++++++++++++++++++++++++++ unittests/Basic/CMakeLists.txt | 1 + unittests/Basic/TaggedUnionTest.cpp | 219 +++++++++++++++++++++++ 4 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 include/swift/Basic/TaggedUnion.h create mode 100644 unittests/Basic/TaggedUnionTest.cpp diff --git a/include/swift/Basic/ExternalUnion.h b/include/swift/Basic/ExternalUnion.h index 8a82c78fd6d6c..2207a361b5415 100644 --- a/include/swift/Basic/ExternalUnion.h +++ b/include/swift/Basic/ExternalUnion.h @@ -87,6 +87,11 @@ struct ExternalUnionMembers { static constexpr int maybeIndexOf() { return ExternalUnionImpl::indexOf::value; } + + template + static constexpr bool contains() { + return ExternalUnionImpl::indexOf::value != -1; + } }; /// An external union that uses the member-list index as the user-facing @@ -237,7 +242,7 @@ class BasicExternalUnion { Members::Info::copyAssignSame(unsigned(thisIndex), Storage, other.Storage); } else { - destruct(thisIndex, Storage); + destruct(thisIndex); copyConstruct(otherIndex, other); } } diff --git a/include/swift/Basic/TaggedUnion.h b/include/swift/Basic/TaggedUnion.h new file mode 100644 index 0000000000000..bd8f74b98833d --- /dev/null +++ b/include/swift/Basic/TaggedUnion.h @@ -0,0 +1,259 @@ +//===- TaggedUnion.h - A tagged union ---------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides a simple tagged union, like std::variant but without +// any effort to be exception-safe and therefore much simpler. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_TAGGEDUNION_H +#define SWIFT_BASIC_TAGGEDUNION_H + +#include "swift/Basic/ExternalUnion.h" + +namespace swift { + +namespace TaggedUnionImpl { +template +using simplify_member_type + = typename std::remove_const::type>::type; + +/// Given a type `T` deduced from a `T &&` parameter, can it be used to +/// construct a member of the union? If so, `simplify_member_type` +/// will give the formal member type. +template +inline constexpr bool is_member_constructible() { + return Members::template contains>(); +} + +struct Empty {}; +} + +template ()> +class TaggedUnionBase; + +/// The base partial specialization. All the other partial specializations +/// will end up inheriting from this. +template +class TaggedUnionBase { +protected: + using StorageType = SimpleExternalUnionBase; + using Kind = typename KindHelper::Kind; + StorageType Storage; + Kind TheKind; + + TaggedUnionBase(Kind theKind) : TheKind(theKind) {} + +public: + /// Construct the union with a value of the given type, which must + /// (ignoring references) be one of the declared members of the union. + template + TaggedUnionBase(T &&value, + typename std::enable_if< + TaggedUnionImpl::is_member_constructible(), + TaggedUnionImpl::Empty>::type = {}) { + using TargetType = TaggedUnionImpl::simplify_member_type; + TheKind = StorageType::template kindForMember(); + Storage.template emplace(TheKind, std::forward(value)); + } + + template + typename std::enable_if(), + TaggedUnionBase &>::type + operator=(T &&value) { + using TargetType = TaggedUnionImpl::simplify_member_type; + TheKind = StorageType::template kindForMember(); + Storage.template emplace(TheKind, std::forward(value)); + return *this; + } + + /// Replace the current value in the union with a value of the given + /// type, which must be one of the declared members of the union. + /// + /// The value will be constructed from the arguments with an argument + /// list (i.e. `new(ptr) T(...)`). If aggregate initialization is required, + /// use `emplaceAggregate`. + template + T &emplace(Args &&... args) { + Storage.destruct(TheKind); + TheKind = StorageType::template kindForMember(); + return Storage.template emplace(TheKind, std::forward(args)...); + } + + /// Replace the current value in the union with a value of the given + /// type, which must be one of the declared members of the union. + /// + /// The value will be constructed from the arguments with a braced + /// initializer list (i.e. `T{...}`) and therefore may use aggregate + /// initialization. + template + T &emplaceAggregate(Args &&... args) { + Storage.destruct(TheKind); + TheKind = StorageType::template kindForMember(); + return Storage.template emplaceAggregate(TheKind, + std::forward(args)...); + } + + /// Return true if the union is storing a value of the given type, + /// which must be one of the declared members of the union. + template + bool isa() const { + return TheKind == StorageType::template kindForMember(); + } + + /// Return a pointer to the value if the union is storing a value of the + /// given type, which must be one of the declared members of the union. + template + T *dyn_cast() { + return Storage.template dyn_cast(TheKind); + } + + /// Return a pointer to the value if the union is storing a value of the + /// given type, which must be one of the declared members of the union. + template + const T *dyn_cast() const { + return Storage.template dyn_cast(TheKind); + } + + /// Assert that the union is storing a value of the given type and return + /// a reference to it. + template + T &get() { + return Storage.template get(TheKind); + } + + /// Assert that the union is storing a value of the given type and return + /// a reference to it. + template + const T &get() const { + return Storage.template get(TheKind); + } +}; + +/// The partial specialization for when the union isn't trivially-copyable. +template +class TaggedUnionBase + : public TaggedUnionBase { + using super = TaggedUnionBase; + +protected: + using super::Storage; + using super::TheKind; + + TaggedUnionBase(typename super::Kind kind) : super(kind) {} + +public: + template + TaggedUnionBase(T &&value, + typename std::enable_if< + TaggedUnionImpl::is_member_constructible(), + TaggedUnionImpl::Empty>::type = {}) + : super(std::forward(value)) {} + + TaggedUnionBase(const TaggedUnionBase &other) : super(other.TheKind) { + Storage.copyConstruct(other.TheKind, other.Storage); + } + + TaggedUnionBase(TaggedUnionBase &&other) : super(other.TheKind) { + Storage.moveConstruct(other.TheKind, std::move(other.Storage)); + } + + TaggedUnionBase &operator=(const TaggedUnionBase &other) { + Storage.copyAssign(TheKind, other.TheKind, other.Storage); + TheKind = other.TheKind; + return *this; + } + + TaggedUnionBase &operator=(TaggedUnionBase &&other) { + Storage.moveAssign(TheKind, other.TheKind, std::move(other.Storage)); + TheKind = other.TheKind; + return *this; + } + + ~TaggedUnionBase() { + Storage.destruct(TheKind); + } +}; + +/// The partial specialization for when `void` is a member of the union. +template +class TaggedUnionBase + : public TaggedUnionBase { + using super = TaggedUnionBase; + +protected: + using super::Storage; + using super::TheKind; + + static constexpr typename super::Kind kindForVoid() { + return super::StorageType::template kindForMember(); + } + +public: + template + TaggedUnionBase(T &&value, + typename std::enable_if< + TaggedUnionImpl::is_member_constructible(), + TaggedUnionImpl::Empty>::type = {}) + : super(std::forward(value)) {} + + /// Construct the union in the empty state. + TaggedUnionBase() : super(kindForVoid()) {} + + /// Test whether the union is in the empty state. + bool empty() const { + return TheKind == kindForVoid(); + } + + /// Reset the union to the empty state. + void reset() { + Storage.destruct(TheKind); + TheKind = kindForVoid(); + } +}; + +/// A tagged union of the given types. +/// +/// Non-trivial members are supported; they will make the union +/// non-trivial to copy, move, and destruct. +/// +/// The union provides the following members, as described in the +/// documentation for the primary partial specialization of +/// TaggedUnionBase. In the following, `M` must be a declared member +/// type of the union. +/// +/// ``` +/// TaggedUnion(M); +/// M &emplace(T...); +/// M &emplaceAggregate(T...); +/// bool isa() const; +/// M *dyn_cast(); +/// const M *dyn_cast() const; +/// M &get(); +/// const M &get() const; +/// ``` +/// +/// Additionally, if `void` is one of the types, the union supports an +/// empty state and provides a default constructor as well as `empty()` +/// and `reset()` methods. +template +using TaggedUnion = + TaggedUnionBase, + ExternalUnionMembers>; + +} + +#endif diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index 488dc579d239c..9d200420683f3 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -26,6 +26,7 @@ add_swift_unittest(SwiftBasicTests STLExtrasTest.cpp StringExtrasTest.cpp SuccessorMapTest.cpp + TaggedUnionTest.cpp ThreadSafeRefCntPointerTest.cpp TransformRangeTest.cpp TreeScopedHashTableTest.cpp diff --git a/unittests/Basic/TaggedUnionTest.cpp b/unittests/Basic/TaggedUnionTest.cpp new file mode 100644 index 0000000000000..6ef5ddd22e40b --- /dev/null +++ b/unittests/Basic/TaggedUnionTest.cpp @@ -0,0 +1,219 @@ +//===--- TaggedUnionTest.cpp ----------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "swift/Basic/TaggedUnion.h" +#include "gtest/gtest.h" +#include +#include + +using namespace swift; + +TEST(TaggedUnion, basics_trivial) { + using UnionType = TaggedUnion; + UnionType u1 = 21; + EXPECT_FALSE(u1.isa()); + EXPECT_FALSE(u1.dyn_cast() != nullptr); + EXPECT_TRUE(u1.isa()); + EXPECT_TRUE(u1.dyn_cast() != nullptr); + EXPECT_EQ(21, u1.get()); + EXPECT_EQ(21, *u1.dyn_cast()); + + UnionType u2 = 14L; + EXPECT_FALSE(u2.isa()); + EXPECT_FALSE(u2.dyn_cast() != nullptr); + EXPECT_TRUE(u2.isa()); + EXPECT_TRUE(u2.dyn_cast() != nullptr); + EXPECT_EQ(14L, u2.get()); + EXPECT_EQ(14L, *u2.dyn_cast()); + + UnionType u3 = u1; + EXPECT_FALSE(u3.isa()); + EXPECT_FALSE(u3.dyn_cast() != nullptr); + EXPECT_TRUE(u3.isa()); + EXPECT_TRUE(u3.dyn_cast() != nullptr); + EXPECT_EQ(21, u3.get()); + EXPECT_EQ(21, *u3.dyn_cast()); + + UnionType u4 = u2; + EXPECT_FALSE(u4.isa()); + EXPECT_FALSE(u4.dyn_cast() != nullptr); + EXPECT_TRUE(u4.isa()); + EXPECT_TRUE(u4.dyn_cast() != nullptr); + EXPECT_EQ(14L, u4.get()); + EXPECT_EQ(14L, *u4.dyn_cast()); + + long twenty_nine_ell = 29L; + u4 = twenty_nine_ell; + EXPECT_FALSE(u4.isa()); + EXPECT_FALSE(u4.dyn_cast() != nullptr); + EXPECT_TRUE(u4.isa()); + EXPECT_TRUE(u4.dyn_cast() != nullptr); + EXPECT_EQ(29L, u4.get()); + EXPECT_EQ(29L, *u4.dyn_cast()); + + u4 = u1; + EXPECT_FALSE(u4.isa()); + EXPECT_FALSE(u4.dyn_cast() != nullptr); + EXPECT_TRUE(u4.isa()); + EXPECT_TRUE(u4.dyn_cast() != nullptr); + EXPECT_EQ(21, u4.get()); + EXPECT_EQ(21, *u4.dyn_cast()); + + u4 = 71L; + EXPECT_FALSE(u4.isa()); + EXPECT_FALSE(u4.dyn_cast() != nullptr); + EXPECT_TRUE(u4.isa()); + EXPECT_TRUE(u4.dyn_cast() != nullptr); + EXPECT_EQ(71L, u4.get()); + EXPECT_EQ(71L, *u4.dyn_cast()); +} + +TEST(TaggedUnion, void_basics) { + using UnionType = TaggedUnion; + + UnionType u1; + EXPECT_FALSE(u1.isa()); + EXPECT_FALSE(u1.isa()); + EXPECT_TRUE(u1.isa()); + EXPECT_TRUE(u1.empty()); + EXPECT_TRUE(u1.dyn_cast() == nullptr); + + u1.emplace(111); + EXPECT_FALSE(u1.isa()); + EXPECT_TRUE(u1.isa()); + EXPECT_FALSE(u1.isa()); + EXPECT_FALSE(u1.empty()); + EXPECT_EQ(111, u1.get()); + + u1.reset(); + EXPECT_FALSE(u1.isa()); + EXPECT_FALSE(u1.isa()); + EXPECT_TRUE(u1.isa()); + EXPECT_TRUE(u1.empty()); + EXPECT_TRUE(u1.dyn_cast() == nullptr); +} + +struct OptionalString { + const char *Value; + + OptionalString(const char *value = nullptr) : Value(value) {} + OptionalString(const OptionalString &other) : Value(other.Value) {} + OptionalString(OptionalString &&other) : Value(other.Value) { + other.Value = nullptr; + } + OptionalString &operator=(const OptionalString &other) { + Value = other.Value; + return *this; + } + OptionalString &operator=(OptionalString &&other) { + Value = other.Value; + other.Value = nullptr; + return *this; + } + ~OptionalString() {} + + bool empty() const { return Value == nullptr; } + explicit operator bool() const { return empty(); } + + friend bool operator==(OptionalString lhs, OptionalString rhs) { + return lhs.Value == rhs.Value || + (lhs && rhs && strcmp(lhs.Value, rhs.Value) == 0); + } + friend bool operator!=(OptionalString lhs, OptionalString rhs) { + return !(lhs == rhs); + } +}; + +TEST(TaggedUnion, nontrivial_basics) { + using Value = OptionalString; + using ValueVector = std::vector; + using UnionType = TaggedUnion; + + Value str = "hello"; + UnionType u1 = str; + EXPECT_FALSE(u1.isa()); + EXPECT_FALSE(u1.dyn_cast() != nullptr); + EXPECT_TRUE(u1.isa()); + EXPECT_TRUE(u1.dyn_cast() != nullptr); + EXPECT_EQ("hello", u1.get()); + EXPECT_EQ("hello", *u1.dyn_cast()); + + str = "world"; + EXPECT_EQ("hello", u1.get()); + EXPECT_EQ("hello", *u1.dyn_cast()); + + ValueVector vec; + vec.push_back("a"); + vec.push_back("b"); + vec.push_back("c"); + + UnionType u2 = vec; + EXPECT_FALSE(u2.isa()); + EXPECT_FALSE(u2.dyn_cast() != nullptr); + EXPECT_TRUE(u2.isa()); + EXPECT_TRUE(u2.dyn_cast() != nullptr); + EXPECT_EQ("b", u2.get()[1]); + + UnionType u3 = u2; + EXPECT_FALSE(u3.isa()); + EXPECT_FALSE(u3.dyn_cast() != nullptr); + EXPECT_TRUE(u3.isa()); + EXPECT_TRUE(u3.dyn_cast() != nullptr); + EXPECT_EQ("b", u3.get()[1]); + + u3 = u1; + EXPECT_FALSE(u3.isa()); + EXPECT_FALSE(u3.dyn_cast() != nullptr); + EXPECT_TRUE(u3.isa()); + EXPECT_TRUE(u3.dyn_cast() != nullptr); + EXPECT_EQ("hello", u3.get()); + EXPECT_EQ("hello", *u3.dyn_cast()); + + u3 = u2; + EXPECT_FALSE(u3.isa()); + EXPECT_FALSE(u3.dyn_cast() != nullptr); + EXPECT_TRUE(u3.isa()); + EXPECT_TRUE(u3.dyn_cast() != nullptr); + EXPECT_EQ("a", u3.get()[0]); + + u3 = std::move(u1); + EXPECT_FALSE(u3.isa()); + EXPECT_FALSE(u3.dyn_cast() != nullptr); + EXPECT_TRUE(u3.isa()); + EXPECT_TRUE(u3.dyn_cast() != nullptr); + EXPECT_EQ("hello", u3.get()); + EXPECT_EQ("hello", *u3.dyn_cast()); + + // These will still be true, but the value will have been moved out of. + EXPECT_FALSE(u1.isa()); + EXPECT_FALSE(u1.isa()); + EXPECT_TRUE(u1.isa()); + EXPECT_TRUE(u1.dyn_cast() != nullptr); + EXPECT_EQ(Value(), u1.get()); + EXPECT_EQ(Value(), *u1.dyn_cast()); + + UnionType u4 = std::move(u2); + EXPECT_FALSE(u4.isa()); + EXPECT_FALSE(u4.dyn_cast() != nullptr); + EXPECT_TRUE(u4.isa()); + EXPECT_TRUE(u4.dyn_cast() != nullptr); + EXPECT_EQ(size_t(3), u4.get().size()); + EXPECT_EQ("a", u4.get()[0]); + + // These will still be true, but the value will have been moved out of. + EXPECT_FALSE(u2.isa()); + EXPECT_FALSE(u2.dyn_cast() != nullptr); + EXPECT_TRUE(u2.isa()); + EXPECT_TRUE(u2.dyn_cast() != nullptr); + EXPECT_TRUE(u2.get().empty()); + EXPECT_TRUE(u2.dyn_cast()->empty()); +} From 543d649278c4c2746adc586229b078b65406d747 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Tue, 4 Feb 2020 20:19:37 +0000 Subject: [PATCH 146/237] [Diagnostics] Warn when the result of a Void-returning function is ignored (by assigning into '_') (#29576) --- include/swift/AST/DiagnosticsSema.def | 3 +++ lib/Sema/MiscDiagnostics.cpp | 22 ++++++++++++++++--- .../ClangImporter/MixedSource/submodule.swift | 4 ++-- test/ClangImporter/private_frameworks.swift | 4 ++-- test/Compatibility/special_func_name.swift | 8 +++---- test/Constraints/assignment.swift | 5 +++++ test/Constraints/dynamic_lookup.swift | 2 +- test/Constraints/optional.swift | 4 ++-- test/Constraints/override.swift | 4 ++-- test/Constraints/rdar39931475.swift | 2 +- test/Constraints/tuple_arguments.swift | 4 ++-- test/NameBinding/name_lookup.swift | 2 +- test/Sema/call_as_function_simple.swift | 8 +++---- ...n-only-import-inlinable-conformances.swift | 2 +- .../Recovery/typedefs-in-protocols.swift | 2 +- test/Serialization/Recovery/typedefs.swift | 2 +- test/attr/attr_dynamic_callable.swift | 2 +- test/decl/func/operator.swift | 8 +++---- test/decl/func/special_func_name.swift | 4 ++-- .../protocol/req/optional_visibility.swift | 2 +- test/expr/unary/keypath/keypath.swift | 2 +- test/stdlib/UnsafePointerDiagnostics.swift | 2 +- 22 files changed, 61 insertions(+), 37 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9ad935c96b32b..945e7c94fd301 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3193,6 +3193,9 @@ ERROR(object_literal_broken_proto,none, ERROR(discard_expr_outside_of_assignment,none, "'_' can only appear in a pattern or on the left side of an assignment", ()) +WARNING(discard_expr_void_result_redundant, none, + "using '_' to ignore the result of a Void-returning function " + "is redundant", ()) ERROR(collection_literal_heterogeneous,none, "heterogeneous collection literal could only be inferred to %0; add" " explicit type annotation if this is intentional", (Type)) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index de34690bbaf24..62b80b8ceab95 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -221,8 +221,24 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, } // If we have an assignment expression, scout ahead for acceptable _'s. - if (auto *AE = dyn_cast(E)) - markAcceptableDiscardExprs(AE->getDest()); + if (auto *AE = dyn_cast(E)) { + auto destExpr = AE->getDest(); + markAcceptableDiscardExprs(destExpr); + // If the user is assigning the result of a function that returns + // Void to _ then warn, because that is redundant. + if (auto DAE = dyn_cast(destExpr)) { + if (auto CE = dyn_cast(AE->getSrc())) { + if (CE->getCalledValue() && isa(CE->getCalledValue()) && + CE->getType()->isVoid()) { + Ctx.Diags + .diagnose(DAE->getLoc(), + diag::discard_expr_void_result_redundant) + .fixItRemoveChars(DAE->getStartLoc(), + AE->getSrc()->getStartLoc()); + } + } + } + } /// Diagnose a '_' that isn't on the immediate LHS of an assignment. if (auto *DAE = dyn_cast(E)) { @@ -409,7 +425,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, /// in simple pattern-like expressions, so we reject anything complex here. void markAcceptableDiscardExprs(Expr *E) { if (!E) return; - + if (auto *PE = dyn_cast(E)) return markAcceptableDiscardExprs(PE->getSubExpr()); if (auto *TE = dyn_cast(E)) { diff --git a/test/ClangImporter/MixedSource/submodule.swift b/test/ClangImporter/MixedSource/submodule.swift index a1b0ed390bb02..5f680db6769c8 100644 --- a/test/ClangImporter/MixedSource/submodule.swift +++ b/test/ClangImporter/MixedSource/submodule.swift @@ -4,6 +4,6 @@ @_exported import Mixed.Submodule func test() { - _ = topLevel() - _ = fromSubmodule() + topLevel() + fromSubmodule() } diff --git a/test/ClangImporter/private_frameworks.swift b/test/ClangImporter/private_frameworks.swift index 733ec81fe9fd4..d45072aa627d2 100644 --- a/test/ClangImporter/private_frameworks.swift +++ b/test/ClangImporter/private_frameworks.swift @@ -39,8 +39,8 @@ import SomeKit func testWidget(widget: SKWidget) { - _ = widget.someObjCMethod() - _ = widget.someObjCExtensionMethod() + widget.someObjCMethod() + widget.someObjCExtensionMethod() let ext = widget.extensionMethod() ext.foo() diff --git a/test/Compatibility/special_func_name.swift b/test/Compatibility/special_func_name.swift index 39735193145e0..57d3373567983 100644 --- a/test/Compatibility/special_func_name.swift +++ b/test/Compatibility/special_func_name.swift @@ -34,16 +34,16 @@ struct S3 { } _ = S11(0) // expected-error {{argument passed to call that takes no arguments}} -_ = S11.init(0) -_ = S11.`init`(0) +S11.init(0) +S11.`init`(0) _ = S12(0) _ = S12.init(0) _ = S12.`init`(0) // expected-error {{type 'S12' has no member 'init'}} _ = S21(0) // expected-error {{argument passed to call that takes no arguments}} -_ = S21.init(0) -_ = S21.`init`(0) +S21.init(0) +S21.`init`(0) _ = S22(0) _ = S22.init(0) diff --git a/test/Constraints/assignment.swift b/test/Constraints/assignment.swift index 8888050f4fd53..8657246ad214b 100644 --- a/test/Constraints/assignment.swift +++ b/test/Constraints/assignment.swift @@ -72,3 +72,8 @@ func f23798944() { } .sr_3506 = 0 // expected-error {{type 'Int' has no member 'sr_3506'}} + +// SR-1553 + +func returnsVoid() {} +_ = returnsVoid() // expected-warning {{using '_' to ignore the result of a Void-returning function is redundant}}{{1-5=}} diff --git a/test/Constraints/dynamic_lookup.swift b/test/Constraints/dynamic_lookup.swift index bf13cf25fe960..6297f1475b76d 100644 --- a/test/Constraints/dynamic_lookup.swift +++ b/test/Constraints/dynamic_lookup.swift @@ -224,7 +224,7 @@ let anyValue: Any = X() _ = anyValue.bar() // expected-error {{value of type 'Any' has no member 'bar'}} // expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}}{{5-5=(}}{{13-13= as AnyObject)}} _ = (anyValue as AnyObject).bar() -_ = (anyValue as! X).bar() +(anyValue as! X).bar() var anyDict: [String : Any] = Dictionary() anyDict["test"] = anyValue diff --git a/test/Constraints/optional.swift b/test/Constraints/optional.swift index ea9c8f7a2fa83..a48859f52d172 100644 --- a/test/Constraints/optional.swift +++ b/test/Constraints/optional.swift @@ -391,8 +391,8 @@ func sr8411() { _ = S(&foo) // Ok _ = S.init(&foo) // Ok - _ = S.foo(&foo) // Ok - _ = S.bar(&foo, 42) // Ok + S.foo(&foo) // Ok + S.bar(&foo, 42) // Ok } // SR-11104 - Slightly misleading diagnostics for contextual failures with multiple fixes diff --git a/test/Constraints/override.swift b/test/Constraints/override.swift index e40af11733fbf..2c73c8af49afa 100644 --- a/test/Constraints/override.swift +++ b/test/Constraints/override.swift @@ -9,8 +9,8 @@ class Sub: Base { } func removeOverrides(concrete: Sub, generic: SomeSub) { - _ = concrete.foo() - _ = generic.foo() + concrete.foo() + generic.foo() } class Base1 { diff --git a/test/Constraints/rdar39931475.swift b/test/Constraints/rdar39931475.swift index 5962726dfb081..4b218f1530221 100644 --- a/test/Constraints/rdar39931475.swift +++ b/test/Constraints/rdar39931475.swift @@ -5,5 +5,5 @@ protocol P { } func foo(_ bar: T) { - _ = bar.b { a in Double((a, a += 1).0) } + bar.b { a in Double((a, a += 1).0) } } diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index 0b1852ea221ed..42956bb54ab5d 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -1696,8 +1696,8 @@ class Mappable { let x = Mappable(()) // expected-note@-1 2{{'x' declared here}} -_ = x.map { (_: Void) in return () } -_ = x.map { (_: ()) in () } +x.map { (_: Void) in return () } +x.map { (_: ()) in () } // https://bugs.swift.org/browse/SR-9470 do { diff --git a/test/NameBinding/name_lookup.swift b/test/NameBinding/name_lookup.swift index 6100e519c7561..08e1af0e6046e 100644 --- a/test/NameBinding/name_lookup.swift +++ b/test/NameBinding/name_lookup.swift @@ -609,7 +609,7 @@ class ShadowingGenericParameter { func foo (t : T) {} } -_ = ShadowingGenericParameter().foo(t: "hi") +ShadowingGenericParameter().foo(t: "hi") // rdar://problem/51266778 struct PatternBindingWithTwoVars1 { var x = 3, y = x } diff --git a/test/Sema/call_as_function_simple.swift b/test/Sema/call_as_function_simple.swift index 55dc76b3b7318..153b838447b74 100644 --- a/test/Sema/call_as_function_simple.swift +++ b/test/Sema/call_as_function_simple.swift @@ -106,8 +106,8 @@ func testMutating(_ x: Mutating, _ y: inout Mutating) { _ = x() // expected-error @+1 {{cannot use mutating member on immutable value: 'x' is a 'let' constant}} _ = x.callAsFunction() - _ = y() - _ = y.callAsFunction() + y() + y.callAsFunction() } struct Inout { @@ -185,8 +185,8 @@ func testIUO(a: SimpleCallable!, b: MultipleArgsCallable!, c: Extended!, _ = d()?.callAsFunction()?() _ = e() _ = e(1, 2, 3) - _ = f() - _ = g(&inoutInt) + f() + g(&inoutInt) _ = try? h() _ = try? h { throw DummyError() } } diff --git a/test/Sema/implementation-only-import-inlinable-conformances.swift b/test/Sema/implementation-only-import-inlinable-conformances.swift index 8ebbdfc8e5fab..525018df05dbd 100644 --- a/test/Sema/implementation-only-import-inlinable-conformances.swift +++ b/test/Sema/implementation-only-import-inlinable-conformances.swift @@ -135,7 +135,7 @@ extension NormalProtoAssocHolder { } @inlinable func testMultipleConformances() { - _ = NormalProtoAssocHolder.testAnotherConformance(NormalClass.self) + NormalProtoAssocHolder.testAnotherConformance(NormalClass.self) // expected-error@-1 2 {{cannot use conformance of 'NormalStruct' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} // expected-error@-2 {{cannot use conformance of 'NormalClass' to 'NormalProto' here; 'BADLibrary' has been imported as implementation-only}} } diff --git a/test/Serialization/Recovery/typedefs-in-protocols.swift b/test/Serialization/Recovery/typedefs-in-protocols.swift index 88530aede26e0..10797a765ea1e 100644 --- a/test/Serialization/Recovery/typedefs-in-protocols.swift +++ b/test/Serialization/Recovery/typedefs-in-protocols.swift @@ -44,7 +44,7 @@ public func testGenericDispatch(user: T) { // CHECK-IR: [[METHOD:%.+]] = bitcast i8* [[RAW_METHOD]] to void (%swift.opaque*, %swift.type*, i8**)* // CHECK-IR-NOT: ret // CHECK-IR: call swiftcc void [[METHOD]]( - _ = user.lastMethod() + user.lastMethod() } // CHECK-IR: ret void #if VERIFY diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index 60d0eda010d56..977e4cfe01ca9 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -39,7 +39,7 @@ public func testVTableBuilding(user: User) { // changes, please check that offset is still correct. // CHECK-IR-NOT: ret // CHECK-IR: getelementptr inbounds void (%T3Lib4UserC*)*, void (%T3Lib4UserC*)** %{{[0-9]+}}, {{i64 28|i32 31}} - _ = user.lastMethod() + user.lastMethod() } // CHECK-IR: ret void #if VERIFY diff --git a/test/attr/attr_dynamic_callable.swift b/test/attr/attr_dynamic_callable.swift index 9116ecbad9df4..94da1fbcc41f3 100644 --- a/test/attr/attr_dynamic_callable.swift +++ b/test/attr/attr_dynamic_callable.swift @@ -451,7 +451,7 @@ struct A { } func test9239() { - _ = A()() // ok + A()() // ok } // SR-10313 diff --git a/test/decl/func/operator.swift b/test/decl/func/operator.swift index 80d9c6635456e..96ec52a6d51c2 100644 --- a/test/decl/func/operator.swift +++ b/test/decl/func/operator.swift @@ -391,10 +391,10 @@ func testPrefixOperatorOnTuple() { _ = (∫)foo // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} // expected-warning@-2 {{expression of type '(Int, Int)' is unused}} - _ = (∫)(foo) + (∫)(foo) _ = ∫(1, 2) _ = (∫)(1, 2) // expected-error {{operator function '∫' expects a single parameter of type '(Int, Int)'}} - _ = (∫)((1, 2)) + (∫)((1, 2)) } postfix operator § @@ -412,9 +412,9 @@ func testPostfixOperatorOnTuple(a: A, b: B) { // expected-error@-2 {{generic parameter 'T' could not be inferred}} // expected-error@-3 {{generic parameter 'U' could not be inferred}} // expected-warning@-4 {{expression of type '(A, (B, B), A)' is unused}} - _ = (§)(foo) + (§)(foo) _ = (a, (b, b), a)§ _ = (§)(a, (b, b), a) // expected-error {{operator function '§' expects a single parameter of type '(T, (U, U), T)'}} - _ = (§)((a, (b, b), a)) + (§)((a, (b, b), a)) _ = (a, ((), (b, (a, a), b)§), a)§ } diff --git a/test/decl/func/special_func_name.swift b/test/decl/func/special_func_name.swift index caf5be0848976..679211b329e32 100644 --- a/test/decl/func/special_func_name.swift +++ b/test/decl/func/special_func_name.swift @@ -35,7 +35,7 @@ struct S3 { _ = S11(0) // expected-error {{argument passed to call that takes no arguments}} _ = S11.init(0) // expected-error {{argument passed to call that takes no arguments}} -_ = S11.`init`(0) +S11.`init`(0) _ = S12(0) _ = S12.init(0) @@ -43,7 +43,7 @@ _ = S12.`init`(0) // expected-error {{type 'S12' has no member 'init'}} _ = S21(0) // expected-error {{argument passed to call that takes no arguments}} _ = S21.init(0) // expected-error {{argument passed to call that takes no arguments}} -_ = S21.`init`(0) +S21.`init`(0) _ = S22(0) _ = S22.init(0) diff --git a/test/decl/protocol/req/optional_visibility.swift b/test/decl/protocol/req/optional_visibility.swift index 13c5dca84f6a5..71309195e318a 100644 --- a/test/decl/protocol/req/optional_visibility.swift +++ b/test/decl/protocol/req/optional_visibility.swift @@ -11,5 +11,5 @@ class Conforms : Opt { } func g(x: Conforms) { - _ = x.f(callback: {}) + x.f(callback: {}) } diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index f6f94f1274d4e..fb8346bac874c 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -814,7 +814,7 @@ func test_keypath_with_method_refs() { // SR-10467 - Argument type 'KeyPath' does not conform to expected type 'Any' func test_keypath_in_any_context() { func foo(_: Any) {} - _ = foo(\String.count) // Ok + foo(\String.count) // Ok } protocol PWithTypeAlias { diff --git a/test/stdlib/UnsafePointerDiagnostics.swift b/test/stdlib/UnsafePointerDiagnostics.swift index ccaaf0279656a..7b654dcfdcc77 100644 --- a/test/stdlib/UnsafePointerDiagnostics.swift +++ b/test/stdlib/UnsafePointerDiagnostics.swift @@ -131,7 +131,7 @@ struct SR9800 { func foo(_: UnsafePointer) {} func ambiguityTest(buf: UnsafeMutablePointer) { - _ = foo(UnsafePointer(buf)) // this call should be unambiguoius + foo(UnsafePointer(buf)) // this call should be unambiguoius } } From 849bd62a2670e37252dfaf5827e288db183617b2 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 4 Feb 2020 12:53:27 -0800 Subject: [PATCH 147/237] [AutoDiff upstream] Add SIL differentiability witnesses. (#29623) SIL differentiability witnesses are a new top-level SIL construct mapping "original" SIL functions to derivative SIL functions. SIL differentiability witnesses have the following components: - "Original" `SILFunction`. - SIL linkage. - Differentiability parameter indices (`IndexSubset`). - Differentiability result indices (`IndexSubset`). - Derivative `GenericSignature` representing differentiability generic requirements (optional). - JVP derivative `SILFunction` (optional). - VJP derivative `SILFunction` (optional). - "Is serialized?" bit. This patch adds the `SILDifferentiabilityWitness` data structure, with documentation, parsing, and printing. Resolves TF-911. Todos: - TF-1136: upstream `SILDifferentiabilityWitness` serialization. - TF-1137: upstream `SILDifferentiabilityWitness` verification. - TF-1138: upstream `SILDifferentiabilityWitness` SILGen from `@differentiable` and `@derivative` attributes. - TF-20: robust mangling for `SILDifferentiabilityWitness` names. --- docs/SIL.rst | 60 +++++ include/swift/AST/ASTMangler.h | 10 +- include/swift/AST/AutoDiff.h | 5 + include/swift/AST/DiagnosticsParse.def | 26 +++ include/swift/Parse/ParseSILSupport.h | 1 + .../swift/SIL/SILDifferentiabilityWitness.h | 166 ++++++++++++++ include/swift/SIL/SILModule.h | 44 ++++ include/swift/Syntax/TokenKinds.def.gyb | 1 + lib/AST/ASTMangler.cpp | 21 ++ lib/Parse/ParseDecl.cpp | 1 + lib/Parse/ParseStmt.cpp | 1 + lib/ParseSIL/ParseSIL.cpp | 208 ++++++++++++++++++ lib/SIL/CMakeLists.txt | 1 + lib/SIL/SILDifferentiabilityWitness.cpp | 79 +++++++ lib/SIL/SILModule.cpp | 23 ++ lib/SIL/SILPrinter.cpp | 189 +++++++++++----- lib/Syntax/SyntaxSerialization.cpp.gyb | 1 + .../Parse/sil_differentiability_witness.sil | 129 +++++++++++ 18 files changed, 916 insertions(+), 50 deletions(-) create mode 100644 include/swift/SIL/SILDifferentiabilityWitness.h create mode 100644 lib/SIL/SILDifferentiabilityWitness.cpp create mode 100644 test/AutoDiff/SIL/Parse/sil_differentiability_witness.sil diff --git a/docs/SIL.rst b/docs/SIL.rst index 256b14beb74f0..29387e8819c51 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -1316,6 +1316,66 @@ variable cannot be used as l-value, i.e. the reference to the object cannot be modified. As a consequence the variable cannot be accessed with ``global_addr`` but only with ``global_value``. +Differentiability Witnesses +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +:: + + decl ::= sil-differentiability-witness + sil-differentiability-witness ::= + 'sil_differentiability_witness' + sil-linkage? + '[' 'parameters' sil-differentiability-witness-function-index-list ']' + '[' 'results' sil-differentiability-witness-function-index-list ']' + generic-parameter-clause? + sil-function-name ':' sil-type + sil-differentiability-witness-body? + + sil-differentiability-witness-body ::= + '{' sil-differentiability-witness-entry? + sil-differentiability-witness-entry? '}' + + sil-differentiability-witness-entry ::= + sil-differentiability-witness-entry-kind ':' + sil-entry-name ':' sil-type + + sil-differentiability-witness-entry-kind ::= 'jvp' | 'vjp' + +SIL encodes function differentiability via differentiability witnesses. + +Differentiability witnesses map a "key" (including an "original" SIL function) +to derivative SIL functions. + +Differentiability witnesses are keyed by the following: + +- An "original" SIL function name. +- Differentiability parameter indices. +- Differentiability result indices. +- A generic parameter clause, representing differentiability generic + requirements. + +Differentiability witnesses may have a body, specifying derivative functions for +the key. Verification checks that derivative functions have the expected type +based on the key. + +:: + + sil_differentiability_witness hidden [parameters 0] [results 0] @id : $@convention(thin) (T) -> T { + jvp: @id_jvp : $@convention(thin) (T) -> (T, @owned @callee_guaranteed (T.TangentVector) -> T.TangentVector) + vjp: @id_vjp : $@convention(thin) (T) -> (T, @owned @callee_guaranteed (T.TangentVector) -> T.TangentVector) + } + +During SILGen, differentiability witnesses are emitted for the following: + +- `@differentiable` declaration attributes. +- `@derivative` declaration attributes. Registered derivative functions + become differentiability witness entries. + +The SIL differentiation transform canonicalizes differentiability witnesses, +filling in missing entries. + +Differentiability witness entries are accessed via the +`differentiability_witness_function` instruction. + Dataflow Errors --------------- diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index f716daef40faf..95579605f2928 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -153,7 +153,15 @@ class ASTMangler : public Mangler { Type FromType, Type ToType, Type SelfType, ModuleDecl *Module); - + + /// Mangle a SIL differentiability witness key: + /// - Mangled original function name. + /// - Parameter indices. + /// - Result indices. + /// - Derivative generic signature (optional). + std::string + mangleSILDifferentiabilityWitnessKey(SILDifferentiabilityWitnessKey key); + std::string mangleKeyPathGetterThunkHelper(const AbstractStorageDecl *property, GenericSignature signature, CanType baseType, diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index 3bf60d0d77e03..80c0a19213e3c 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -223,6 +223,11 @@ class TangentSpace { NominalTypeDecl *getNominal() const; }; +/// The key type used for uniquing `SILDifferentiabilityWitness` in +/// `SILModule`: original function name, parameter indices, result indices, and +/// derivative generic signature. +using SILDifferentiabilityWitnessKey = std::pair; + /// Automatic differentiation utility namespace. namespace autodiff { diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 1cbb3766c86ee..94b8c21262418 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -678,6 +678,18 @@ ERROR(sil_witness_assoc_conf_not_found,none, ERROR(sil_witness_protocol_conformance_not_found,none, "sil protocol conformance not found", ()) +// SIL differentiability witnesses +ERROR(sil_diff_witness_expected_token,PointsToFirstBadToken, + "expected '%0' in differentiability witness", (StringRef)) +ERROR(sil_diff_witness_serialized_declaration,none, + "differentiability witness declaration should not be serialized", ()) +ERROR(sil_diff_witness_undefined,PointsToFirstBadToken, + "reference to undefined differentiability witness", ()) +ERROR(sil_diff_witness_invalid_generic_signature,PointsToFirstBadToken, + "expected witness generic signature '%0' does not have same generic " + "parameters as original function generic signature '%1'", + (StringRef, StringRef)) + // SIL Coverage Map ERROR(sil_coverage_invalid_hash, none, "expected coverage hash", ()) @@ -1577,6 +1589,20 @@ ERROR(diff_params_clause_expected_parameter_unnamed,PointsToFirstBadToken, ERROR(autodiff_attr_expected_original_decl_name,PointsToFirstBadToken, "expected an original function name", ()) +// SIL autodiff +ERROR(sil_autodiff_expected_lsquare,PointsToFirstBadToken, + "expected '[' to start the %0", (StringRef)) +ERROR(sil_autodiff_expected_rsquare,PointsToFirstBadToken, + "expected ']' to complete the %0", (StringRef)) +ERROR(sil_autodiff_expected_index_list,PointsToFirstBadToken, + "expected a space-separated list of indices, e.g. '0 1'", ()) +ERROR(sil_autodiff_expected_index_list_label,PointsToFirstBadToken, + "expected label '%0' in index list", (StringRef)) +ERROR(sil_autodiff_expected_parameter_index,PointsToFirstBadToken, + "expected the index of a parameter to differentiate with respect to", ()) +ERROR(sil_autodiff_expected_result_index,PointsToFirstBadToken, + "expected the index of a result to differentiate from", ()) + //------------------------------------------------------------------------------ // MARK: Generics parsing diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/Parse/ParseSILSupport.h b/include/swift/Parse/ParseSILSupport.h index 00eaba3e80caf..2ead9961416e5 100644 --- a/include/swift/Parse/ParseSILSupport.h +++ b/include/swift/Parse/ParseSILSupport.h @@ -32,6 +32,7 @@ namespace swift { virtual bool parseSILGlobal(Parser &P) = 0; virtual bool parseSILWitnessTable(Parser &P) = 0; virtual bool parseSILDefaultWitnessTable(Parser &P) = 0; + virtual bool parseSILDifferentiabilityWitness(Parser &P) = 0; virtual bool parseSILCoverageMap(Parser &P) = 0; virtual bool parseSILProperty(Parser &P) = 0; virtual bool parseSILScope(Parser &P) = 0; diff --git a/include/swift/SIL/SILDifferentiabilityWitness.h b/include/swift/SIL/SILDifferentiabilityWitness.h new file mode 100644 index 0000000000000..791bc98e8206b --- /dev/null +++ b/include/swift/SIL/SILDifferentiabilityWitness.h @@ -0,0 +1,166 @@ +//===--- SILDifferentiabilityWitness.h - Differentiability witnesses ------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the SILDifferentiabilityWitness class, which maps an +// original SILFunction and derivative configuration (parameter indices, result +// indices, derivative generic signature) to derivative functions (JVP and VJP). +// +// SIL differentiability witnesses are generated from the `@differentiable` +// and `@derivative` AST declaration attributes. +// +// Differentiability witnesses are canonicalized by the SIL differentiation +// transform, which fills in missing derivative functions. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_SILDIFFERENTIABILITYWITNESS_H +#define SWIFT_SIL_SILDIFFERENTIABILITYWITNESS_H + +#include "swift/AST/Attr.h" +#include "swift/AST/AutoDiff.h" +#include "swift/AST/GenericSignature.h" +#include "swift/SIL/SILAllocated.h" +#include "swift/SIL/SILLinkage.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/ilist_node.h" + +namespace swift { + +class SILPrintContext; + +class SILDifferentiabilityWitness + : public llvm::ilist_node, + public SILAllocated { +private: + /// The module which contains the differentiability witness. + SILModule &Module; + /// The linkage of the differentiability witness. + SILLinkage Linkage; + /// The original function. + SILFunction *OriginalFunction; + /// The derivative configuration: parameter indices, result indices, and + /// derivative generic signature (optional). The derivative generic signature + /// may contain same-type requirements such that all generic parameters are + /// bound to concrete types. + AutoDiffConfig Config; + /// The JVP (Jacobian-vector products) derivative function. + SILFunction *JVP; + /// The VJP (vector-Jacobian products) derivative function. + SILFunction *VJP; + /// Whether or not this differentiability witness is a declaration. + bool IsDeclaration; + /// Whether or not this differentiability witness is serialized, which allows + /// devirtualization from another module. + bool IsSerialized; + /// The AST `@differentiable` or `@derivative` attribute from which the + /// differentiability witness is generated. Used for diagnostics. + /// Null if the differentiability witness is parsed from SIL or if it is + /// deserialized. + const DeclAttribute *Attribute = nullptr; + + SILDifferentiabilityWitness( + SILModule &module, SILLinkage linkage, SILFunction *originalFunction, + IndexSubset *parameterIndices, IndexSubset *resultIndices, + GenericSignature derivativeGenSig, SILFunction *jvp, SILFunction *vjp, + bool isDeclaration, bool isSerialized, const DeclAttribute *attribute) + : Module(module), Linkage(linkage), OriginalFunction(originalFunction), + Config(parameterIndices, resultIndices, derivativeGenSig.getPointer()), + JVP(jvp), VJP(vjp), IsDeclaration(isDeclaration), + IsSerialized(isSerialized), Attribute(attribute) {} + +public: + static SILDifferentiabilityWitness * + createDeclaration(SILModule &module, SILLinkage linkage, + SILFunction *originalFunction, + IndexSubset *parameterIndices, IndexSubset *resultIndices, + GenericSignature derivativeGenSig, + const DeclAttribute *attribute = nullptr); + + static SILDifferentiabilityWitness *createDefinition( + SILModule &module, SILLinkage linkage, SILFunction *originalFunction, + IndexSubset *parameterIndices, IndexSubset *resultIndices, + GenericSignature derivativeGenSig, SILFunction *jvp, SILFunction *vjp, + bool isSerialized, const DeclAttribute *attribute = nullptr); + + void convertToDefinition(SILFunction *jvp, SILFunction *vjp, + bool isSerialized); + + SILDifferentiabilityWitnessKey getKey() const; + SILModule &getModule() const { return Module; } + SILLinkage getLinkage() const { return Linkage; } + SILFunction *getOriginalFunction() const { return OriginalFunction; } + const AutoDiffConfig &getConfig() const { return Config; } + IndexSubset *getParameterIndices() const { return Config.parameterIndices; } + IndexSubset *getResultIndices() const { return Config.resultIndices; } + GenericSignature getDerivativeGenericSignature() const { + return Config.derivativeGenericSignature; + } + SILFunction *getJVP() const { return JVP; } + SILFunction *getVJP() const { return VJP; } + SILFunction *getDerivative(AutoDiffDerivativeFunctionKind kind) const { + switch (kind) { + case AutoDiffDerivativeFunctionKind::JVP: + return JVP; + case AutoDiffDerivativeFunctionKind::VJP: + return VJP; + } + } + void setJVP(SILFunction *jvp) { JVP = jvp; } + void setVJP(SILFunction *vjp) { VJP = vjp; } + void setDerivative(AutoDiffDerivativeFunctionKind kind, + SILFunction *derivative) { + switch (kind) { + case AutoDiffDerivativeFunctionKind::JVP: + JVP = derivative; + break; + case AutoDiffDerivativeFunctionKind::VJP: + VJP = derivative; + break; + } + } + bool isDeclaration() const { return IsDeclaration; } + bool isDefinition() const { return !IsDeclaration; } + bool isSerialized() const { return IsSerialized; } + const DeclAttribute *getAttribute() const { return Attribute; } + + /// Verify that the differentiability witness is well-formed. + void verify(const SILModule &module) const; + + void print(llvm::raw_ostream &os, bool verbose = false) const; + void dump() const; +}; + +} // end namespace swift + +namespace llvm { + +//===----------------------------------------------------------------------===// +// ilist_traits for SILDifferentiabilityWitness +//===----------------------------------------------------------------------===// + +template <> +struct ilist_traits<::swift::SILDifferentiabilityWitness> + : public ilist_node_traits<::swift::SILDifferentiabilityWitness> { + using SILDifferentiabilityWitness = ::swift::SILDifferentiabilityWitness; + +public: + static void deleteNode(SILDifferentiabilityWitness *DW) { + DW->~SILDifferentiabilityWitness(); + } + +private: + void createNode(const SILDifferentiabilityWitness &); +}; + +} // namespace llvm + +#endif // SWIFT_SIL_SILDIFFERENTIABILITYWITNESS_H diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 9b5f36b1280dd..9a24fd36dbd2e 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -28,6 +28,7 @@ #include "swift/SIL/SILCoverageMap.h" #include "swift/SIL/SILDeclRef.h" #include "swift/SIL/SILDefaultWitnessTable.h" +#include "swift/SIL/SILDifferentiabilityWitness.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILGlobalVariable.h" #include "swift/SIL/SILPrintContext.h" @@ -113,6 +114,8 @@ class SILModule { using PropertyListType = llvm::ilist; using WitnessTableListType = llvm::ilist; using DefaultWitnessTableListType = llvm::ilist; + using DifferentiabilityWitnessListType = + llvm::ilist; using CoverageMapCollectionType = llvm::MapVector; @@ -131,6 +134,7 @@ class SILModule { friend SILBasicBlock; friend SILCoverageMap; friend SILDefaultWitnessTable; + friend SILDifferentiabilityWitness; friend SILFunction; friend SILGlobalVariable; friend SILLayout; @@ -194,6 +198,17 @@ class SILModule { /// The list of SILDefaultWitnessTables in the module. DefaultWitnessTableListType defaultWitnessTables; + /// Lookup table for SIL differentiability witnesses, keyed by mangled name. + llvm::StringMap DifferentiabilityWitnessMap; + + /// Lookup table for SILDifferentiabilityWitnesses, keyed by original + /// function name. + llvm::StringMap> + DifferentiabilityWitnessesByFunction; + + /// The list of SILDifferentiabilityWitnesses in the module. + DifferentiabilityWitnessListType differentiabilityWitnesses; + /// Declarations which are externally visible. /// /// These are method declarations which are referenced from inlinable @@ -455,6 +470,24 @@ class SILModule { return {defaultWitnessTables.begin(), defaultWitnessTables.end()}; } + using differentiability_witness_iterator = DifferentiabilityWitnessListType::iterator; + using differentiability_witness_const_iterator = DifferentiabilityWitnessListType::const_iterator; + DifferentiabilityWitnessListType &getDifferentiabilityWitnessList() { return differentiabilityWitnesses; } + const DifferentiabilityWitnessListType &getDifferentiabilityWitnessList() const { return differentiabilityWitnesses; } differentiability_witness_iterator differentiability_witness_begin() { return differentiabilityWitnesses.begin(); } + differentiability_witness_iterator differentiability_witness_end() { return differentiabilityWitnesses.end(); } + differentiability_witness_const_iterator differentiability_witness_begin() const { return differentiabilityWitnesses.begin(); } + differentiability_witness_const_iterator differentiability_witness_end() const { return differentiabilityWitnesses.end(); } + iterator_range + getDifferentiabilityWitnesses() { + return {differentiabilityWitnesses.begin(), + differentiabilityWitnesses.end()}; + } + iterator_range + getDifferentiabilityWitnesses() const { + return {differentiabilityWitnesses.begin(), + differentiabilityWitnesses.end()}; + } + void addExternallyVisibleDecl(ValueDecl *decl) { externallyVisible.insert(decl); } @@ -591,6 +624,17 @@ class SILModule { /// hierarchy of \p Class. SILFunction *lookUpFunctionInVTable(ClassDecl *Class, SILDeclRef Member); + /// Look up the differentiability witness with the given name. + SILDifferentiabilityWitness *lookUpDifferentiabilityWitness(StringRef name); + + /// Look up the differentiability witness corresponding to the given key. + SILDifferentiabilityWitness * + lookUpDifferentiabilityWitness(SILDifferentiabilityWitnessKey key); + + /// Look up the differentiability witness corresponding to the given function. + llvm::ArrayRef + lookUpDifferentiabilityWitnessesForFunction(StringRef name); + // Given a protocol, attempt to create a default witness table declaration // for it. SILDefaultWitnessTable * diff --git a/include/swift/Syntax/TokenKinds.def.gyb b/include/swift/Syntax/TokenKinds.def.gyb index 4627055832e9c..4afa4f0d48e76 100644 --- a/include/swift/Syntax/TokenKinds.def.gyb +++ b/include/swift/Syntax/TokenKinds.def.gyb @@ -165,6 +165,7 @@ SIL_KEYWORD(sil_vtable) SIL_KEYWORD(sil_global) SIL_KEYWORD(sil_witness_table) SIL_KEYWORD(sil_default_witness_table) +SIL_KEYWORD(sil_differentiability_witness) SIL_KEYWORD(sil_coverage_map) SIL_KEYWORD(sil_scope) diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 1b7383421e320..6737ceed42f6e 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -379,6 +379,27 @@ std::string ASTMangler::mangleReabstractionThunkHelper( return finalize(); } +std::string ASTMangler::mangleSILDifferentiabilityWitnessKey( + SILDifferentiabilityWitnessKey key) { + // TODO(TF-20): Make the mangling scheme robust. Support demangling. + beginManglingWithoutPrefix(); + + auto originalName = key.first; + auto *parameterIndices = key.second.parameterIndices; + auto *resultIndices = key.second.resultIndices; + auto derivativeGenericSignature = key.second.derivativeGenericSignature; + + Buffer << "AD__" << originalName << '_'; + Buffer << "P" << parameterIndices->getString(); + Buffer << "R" << resultIndices->getString(); + if (derivativeGenericSignature) + appendGenericSignature(derivativeGenericSignature); + + auto result = Storage.str().str(); + Storage.clear(); + return result; +} + std::string ASTMangler::mangleTypeForDebugger(Type Ty, const DeclContext *DC) { PrettyStackTraceType prettyStackTrace(Ty->getASTContext(), "mangling type for debugger", Ty); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 6edbf335b8d78..a657bfb1c0674 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -226,6 +226,7 @@ void Parser::parseTopLevel() { CASE_SIL(sil_global, SILGlobal) CASE_SIL(sil_witness_table, SILWitnessTable) CASE_SIL(sil_default_witness_table, SILDefaultWitnessTable) + CASE_SIL(sil_differentiability_witness, SILDifferentiabilityWitness) CASE_SIL(sil_coverage_map, SILCoverageMap) CASE_SIL(sil_property, SILProperty) CASE_SIL(sil_scope, SILScope) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 3805594342060..93f41d140e062 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -313,6 +313,7 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl &Entries, Tok.isNot(tok::kw_sil_global) && Tok.isNot(tok::kw_sil_witness_table) && Tok.isNot(tok::kw_sil_default_witness_table) && + Tok.isNot(tok::kw_sil_differentiability_witness) && Tok.isNot(tok::kw_sil_property) && (isConditionalBlock || !isTerminatorForBraceItemListKind(Kind, Entries))) { diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 35aa6f9a2be75..9b6a572f95b07 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -76,6 +76,7 @@ class SILParserTUState : public SILParserTUStateBase { bool parseSILGlobal(Parser &P) override; bool parseSILWitnessTable(Parser &P) override; bool parseSILDefaultWitnessTable(Parser &P) override; + bool parseSILDifferentiabilityWitness(Parser &P) override; bool parseSILCoverageMap(Parser &P) override; bool parseSILProperty(Parser &P) override; bool parseSILScope(Parser &P) override; @@ -2006,6 +2007,108 @@ static bool parseAssignOwnershipQualifier(AssignOwnershipQualifier &Result, return false; } +// Parse a list of integer indices, prefaced with the given string label. +// Returns true on error. +static bool parseIndexList(Parser &P, StringRef label, + SmallVectorImpl &indices, + const Diagnostic &parseIndexDiag) { + SourceLoc loc; + // Parse `[ (Builtin.Word) -> (Swift.Array, Builtin.RawPointer) -sil [_semantics "array.uninitialized"] @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer) +sil [_semantics "array.uninitialized_intrinsic"] @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer) sil [_semantics "array.uninitialized"] @adoptStorageSpecialiedForInt : $@convention(method) (@guaranteed _ContiguousArrayStorage, Builtin.Word, @thin Array.Type) -> (@owned Array, UnsafeMutablePointer) diff --git a/test/SILOptimizer/dead_array_elim.swift b/test/SILOptimizer/dead_array_elim.swift new file mode 100644 index 0000000000000..fdc389e8441c2 --- /dev/null +++ b/test/SILOptimizer/dead_array_elim.swift @@ -0,0 +1,40 @@ +// RUN: %target-swift-frontend -O -emit-sil -primary-file %s | %FileCheck %s + +// These tests check whether DeadObjectElimination pass runs as a part of the +// optimization pipeline and eliminates dead array literals in Swift code. +// Note that DeadObjectElimination pass relies on @_semantics annotations on +// the array initializer that is used by the compiler to create array literals. +// This test would fail if in case the initializer used by the compiler to +// initialize array literals doesn't match the one expected by the pass. + +// CHECK-LABEL: sil hidden @$s15dead_array_elim24testDeadArrayEliminationyyF +func testDeadArrayElimination() { + _ = [1, 2, 3] + // CHECK: bb0: + // CHECK-NEXT: %{{.*}} = tuple () + // CHECK-NEXT: return %{{.*}} : $() +} + +// CHECK-LABEL: sil hidden @$s15dead_array_elim29testEmptyDeadArrayEliminationyyF +func testEmptyDeadArrayElimination() { + _ = [] + // CHECK: bb0: + // CHECK-NEXT: %{{.*}} = tuple () + // CHECK-NEXT: return %{{.*}} : $() +} + +// The use case tested by the following test, where a _fixLifetime call is +// invoked on an array, appears when new os log APIs are used. +// CHECK-LABEL: sil hidden @$s15dead_array_elim35testDeadArrayElimWithFixLifetimeUseyyF +func testDeadArrayElimWithFixLifetimeUse() { + let a: [Int] = [] + _fixLifetime(a) + // CHECK: bb0: + // CHECK-NEXT: %{{.*}} = tuple () + // CHECK-NEXT: return %{{.*}} : $() +} + +// FIXME: DeadObjectElimination doesn't optimize this yet. +func testDeadArrayElimWithAddressOnlyValues(x: T, y: T) { + _ = [x, y] +} From e9c51a0755aebb87921c82b7e8e2fc115410b34b Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 5 Feb 2020 15:05:11 -0800 Subject: [PATCH 175/237] [CodeCompletion] Report 'Unrelated' type relation for keywords --- lib/IDE/CodeCompletion.cpp | 7 +- .../CodeComplete/complete_typerelation.swift | 70 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index c09834aa0840a..5e9e6c8f853c2 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -3613,8 +3613,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } // Fallback to showing the default type. - if (!defaultTypeName.empty()) + if (!defaultTypeName.empty()) { builder.addTypeAnnotation(defaultTypeName); + builder.setExpectedTypeRelation( + expectedTypeContext.possibleTypes.empty() + ? CodeCompletionResult::ExpectedTypeRelation::Unknown + : CodeCompletionResult::ExpectedTypeRelation::Unrelated); + } } /// Add '#file', '#line', et at. diff --git a/test/SourceKit/CodeComplete/complete_typerelation.swift b/test/SourceKit/CodeComplete/complete_typerelation.swift index c62b0d05d96ce..5aaf2235e37ba 100644 --- a/test/SourceKit/CodeComplete/complete_typerelation.swift +++ b/test/SourceKit/CodeComplete/complete_typerelation.swift @@ -17,8 +17,78 @@ func testConvertibleContext() -> MyProto { return MyEnum. } +func testBool() -> Bool { + return +} +func testOptionalInt() -> Int? { + return +} +func testVoid() -> Void { + return +} +func testUnknown() { + +} + // RUN: %sourcekitd-test -req=complete -pos=13:17 %s -- %s > %t.identical.response // RUN: diff -u %s.identical.response %t.identical.response // RUN: %sourcekitd-test -req=complete -pos=17:17 %s -- %s > %t.convertible.response // RUN: diff -u %s.convertible.response %t.convertible.response + +// RUN: %sourcekitd-test -req=complete -pos=21:10 %s -- %s | %FileCheck %s --check-prefix=BOOLCONTEXT +// RUN: %sourcekitd-test -req=complete -pos=24:10 %s -- %s | %FileCheck %s --check-prefix=OPTIONALCONTEXT +// RUN: %sourcekitd-test -req=complete -pos=27:10 %s -- %s | %FileCheck %s --check-prefix=VOIDCONTEXT +// RUN: %sourcekitd-test -req=complete -pos=27:10 %s -- %s | %FileCheck %s --check-prefix=UNKNOWNCONTEXT + +// BOOLCONTEXT-LABEL: key.name: "false", +// BOOLCONTEXT-NOT: key.name: +// BOOLCONTEXT: key.typename: "Bool", +// BOOLCONTEXT: key.typerelation: source.codecompletion.typerelation.identical, +// BOOLCONTEXT-LABEL: key.name: "nil", +// BOOLCONTEXT-NOT: key.name: +// BOOLCONTEXT: key.typename: "", +// BOOLCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, +// BOOLCONTEXT-LABEL: key.name: "true", +// BOOLCONTEXT-NOT: key.name: +// BOOLCONTEXT: key.typename: "Bool", +// BOOLCONTEXT: key.typerelation: source.codecompletion.typerelation.identical, + +// OPTIONALCONTEXT-LABEL: key.name: "false", +// OPTIONALCONTEXT-NOT: key.name: +// OPTIONALCONTEXT: key.typename: "Bool", +// OPTIONALCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, +// OPTIONALCONTEXT-LABEL: key.name: "nil", +// OPTIONALCONTEXT-NOT: key.name: +// OPTIONALCONTEXT: key.typename: "Int?", +// OPTIONALCONTEXT: key.typerelation: source.codecompletion.typerelation.identical, +// OPTIONALCONTEXT-LABEL: key.name: "true", +// OPTIONALCONTEXT-NOT: key.name: +// OPTIONALCONTEXT: key.typename: "Bool", +// OPTIONALCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, + +// VOIDCONTEXT-LABEL: key.name: "false", +// VOIDCONTEXT-NOT: key.name: +// VOIDCONTEXT: key.typename: "Bool", +// VOIDCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, +// VOIDCONTEXT-LABEL: key.name: "nil", +// VOIDCONTEXT-NOT: key.name: +// VOIDCONTEXT: key.typename: "", +// VOIDCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, +// VOIDCONTEXT-LABEL: key.name: "true", +// VOIDCONTEXT-NOT: key.name: +// VOIDCONTEXT: key.typename: "Bool", +// VOIDCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, + +// UNKNOWNCONTEXT-LABEL: key.name: "false", +// UNKNOWNCONTEXT-NOT: key.name: +// UNKNOWNCONTEXT: key.typename: "Bool", +// UNKNOWNCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, +// UNKNOWNCONTEXT-LABEL: key.name: "nil", +// UNKNOWNCONTEXT-NOT: key.name: +// UNKNOWNCONTEXT: key.typename: "", +// UNKNOWNCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, +// UNKNOWNCONTEXT-LABEL: key.name: "true", +// UNKNOWNCONTEXT-NOT: key.name: +// UNKNOWNCONTEXT: key.typename: "Bool", +// UNKNOWNCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, From 566a84d641d021feee2ba53597dee112cce6ddcd Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 5 Feb 2020 15:35:01 -0800 Subject: [PATCH 176/237] [CodeCompletion] Handle new kinds in type relation printing --- lib/IDE/CodeCompletion.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 5e9e6c8f853c2..bc7efd9211556 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -695,7 +695,7 @@ void CodeCompletionResult::print(raw_ostream &OS) const { Prefix.append(Twine(NumBytesToErase).str()); Prefix.append("]"); } - switch (TypeDistance) { + switch (ExpectedTypeRelation(TypeDistance)) { case ExpectedTypeRelation::Invalid: Prefix.append("/TypeRelation[Invalid]"); break; @@ -705,6 +705,8 @@ void CodeCompletionResult::print(raw_ostream &OS) const { case ExpectedTypeRelation::Convertible: Prefix.append("/TypeRelation[Convertible]"); break; + case ExpectedTypeRelation::NotApplicable: + case ExpectedTypeRelation::Unknown: case ExpectedTypeRelation::Unrelated: break; } From db6eb27ac7a94de59bf3b83f9f0332a04639a208 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 5 Feb 2020 18:42:28 -0500 Subject: [PATCH 177/237] Sema: Only diagnose 'throws' in 'defer' as part of TypeCheckError We had a similar check in TypeCheckStmt but it did not account for the possibility of the 'throw' being contained inside a 'do'/'catch' inside a 'defer'. Remove it and just diagnose everything in one place. Fixes . --- lib/Sema/TypeCheckError.cpp | 16 +++------- lib/Sema/TypeCheckStmt.cpp | 7 ---- test/stmt/statements.swift | 64 ++++++++++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/lib/Sema/TypeCheckError.cpp b/lib/Sema/TypeCheckError.cpp index c44dc471deb9d..298576c14bba7 100644 --- a/lib/Sema/TypeCheckError.cpp +++ b/lib/Sema/TypeCheckError.cpp @@ -855,7 +855,6 @@ class Context { Kind TheKind; bool DiagnoseErrorOnTry = false; - bool isInDefer = false; DeclContext *RethrowsDC = nullptr; InterpolatedStringLiteralExpr *InterpolatedString = nullptr; @@ -899,9 +898,7 @@ class Context { } static Context forDeferBody() { - Context result(Kind::DeferBody); - result.isInDefer = true; - return result; + return Context(Kind::DeferBody); } static Context forInitializer(Initializer *init) { @@ -979,18 +976,13 @@ class Context { static void diagnoseThrowInIllegalContext(DiagnosticEngine &Diags, ASTNode node, - StringRef description, - bool throwInDefer = false) { - if (auto *e = node.dyn_cast()) + StringRef description) { + if (auto *e = node.dyn_cast()) { if (isa(e)) { Diags.diagnose(e->getLoc(), diag::throwing_call_in_illegal_context, description); return; } - - if (throwInDefer) { - // Return because this would've already been diagnosed in TypeCheckStmt. - return; } Diags.diagnose(node.getStartLoc(), diag::throw_in_illegal_context, @@ -1156,7 +1148,7 @@ class Context { diagnoseThrowInIllegalContext(Diags, E, "a catch guard expression"); return; case Kind::DeferBody: - diagnoseThrowInIllegalContext(Diags, E, "a defer body", isInDefer); + diagnoseThrowInIllegalContext(Diags, E, "a defer body"); return; } llvm_unreachable("bad context kind"); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index a84ccb3eb5bf6..e86e24822fef9 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -610,13 +610,6 @@ class StmtChecker : public StmtVisitor { } Stmt *visitThrowStmt(ThrowStmt *TS) { - // If the throw is in a defer, then it isn't valid. - if (isInDefer()) { - getASTContext().Diags.diagnose(TS->getThrowLoc(), - diag::jump_out_of_defer, "throw"); - return nullptr; - } - // Coerce the operand to the exception type. auto E = TS->getSubExpr(); diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 085a554189cec..4ad35cdee84c7 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -388,30 +388,92 @@ enum DeferThrowError: Error { } func throwInDefer() { - defer { throw DeferThrowError.someError } // expected-error {{'throw' cannot transfer control out of a defer statement}} + defer { throw DeferThrowError.someError } // expected-error {{errors cannot be thrown out of a defer body}} print("Foo") } +func throwInDeferOK1() { + defer { + do { + throw DeferThrowError.someError + } catch {} + } + print("Bar") +} + +func throwInDeferOK2() throws { + defer { + do { + throw DeferThrowError.someError + } catch {} + } + print("Bar") +} + func throwingFuncInDefer1() throws { defer { try throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} print("Bar") } +func throwingFuncInDefer1a() throws { + defer { + do { + try throwingFunctionCalledInDefer() + } catch {} + } + print("Bar") +} + func throwingFuncInDefer2() throws { defer { throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} print("Bar") } +func throwingFuncInDefer2a() throws { + defer { + do { + throwingFunctionCalledInDefer() + // expected-error@-1 {{call can throw but is not marked with 'try'}} + // expected-note@-2 {{did you mean to use 'try'?}} + // expected-note@-3 {{did you mean to handle error as optional value?}} + // expected-note@-4 {{did you mean to disable error propagation?}} + } catch {} + } + print("Bar") +} + func throwingFuncInDefer3() { defer { try throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} print("Bar") } +func throwingFuncInDefer3a() { + defer { + do { + try throwingFunctionCalledInDefer() + } catch {} + } + print("Bar") +} + func throwingFuncInDefer4() { defer { throwingFunctionCalledInDefer() } // expected-error {{errors cannot be thrown out of a defer body}} print("Bar") } +func throwingFuncInDefer4a() { + defer { + do { + throwingFunctionCalledInDefer() + // expected-error@-1 {{call can throw but is not marked with 'try'}} + // expected-note@-2 {{did you mean to use 'try'?}} + // expected-note@-3 {{did you mean to handle error as optional value?}} + // expected-note@-4 {{did you mean to disable error propagation?}} + } catch {} + } + print("Bar") +} + func throwingFunctionCalledInDefer() throws { throw DeferThrowError.someError } From cc8d4965f89b984990613e5ba3b6375b33c7da4e Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 5 Feb 2020 17:05:23 -0800 Subject: [PATCH 178/237] [CodeCompletion] Ensure cached results have 'unknown' type relation --- include/swift/IDE/CodeCompletion.h | 6 ++--- lib/IDE/CodeCompletion.cpp | 2 +- lib/IDE/CodeCompletionCache.cpp | 2 +- .../CodeComplete/complete_typerelation.swift | 25 ++++++++++++++++--- .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 3 ++- .../lib/SwiftLang/SwiftCompletion.cpp | 3 ++- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index 3265e08375b04..553fd504cd971 100644 --- a/include/swift/IDE/CodeCompletion.h +++ b/include/swift/IDE/CodeCompletion.h @@ -585,7 +585,7 @@ class CodeCompletionResult { CodeCompletionResult(ResultKind Kind, SemanticContextKind SemanticContext, unsigned NumBytesToErase, CodeCompletionString *CompletionString, - ExpectedTypeRelation TypeDistance = Unrelated, + ExpectedTypeRelation TypeDistance, CodeCompletionOperatorKind KnownOperatorKind = CodeCompletionOperatorKind::None) : Kind(Kind), KnownOperatorKind(unsigned(KnownOperatorKind)), @@ -610,7 +610,7 @@ class CodeCompletionResult { SemanticContextKind SemanticContext, unsigned NumBytesToErase, CodeCompletionString *CompletionString, - ExpectedTypeRelation TypeDistance = Unrelated) + ExpectedTypeRelation TypeDistance) : Kind(Keyword), KnownOperatorKind(0), SemanticContext(unsigned(SemanticContext)), NotRecommended(false), NotRecReason(NotRecommendedReason::NoReason), @@ -689,7 +689,7 @@ class CodeCompletionResult { AssociatedUSRs(AssociatedUSRs), DocWords(DocWords) { AssociatedKind = static_cast(DeclKind); assert(CompletionString); - TypeDistance = ExpectedTypeRelation::Unrelated; + TypeDistance = ExpectedTypeRelation::Unknown; assert(!isOperator() || getOperatorKind() != CodeCompletionOperatorKind::None); } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index bc7efd9211556..155cdb72299bf 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -695,7 +695,7 @@ void CodeCompletionResult::print(raw_ostream &OS) const { Prefix.append(Twine(NumBytesToErase).str()); Prefix.append("]"); } - switch (ExpectedTypeRelation(TypeDistance)) { + switch (getExpectedTypeRelation()) { case ExpectedTypeRelation::Invalid: Prefix.append("/TypeRelation[Invalid]"); break; diff --git a/lib/IDE/CodeCompletionCache.cpp b/lib/IDE/CodeCompletionCache.cpp index dd63357194c35..3d466f3bfdbc4 100644 --- a/lib/IDE/CodeCompletionCache.cpp +++ b/lib/IDE/CodeCompletionCache.cpp @@ -255,7 +255,7 @@ static bool readCachedModule(llvm::MemoryBuffer *in, } else { result = new (*V.Sink.Allocator) CodeCompletionResult(kind, context, numBytesToErase, string, - CodeCompletionResult::Unrelated, opKind); + CodeCompletionResult::NotApplicable, opKind); } V.Sink.Results.push_back(result); diff --git a/test/SourceKit/CodeComplete/complete_typerelation.swift b/test/SourceKit/CodeComplete/complete_typerelation.swift index 5aaf2235e37ba..603d57df21eb5 100644 --- a/test/SourceKit/CodeComplete/complete_typerelation.swift +++ b/test/SourceKit/CodeComplete/complete_typerelation.swift @@ -36,15 +36,20 @@ func testUnknown() { // RUN: %sourcekitd-test -req=complete -pos=17:17 %s -- %s > %t.convertible.response // RUN: diff -u %s.convertible.response %t.convertible.response -// RUN: %sourcekitd-test -req=complete -pos=21:10 %s -- %s | %FileCheck %s --check-prefix=BOOLCONTEXT -// RUN: %sourcekitd-test -req=complete -pos=24:10 %s -- %s | %FileCheck %s --check-prefix=OPTIONALCONTEXT -// RUN: %sourcekitd-test -req=complete -pos=27:10 %s -- %s | %FileCheck %s --check-prefix=VOIDCONTEXT -// RUN: %sourcekitd-test -req=complete -pos=27:10 %s -- %s | %FileCheck %s --check-prefix=UNKNOWNCONTEXT +// RUN: %empty-directory(%t/cache) +// RUN: %sourcekitd-test -req=complete.cache.ondisk -cache-path %t/cache == -req=complete -pos=21:10 %s -- %s | %FileCheck %s --check-prefix=BOOLCONTEXT +// RUN: %sourcekitd-test -req=complete.cache.ondisk -cache-path %t/cache == -req=complete -pos=24:10 %s -- %s | %FileCheck %s --check-prefix=OPTIONALCONTEXT +// RUN: %sourcekitd-test -req=complete.cache.ondisk -cache-path %t/cache == -req=complete -pos=27:10 %s -- %s | %FileCheck %s --check-prefix=VOIDCONTEXT +// RUN: %sourcekitd-test -req=complete.cache.ondisk -cache-path %t/cache == -req=complete -pos=27:10 %s -- %s | %FileCheck %s --check-prefix=UNKNOWNCONTEXT // BOOLCONTEXT-LABEL: key.name: "false", // BOOLCONTEXT-NOT: key.name: // BOOLCONTEXT: key.typename: "Bool", // BOOLCONTEXT: key.typerelation: source.codecompletion.typerelation.identical, +// BOOLCONTEXT-LABEL: key.name: "Int", +// BOOLCONTEXT-NOT: key.name: +// BOOLCONTEXT: key.typename: "Int", +// BOOLCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, // BOOLCONTEXT-LABEL: key.name: "nil", // BOOLCONTEXT-NOT: key.name: // BOOLCONTEXT: key.typename: "", @@ -58,6 +63,10 @@ func testUnknown() { // OPTIONALCONTEXT-NOT: key.name: // OPTIONALCONTEXT: key.typename: "Bool", // OPTIONALCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, +// OPTIONALCONTEXT-LABEL: key.name: "Int", +// OPTIONALCONTEXT-NOT: key.name: +// OPTIONALCONTEXT: key.typename: "Int", +// OPTIONALCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, // OPTIONALCONTEXT-LABEL: key.name: "nil", // OPTIONALCONTEXT-NOT: key.name: // OPTIONALCONTEXT: key.typename: "Int?", @@ -71,6 +80,10 @@ func testUnknown() { // VOIDCONTEXT-NOT: key.name: // VOIDCONTEXT: key.typename: "Bool", // VOIDCONTEXT: key.typerelation: source.codecompletion.typerelation.unrelated, +// VOIDCONTEXT-LABEL: key.name: "Int", +// VOIDCONTEXT-NOT: key.name: +// VOIDCONTEXT: key.typename: "Int", +// VOIDCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, // VOIDCONTEXT-LABEL: key.name: "nil", // VOIDCONTEXT-NOT: key.name: // VOIDCONTEXT: key.typename: "", @@ -84,6 +97,10 @@ func testUnknown() { // UNKNOWNCONTEXT-NOT: key.name: // UNKNOWNCONTEXT: key.typename: "Bool", // UNKNOWNCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, +// UNKNOWNCONTEXT-LABEL: key.name: "Int", +// UNKNOWNCONTEXT-NOT: key.name: +// UNKNOWNCONTEXT: key.typename: "Int", +// UNKNOWNCONTEXT: key.typerelation: source.codecompletion.typerelation.unknown, // UNKNOWNCONTEXT-LABEL: key.name: "nil", // UNKNOWNCONTEXT-NOT: key.name: // UNKNOWNCONTEXT: key.typename: "", diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 83635923ba70f..8a4237f0d4449 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -158,7 +158,8 @@ bool SourceKit::CodeCompletion::addCustomCompletions( CodeCompletion::SwiftResult swiftResult( CodeCompletion::SwiftResult::ResultKind::Pattern, SemanticContextKind::ExpressionSpecific, - /*NumBytesToErase=*/0, completionString); + /*NumBytesToErase=*/0, completionString, + CodeCompletionResult::ExpectedTypeRelation::Unknown); CompletionBuilder builder(sink, swiftResult); builder.setCustomKind(customCompletion.Kind); diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 7634210f6bbf8..006f871fa4f1c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -992,7 +992,8 @@ static void transformAndForwardResults( CodeCompletion::SwiftResult paren( CodeCompletion::SwiftResult::ResultKind::BuiltinOperator, SemanticContextKind::ExpressionSpecific, - exactMatch ? exactMatch->getNumBytesToErase() : 0, completionString); + exactMatch ? exactMatch->getNumBytesToErase() : 0, completionString, + CodeCompletionResult::ExpectedTypeRelation::NotApplicable); SwiftCompletionInfo info; std::vector extended = From 0ae9e68607a235b30c9cf8204e055540be0d2156 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 10 Dec 2019 16:54:03 -0800 Subject: [PATCH 179/237] [metadata prespecialization] On by default. Previously, -Xfrontend -prespecialize-generic-metadata had to be passed in order for generic metadata to be prespecialized. Now it is prespecialized unless -Xfrontend -disable-generic-metadata-prespecialization is passed. --- include/swift/AST/IRGenOptions.h | 2 +- include/swift/Option/FrontendOptions.td | 6 +- lib/Frontend/CompilerInvocation.cpp | 4 +- test/IRGen/conditional_conformances.swift | 8 +- .../conditional_conformances_future.swift | 8 + ...al_conformances_gettypemetdatabyname.swift | 3 +- test/IRGen/foreign_types.sil | 2 +- test/IRGen/foreign_types_future.sil | 43 ++ test/IRGen/generic_metatypes.swift | 22 +- test/IRGen/generic_metatypes_future.swift | 178 ++++++++ test/IRGen/generic_structs.sil | 2 +- test/IRGen/generic_structs_future.sil | 261 +++++++++++ ...within-class-1argument-1distinct_use.swift | 2 +- ...ate-inmodule-1argument-1distinct_use.swift | 2 +- ...within-class-1argument-1distinct_use.swift | 2 +- .../struct-inmodule-0argument.swift | 2 +- ...uct-inmodule-1argument-0distinct_use.swift | 2 +- ...1argument-1conformance-1distinct_use.swift | 2 +- ...mance_stdlib_equatable-1distinct_use.swift | 2 +- ...dule-1argument-1distinct_generic_use.swift | 2 +- ...uct-inmodule-1argument-1distinct_use.swift | 2 +- ...1argument-2conformance-1distinct_use.swift | 2 +- ...uct-inmodule-1argument-2distinct_use.swift | 2 +- ...1argument-3conformance-1distinct_use.swift | 2 +- ...uct-inmodule-1argument-3distinct_use.swift | 2 +- ...1argument-4conformance-1distinct_use.swift | 2 +- ...uct-inmodule-1argument-4distinct_use.swift | 2 +- ...1argument-5conformance-1distinct_use.swift | 2 +- ...uct-inmodule-1argument-5distinct_use.swift | 2 +- ...within-class-1argument-1distinct_use.swift | 2 +- ...-within-enum-1argument-1distinct_use.swift | 2 +- ...ithin-struct-1argument-1distinct_use.swift | 2 +- ...ension-equal_arguments-1distinct_use.swift | 8 +- ...uct-inmodule-2argument-0distinct_use.swift | 2 +- ...uct-inmodule-2argument-1distinct_use.swift | 2 +- ...uct-inmodule-2argument-2distinct_use.swift | 2 +- ...uct-inmodule-2argument-3distinct_use.swift | 2 +- ...uct-inmodule-2argument-4distinct_use.swift | 2 +- ...uct-inmodule-2argument-5distinct_use.swift | 2 +- ...within-class-1argument-1distinct_use.swift | 2 +- ...lic-inmodule-1argument-1distinct_use.swift | 2 +- .../signature_conformances_multifile.swift | 2 +- ...nature_conformances_multifile_future.swift | 65 +++ test/IRGen/synthesized_conformance.swift | 2 +- .../synthesized_conformance_future.swift | 100 ++++ ...ional_conformance_basic_conformances.swift | 60 +++ ...onformance_basic_conformances_future.swift | 428 ++++++++++++++++++ ...ditional_conformance_subclass_future.swift | 176 +++++++ .../conditional_conformance_with_assoc.swift | 19 +- ...tional_conformance_with_assoc_future.swift | 308 +++++++++++++ 50 files changed, 1704 insertions(+), 59 deletions(-) create mode 100644 test/IRGen/conditional_conformances_future.swift create mode 100644 test/IRGen/foreign_types_future.sil create mode 100644 test/IRGen/generic_metatypes_future.swift create mode 100644 test/IRGen/generic_structs_future.sil create mode 100644 test/IRGen/signature_conformances_multifile_future.swift create mode 100644 test/IRGen/synthesized_conformance_future.swift create mode 100644 test/Inputs/conditional_conformance_basic_conformances_future.swift create mode 100644 test/Inputs/conditional_conformance_subclass_future.swift create mode 100644 test/Inputs/conditional_conformance_with_assoc_future.swift diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 699385ad271d1..d28d7386473d6 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -262,7 +262,7 @@ class IRGenOptions { EnableAnonymousContextMangledNames(false), ForcePublicLinkage(false), LazyInitializeClassMetadata(false), LazyInitializeProtocolConformances(false), DisableLegacyTypeInfo(false), - PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true), + PrespecializeGenericMetadata(true), UseIncrementalLLVMCodeGen(true), UseSwiftCall(false), GenerateProfile(false), EnableDynamicReplacementChaining(false), DisableRoundTripDebugTypes(false), DisableDebuggerShadowCopies(false), diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index f8fd3abd492b0..f015556a4b6dd 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -651,9 +651,9 @@ def disable_verify_exclusivity : Flag<["-"], "disable-verify-exclusivity">, def disable_legacy_type_info : Flag<["-"], "disable-legacy-type-info">, HelpText<"Completely disable legacy type layout">; -def prespecialize_generic_metadata : Flag<["-"], "prespecialize-generic-metadata">, - HelpText<"Statically specialize metadata for generic types at types that " - "are known to be used in source.">; +def disable_generic_metadata_prespecialization : Flag<["-"], "disable-generic-metadata-prespecialization">, + HelpText<"Do not statically specialize metadata for generic types at types " + "that are known to be used in source.">; def read_legacy_type_info_path_EQ : Joined<["-"], "read-legacy-type-info-path=">, HelpText<"Read legacy type layout from the given path instead of default path">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3ac0cd2dc59fd..985730391d1a0 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1327,8 +1327,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.DisableLegacyTypeInfo = true; } - if (Args.hasArg(OPT_prespecialize_generic_metadata)) { - Opts.PrespecializeGenericMetadata = true; + if (Args.hasArg(OPT_disable_generic_metadata_prespecialization)) { + Opts.PrespecializeGenericMetadata = false; } if (const Arg *A = Args.getLastArg(OPT_read_legacy_type_info_path_EQ)) { diff --git a/test/IRGen/conditional_conformances.swift b/test/IRGen/conditional_conformances.swift index 8d9cff14aff4a..6503c5aed0d06 100644 --- a/test/IRGen/conditional_conformances.swift +++ b/test/IRGen/conditional_conformances.swift @@ -1,7 +1,7 @@ -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=CHECK --check-prefix=%target-os -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_with_assoc.swift | %FileCheck %S/../Inputs/conditional_conformance_with_assoc.swift --check-prefix=CHECK --check-prefix=%target-os -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_subclass.swift | %FileCheck %S/../Inputs/conditional_conformance_subclass.swift --check-prefix=CHECK --check-prefix=%target-os -// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_recursive.swift | %FileCheck %S/../Inputs/conditional_conformance_recursive.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -emit-ir %S/../Inputs/conditional_conformance_with_assoc.swift | %FileCheck %S/../Inputs/conditional_conformance_with_assoc.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -emit-ir %S/../Inputs/conditional_conformance_subclass.swift | %FileCheck %S/../Inputs/conditional_conformance_subclass.swift --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -emit-ir %S/../Inputs/conditional_conformance_recursive.swift | %FileCheck %S/../Inputs/conditional_conformance_recursive.swift --check-prefix=CHECK --check-prefix=%target-os // Too many pointer-sized integers in the IR // REQUIRES: PTRSIZE=64 diff --git a/test/IRGen/conditional_conformances_future.swift b/test/IRGen/conditional_conformances_future.swift new file mode 100644 index 0000000000000..c4aa7158dec8e --- /dev/null +++ b/test/IRGen/conditional_conformances_future.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-frontend -target %module-target-future -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances_future.swift -DINT=i%target-ptrsize --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -target %module-target-future -emit-ir %S/../Inputs/conditional_conformance_with_assoc.swift | %FileCheck %S/../Inputs/conditional_conformance_with_assoc_future.swift -DINT=i%target-ptrsize --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -target %module-target-future -emit-ir %S/../Inputs/conditional_conformance_subclass.swift | %FileCheck %S/../Inputs/conditional_conformance_subclass_future.swift -DINT=i%target-ptrsize --check-prefix=CHECK --check-prefix=%target-os +// RUN: %target-swift-frontend -target %module-target-future -emit-ir %S/../Inputs/conditional_conformance_recursive.swift | %FileCheck %S/../Inputs/conditional_conformance_recursive.swift -DINT=i%target-ptrsize --check-prefix=CHECK --check-prefix=%target-os + +// Too many pointer-sized integers in the IR +// REQUIRES: PTRSIZE=64 + diff --git a/test/IRGen/conditional_conformances_gettypemetdatabyname.swift b/test/IRGen/conditional_conformances_gettypemetdatabyname.swift index bf602f5920b80..c5fb25a158e3f 100644 --- a/test/IRGen/conditional_conformances_gettypemetdatabyname.swift +++ b/test/IRGen/conditional_conformances_gettypemetdatabyname.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend -target x86_64-apple-macosx10.99 -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=TYPEBYNAME +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -target x86_64-apple-macosx10.99 -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=TYPEBYNAME +// RUN: %target-swift-frontend -target x86_64-apple-macosx10.99 -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift --check-prefix=TYPEBYNAME_PRESPECIALIZED // Too many pointer-sized integers in the IR // REQUIRES: PTRSIZE=64 diff --git a/test/IRGen/foreign_types.sil b/test/IRGen/foreign_types.sil index 0f67dbe105044..be1380db1c630 100644 --- a/test/IRGen/foreign_types.sil +++ b/test/IRGen/foreign_types.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize sil_stage canonical import c_layout diff --git a/test/IRGen/foreign_types_future.sil b/test/IRGen/foreign_types_future.sil new file mode 100644 index 0000000000000..9039e985e4fa7 --- /dev/null +++ b/test/IRGen/foreign_types_future.sil @@ -0,0 +1,43 @@ +// RUN: %target-swift-frontend -target %module-target-future -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +sil_stage canonical +import c_layout + +// CHECK: [[AMAZING_COLOR_NAME:@.*]] = private constant [13 x i8] c"AmazingColor\00" + +// CHECK-LABEL: @"$sSo12AmazingColorVMn" = linkonce_odr hidden constant +// CHECK-SAME: [[AMAZING_COLOR_NAME]] +// CHECK-SAME: @"$sSo12AmazingColorVMa" + +// CHECK: [[HAS_NESTED_UNION_NAME:@.*]] = private constant [15 x i8] c"HasNestedUnion\00" + +// CHECK-LABEL: @"$sSo14HasNestedUnionVMn" = linkonce_odr hidden constant +// CHECK-SAME: [[HAS_NESTED_UNION_NAME]] +// CHECK-SAME: @"$sSo14HasNestedUnionVMa" + +// CHECK-LABEL: @"$sSo14HasNestedUnionV18__Unnamed_struct_sVMf" = linkonce_odr hidden constant +// CHECK-SAME: @"$sSo14HasNestedUnionV18__Unnamed_struct_sVWV" +// CHECK-SAME: [[INT]] 512, +// CHECK-SAME: @"$sSo14HasNestedUnionV18__Unnamed_struct_sVMn" +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 4, +// CHECK-SAME: i64 0 } + +// CHECK-LABEL: @"\01l_type_metadata_table" = private constant +// CHECK-SAME: @"$sSo12AmazingColorVMn" +// CHECK-SAME: @"$sSo14HasNestedUnionVMn" +// CHECK-SAME: @"$sSo14HasNestedUnionV18__Unnamed_struct_sVMn" + +sil @test0 : $() -> () { +bb0: + %0 = metatype $@thick HasNestedUnion.Type + %1 = metatype $@thick AmazingColor.Type + + %ret = tuple () + return %ret : $() +} + diff --git a/test/IRGen/generic_metatypes.swift b/test/IRGen/generic_metatypes.swift index f43d5cfbfa266..86277c13953c1 100644 --- a/test/IRGen/generic_metatypes.swift +++ b/test/IRGen/generic_metatypes.swift @@ -1,15 +1,15 @@ -// RUN: %swift -module-name generic_metatypes -target x86_64-apple-macosx10.9 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s -// RUN: %swift -module-name generic_metatypes -target i386-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s -// RUN: %swift -module-name generic_metatypes -target x86_64-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s -// RUN: %swift -module-name generic_metatypes -target x86_64-apple-tvos9.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s -// RUN: %swift -module-name generic_metatypes -target i386-apple-watchos2.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s -// RUN: %swift -module-name generic_metatypes -target x86_64-unknown-linux-gnu -disable-objc-interop -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s - -// RUN: %swift -module-name generic_metatypes -target armv7-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s -// RUN: %swift -module-name generic_metatypes -target arm64-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s -// RUN: %swift -module-name generic_metatypes -target arm64-apple-tvos9.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s -// RUN: %swift -module-name generic_metatypes -target armv7k-apple-watchos2.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target x86_64-apple-macosx10.9 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target i386-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target x86_64-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target x86_64-apple-tvos9.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target i386-apple-watchos2.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target x86_64-unknown-linux-gnu -disable-objc-interop -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s + +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target armv7-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target arm64-apple-ios7.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target arm64-apple-tvos9.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -disable-generic-metadata-prespecialization -module-name generic_metatypes -target armv7k-apple-watchos2.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s // REQUIRES: CODEGENERATOR=X86 // REQUIRES: CODEGENERATOR=ARM diff --git a/test/IRGen/generic_metatypes_future.swift b/test/IRGen/generic_metatypes_future.swift new file mode 100644 index 0000000000000..363310ac23142 --- /dev/null +++ b/test/IRGen/generic_metatypes_future.swift @@ -0,0 +1,178 @@ + +// RUN: %swift -module-name generic_metatypes -target x86_64-apple-macosx10.99 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -module-name generic_metatypes -target x86_64-apple-ios99.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -module-name generic_metatypes -target x86_64-apple-tvos99.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -module-name generic_metatypes -target i386-apple-watchos9.99 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s +// RUN: %swift -module-name generic_metatypes -target x86_64-unknown-linux-gnu -disable-objc-interop -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s + +// RUN: %swift -module-name generic_metatypes -target arm64-apple-ios99.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -module-name generic_metatypes -target arm64-apple-tvos99.0 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-64 -DINT=i64 %s +// RUN: %swift -module-name generic_metatypes -target armv7k-apple-watchos9.99 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s + + +// REQUIRES: CODEGENERATOR=X86 +// REQUIRES: CODEGENERATOR=ARM + +enum Never {} + +func never() -> Never { return never() } + +@_semantics("typechecker.type(of:)") +public func type(of value: T) -> Metatype { + never() +} + +// CHECK: define hidden swiftcc %swift.type* [[GENERIC_TYPEOF:@"\$s17generic_metatypes0A6TypeofyxmxlF"]](%swift.opaque* noalias nocapture, %swift.type* [[TYPE:%.*]]) +func genericTypeof(_ x: T) -> T.Type { + // CHECK: [[METATYPE:%.*]] = call %swift.type* @swift_getDynamicType(%swift.opaque* {{.*}}, %swift.type* [[TYPE]], i1 false) + // CHECK: ret %swift.type* [[METATYPE]] + return type(of: x) +} + +struct Foo {} +class Bar {} + +// CHECK-LABEL: define hidden swiftcc %swift.type* @"$s17generic_metatypes27remapToSubstitutedMetatypes{{.*}}"(%T17generic_metatypes3BarC*) {{.*}} { +func remapToSubstitutedMetatypes(_ x: Foo, y: Bar) + -> (Foo.Type, Bar.Type) +{ + // CHECK: call swiftcc %swift.type* [[GENERIC_TYPEOF]](%swift.opaque* noalias nocapture undef, %swift.type* {{.*}} @"$s17generic_metatypes3FooVMf", {{.*}}) + // CHECK: [[BAR_REQUEST:%.*]] = call {{.*}}@"$s17generic_metatypes3BarCMa" + // CHECK: [[BAR:%.*]] = extractvalue {{.*}} [[BAR_REQUEST]] + // CHECK: [[BAR_META:%.*]] = call swiftcc %swift.type* [[GENERIC_TYPEOF]](%swift.opaque* noalias nocapture {{%.*}}, %swift.type* [[BAR]]) + // CHECK: ret %swift.type* [[BAR_META]] + return (genericTypeof(x), genericTypeof(y)) +} + + +// CHECK-LABEL: define hidden swiftcc void @"$s17generic_metatypes23remapToGenericMetatypesyyF"() +func remapToGenericMetatypes() { + // CHECK: [[BAR_REQUEST:%.*]] = call {{.*}}@"$s17generic_metatypes3BarCMa" + // CHECK: [[BAR:%.*]] = extractvalue {{.*}} [[BAR_REQUEST]] + // CHECK: call swiftcc void @"$s17generic_metatypes0A9Metatypes{{.*}}"(%swift.type* {{.*}} @"$s17generic_metatypes3FooVMf", {{.*}} %swift.type* [[BAR]], %swift.type* {{.*}} @"$s17generic_metatypes3FooVMf", {{.*}} %swift.type* [[BAR]]) + genericMetatypes(Foo.self, Bar.self) +} + +func genericMetatypes(_ t: T.Type, _ u: U.Type) {} + +protocol Bas {} + +// CHECK: define hidden swiftcc { %swift.type*, i8** } @"$s17generic_metatypes14protocolTypeof{{.*}}"(%T17generic_metatypes3BasP* noalias nocapture dereferenceable({{.*}})) +func protocolTypeof(_ x: Bas) -> Bas.Type { + // CHECK: [[METADATA_ADDR:%.*]] = getelementptr inbounds %T17generic_metatypes3BasP, %T17generic_metatypes3BasP* [[X:%.*]], i32 0, i32 1 + // CHECK: [[METADATA:%.*]] = load %swift.type*, %swift.type** [[METADATA_ADDR]] + // CHECK: [[BUFFER:%.*]] = bitcast %T17generic_metatypes3BasP* [[X]] to %__opaque_existential_type_1* + // CHECK: [[VALUE_ADDR:%.*]] = call %swift.opaque* @__swift_project_boxed_opaque_existential_1(%__opaque_existential_type_1* [[BUFFER]], %swift.type* [[METADATA]]) + // CHECK: [[METATYPE:%.*]] = call %swift.type* @swift_getDynamicType(%swift.opaque* [[VALUE_ADDR]], %swift.type* [[METADATA]], i1 true) + // CHECK: [[WTABLE_ADDR:%.*]] = getelementptr inbounds %T17generic_metatypes3BasP, %T17generic_metatypes3BasP* %0, i32 0, i32 2 + // CHECK: [[WTABLE:%.*]] = load i8**, i8*** [[WTABLE_ADDR]] + // CHECK-NOT: call void @__swift_destroy_boxed_opaque_existential_1(%T17generic_metatypes3BasP* %0) + // CHECK: [[T0:%.*]] = insertvalue { %swift.type*, i8** } undef, %swift.type* [[METATYPE]], 0 + // CHECK: [[T1:%.*]] = insertvalue { %swift.type*, i8** } [[T0]], i8** [[WTABLE]], 1 + // CHECK: ret { %swift.type*, i8** } [[T1]] + return type(of: x) +} + +struct Zim : Bas {} +class Zang : Bas {} + +// CHECK-LABEL: define hidden swiftcc { %swift.type*, i8** } @"$s17generic_metatypes15metatypeErasureyAA3Bas_pXpAA3ZimVmF"() #0 +func metatypeErasure(_ z: Zim.Type) -> Bas.Type { + // CHECK: ret { %swift.type*, i8** } {{.*}} @"$s17generic_metatypes3ZimVMf", {{.*}} @"$s17generic_metatypes3ZimVAA3BasAAWP" + return z +} + +// CHECK-LABEL: define hidden swiftcc { %swift.type*, i8** } @"$s17generic_metatypes15metatypeErasureyAA3Bas_pXpAA4ZangCmF"(%swift.type*) #0 +func metatypeErasure(_ z: Zang.Type) -> Bas.Type { + // CHECK: [[RET:%.*]] = insertvalue { %swift.type*, i8** } undef, %swift.type* %0, 0 + // CHECK: [[RET2:%.*]] = insertvalue { %swift.type*, i8** } [[RET]], i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s17generic_metatypes4ZangCAA3BasAAWP", i32 0, i32 0), 1 + // CHECK: ret { %swift.type*, i8** } [[RET2]] + return z +} + +struct OneArg {} +struct TwoArgs {} +struct ThreeArgs {} +struct FourArgs {} +struct FiveArgs {} + +func genericMetatype(_ x: A.Type) {} + +// CHECK-LABEL: define hidden swiftcc void @"$s17generic_metatypes20makeGenericMetatypesyyF"() {{.*}} { +func makeGenericMetatypes() { + // CHECK: call swiftcc void @"$s17generic_metatypes0A8MetatypeyyxmlF"( + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s17generic_metatypes6OneArgVyAA3FooVGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ), + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, + // CHECK-SAME: %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s17generic_metatypes6OneArgVyAA3FooVGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ) + // CHECK-SAME: ) + genericMetatype(OneArg.self) + + // CHECK: call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$s17generic_metatypes7TwoArgsVyAA3FooVAA3BarCGMD") [[NOUNWIND_READNONE:#[0-9]+]] + genericMetatype(TwoArgs.self) + + // CHECK: call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$s17generic_metatypes9ThreeArgsVyAA3FooVAA3BarCAEGMD") [[NOUNWIND_READNONE]] + genericMetatype(ThreeArgs.self) + + // CHECK: call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$s17generic_metatypes8FourArgsVyAA3FooVAA3BarCAeGGMD") [[NOUNWIND_READNONE]] + genericMetatype(FourArgs.self) + + // CHECK: call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$s17generic_metatypes8FiveArgsVyAA3FooVAA3BarCAegEGMD") [[NOUNWIND_READNONE]] + genericMetatype(FiveArgs.self) +} + +// CHECK-LABEL: define hidden swiftcc %swift.metadata_response @"$s17generic_metatypes6OneArgVMa" +// CHECK-SAME: ([[INT]], %swift.type*) +// CHECK: [[BITCAST_1:%.*]] = bitcast {{.*}} %1 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[BITCAST_1]], i8* undef, i8* undef, %swift.type_descriptor* {{.*}} @"$s17generic_metatypes6OneArgVMn" {{.*}}) +// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK-LABEL: define hidden swiftcc %swift.metadata_response @"$s17generic_metatypes7TwoArgsVMa" +// CHECK-SAME: ([[INT]], %swift.type*, %swift.type*) +// CHECK: [[BITCAST_1:%.*]] = bitcast {{.*}} %1 +// CHECK: [[BITCAST_2:%.*]] = bitcast {{.*}} %2 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[BITCAST_1]], i8* [[BITCAST_2]], i8* undef, %swift.type_descriptor* {{.*}} @"$s17generic_metatypes7TwoArgsVMn" {{.*}}) +// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK-LABEL: define hidden swiftcc %swift.metadata_response @"$s17generic_metatypes9ThreeArgsVMa" +// CHECK-SAME: ({{i[0-9]+}}, %swift.type*, %swift.type*, %swift.type*) +// CHECK: [[BITCAST_1:%.*]] = bitcast {{.*}} %1 +// CHECK: [[BITCAST_2:%.*]] = bitcast {{.*}} %2 +// CHECK: [[BITCAST_3:%.*]] = bitcast {{.*}} %3 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @__swift_instantiateGenericMetadata([[INT]] %0, i8* [[BITCAST_1]], i8* [[BITCAST_2]], i8* [[BITCAST_3]], %swift.type_descriptor* {{.*}} @"$s17generic_metatypes9ThreeArgsVMn" {{.*}}) +// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK-LABEL: define hidden swiftcc %swift.metadata_response @"$s17generic_metatypes8FiveArgsVMa" +// CHECK-SAME: ([[INT]], i8**) [[NOUNWIND_OPT:#[0-9]+]] +// CHECK-NOT: alloc +// CHECK: call swiftcc %swift.metadata_response @swift_getGenericMetadata([[INT]] %0, i8* {{.*}}, %swift.type_descriptor* {{.*}} @"$s17generic_metatypes8FiveArgsVMn" {{.*}}) +// CHECK-NOT: call void @llvm.lifetime.end +// CHECK: ret %swift.metadata_response + +// CHECK-DAG: attributes [[NOUNWIND_READNONE]] = { nounwind readnone } +// CHECK-DAG: attributes [[NOUNWIND_OPT]] = { noinline nounwind "frame-pointer"="none" "target-cpu" diff --git a/test/IRGen/generic_structs.sil b/test/IRGen/generic_structs.sil index 77dd4817e79bd..618a91ad82106 100644 --- a/test/IRGen/generic_structs.sil +++ b/test/IRGen/generic_structs.sil @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %{python} %utils/chex.py < %s > %t/generic_structs.sil -// RUN: %target-swift-frontend %t/generic_structs.sil -emit-ir | %FileCheck %t/generic_structs.sil +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization %t/generic_structs.sil -emit-ir | %FileCheck %t/generic_structs.sil // REQUIRES: CPU=x86_64 diff --git a/test/IRGen/generic_structs_future.sil b/test/IRGen/generic_structs_future.sil new file mode 100644 index 0000000000000..772419f0aa677 --- /dev/null +++ b/test/IRGen/generic_structs_future.sil @@ -0,0 +1,261 @@ +// RUN: %empty-directory(%t) +// RUN: %{python} %utils/chex.py < %s > %t/generic_structs_future.sil +// RUN: %target-swift-frontend -target %module-target-future %t/generic_structs_future.sil -emit-ir | %FileCheck %t/generic_structs_future.sil + +// REQUIRES: CPU=x86_64 + +import Builtin + +// -- Generic structs with fixed layout should have no completion function +// and emit the field offset vector as part of the pattern. +// CHECK: [[PATTERN:@.*]] = internal constant [4 x i32] [i32 0, i32 1, i32 8, i32 0] +// CHECK-LABEL: @"$s22generic_structs_future18FixedLayoutGenericVMP" = internal constant <{ {{.*}} }> <{ +// -- instantiation function +// CHECK-SAME: %swift.type* (%swift.type_descriptor*, i8**, i8*)* @"$s22generic_structs_future18FixedLayoutGenericVMi" +// -- completion function +// CHECK-SAME: i32 0, +// -- pattern flags +// CHECK-SAME: , +// -- vwtable pointer +// CHECK-SAME: @"$s22generic_structs_future18FixedLayoutGenericVWV" +// -- extra data pattern +// CHECK-SAME: [4 x i32]* [[PATTERN]] +// CHECK-SAME: i16 1, +// CHECK-SAME: i16 2 }> + +// -- Generic structs with dynamic layout contain the vwtable pattern as part +// of the metadata pattern, and no independent vwtable symbol +// CHECK: @"$s22generic_structs_future13SingleDynamicVWV" = internal constant +// CHECK-SAME: i8* bitcast (%swift.opaque* ([24 x i8]*, [24 x i8]*, %swift.type*)* @"$s22generic_structs_future13SingleDynamicVwCP" to i8*), +// -- ... +// -- placeholder for size, stride, flags +// CHECK-SAME: i64 0, +// CHECK-SAME: i64 0, +// CHECK-SAME: i32 4194304, +// CHECK-SAME: i32 0 } + +// FIXME: Strings should be unnamed_addr. rdar://problem/22674524 +// CHECK: [[SINGLEDYNAMIC_NAME:@.*]] = private constant [14 x i8] c"SingleDynamic\00" +// CHECK: @"$s22generic_structs_future13SingleDynamicVMn" = hidden constant +// -- flags: struct, unique, generic +// CHECK-SAME: +// -- name +// CHECK-SAME: [14 x i8]* [[SINGLEDYNAMIC_NAME]] +// -- field count +// CHECK-SAME: i32 1, +// -- field offset vector offset +// CHECK-SAME: i32 3, +// -- generic instantiation info +// CHECK-SAME: [{{[0-9]+}} x i8*]* @"$s22generic_structs_future13SingleDynamicVMI" +// CHECK-SAME: @"$s22generic_structs_future13SingleDynamicVMP" +// -- generic params, requirements, key args, extra args +// CHECK-SAME: i16 1, i16 0, i16 1, i16 0 +// -- generic parameters +// CHECK-SAME: +// CHECK-SAME: }> +// CHECK: @"$s22generic_structs_future13SingleDynamicVMP" = internal constant <{ {{.*}} }> <{ +// -- instantiation function +// CHECK-SAME: %swift.type* (%swift.type_descriptor*, i8**, i8*)* @"$s22generic_structs_future13SingleDynamicVMi" +// -- vwtable pointer +// CHECK-SAME: @"$s22generic_structs_future13SingleDynamicVWV" + +// -- Nominal type descriptor for generic struct with protocol requirements +// FIXME: Strings should be unnamed_addr. rdar://problem/22674524 +// CHECK: [[DYNAMICWITHREQUIREMENTS_NAME:@.*]] = private constant [24 x i8] c"DynamicWithRequirements\00" +// CHECK: @"$s22generic_structs_future23DynamicWithRequirementsVMn" = hidden constant <{ {{.*}} i32 }> <{ +// -- flags: struct, unique, generic +// CHECK-SAME: +// -- name +// CHECK-SAME: [24 x i8]* [[DYNAMICWITHREQUIREMENTS_NAME]] +// -- field count +// CHECK-SAME: i32 2, +// -- field offset vector offset +// CHECK-SAME: i32 6, +// -- generic params, requirements, key args, extra args +// CHECK-SAME: i16 2, i16 2, i16 4, i16 0, +// -- generic parameters +// CHECK-SAME: , , +// -- generic requirement +// -- protocol requirement with key arg +// CHECK-SAME: i32 128 +// -- param 0 +// CHECK-SAME: i32 0 +// -- protocol Req1 +// CHECK-SAME: @"$s22generic_structs_future4Req1Mp" + +// -- protocol requirement with key arg +// CHECK-SAME: i32 128 +// -- param 1 +// CHECK-SAME: i32 2 +// -- protocol Req2 +// CHECK-SAME: @"$s22generic_structs_future4Req2Mp" +// CHECK-SAME: }> + +// CHECK: @"$s22generic_structs_future23DynamicWithRequirementsVMP" = internal constant <{ {{.*}} }> <{ + +// -- Fixed-layout struct metadata contains fixed field offsets +// CHECK: @"$s22generic_structs_future6IntishVMf" = internal constant <{ {{.*}} i32, [4 x i8], i64 }> <{ +// CHECK-SAME: i32 0 +// CHECK-SAME: }> +// CHECK: @"$s22generic_structs_future7CharethVMf" = internal constant <{ {{.*}} i32, [4 x i8], i64 }> <{ +// CHECK-SAME: i32 0 +// CHECK-SAME: }> +// CHECK: @"$s22generic_structs_future8StringlyVMf" = internal constant <{ {{.*}} i32, i32, i32, [4 x i8], i64 }> <{ +// CHECK-SAME: i32 0, i32 8, i32 16, [4 x i8] zeroinitializer +// CHECK-SAME: }> + +struct FixedLayoutGeneric { + var x: Byteful + var y: Byteful + var z: Intish +} + +struct SingleDynamic { + var x : T +} + +protocol Req1 { associatedtype Assoc1 } +protocol Req2 {} + +struct DynamicWithRequirements { + var x : T + var y : U +} + +struct Intish { var value : Builtin.Int64 } +struct Chareth { var value : Builtin.Int21 } +struct Byteful { var value : Builtin.Int8 } +struct Stringly { + var owner : Builtin.NativeObject + var base : Builtin.RawPointer + var size : Builtin.Int64 +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { i64, i32 } @concrete_instances(i64, i32) {{.*}} { +// CHECK: entry: +// CHECK: %2 = trunc i32 %1 to i21 +// CHECK: %3 = zext i21 %2 to i32 +// CHECK: %4 = insertvalue { i64, i32 } undef, i64 %0, 0 +// CHECK: %5 = insertvalue { i64, i32 } %4, i32 %3, 1 +// CHECK: ret { i64, i32 } %5 +// CHECK: } +sil @concrete_instances : $(SingleDynamic, SingleDynamic) -> (Intish, Chareth) { +entry(%0 : $SingleDynamic, %1 : $SingleDynamic): + %a = struct_extract %0 : $SingleDynamic, #SingleDynamic.x + %b = struct_extract %1 : $SingleDynamic, #SingleDynamic.x + %c = tuple (%a : $Intish, %b : $Chareth) + return %c : $(Intish, Chareth) +} + +struct ComplexDynamic { + var a, a2 : Byteful + var b : U + var c : SingleDynamic + var d : Chareth +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @explode_complex_dynamic +sil @explode_complex_dynamic : $ (@in ComplexDynamic, @inout Byteful, @inout A, @inout B, @inout Chareth) -> () { +entry(%0 : $*ComplexDynamic, %1 : $*Byteful, %2 : $*A, %3 : $*B, %4 : $*Chareth): + %a = struct_element_addr %0 : $*ComplexDynamic, #ComplexDynamic.a2 + + // CHECK: [[METADATA:%.*]] = bitcast %swift.type* {{%.*}} to i32* + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 10 + // CHECK: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_ADDR]], align 8 + // CHECK: [[BYTES:%.*]] = bitcast %T22generic_structs_future14ComplexDynamicV* %0 to i8* + // CHECK: [[BYTE_OFFSET:%.*]] = getelementptr inbounds i8, i8* [[BYTES]], i32 [[FIELD_OFFSET]] + // CHECK: bitcast i8* [[BYTE_OFFSET]] to %swift.opaque* + %b = struct_element_addr %0 : $*ComplexDynamic, #ComplexDynamic.b + + // CHECK: [[METADATA:%.*]] = bitcast %swift.type* {{%.*}} to i32* + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 11 + // CHECK: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_ADDR]], align 8 + // CHECK: [[BYTES:%.*]] = bitcast %T22generic_structs_future14ComplexDynamicV* %0 to i8* + // CHECK: [[BYTE_OFFSET:%.*]] = getelementptr inbounds i8, i8* [[BYTES]], i32 [[FIELD_OFFSET]] + // CHECK: bitcast i8* [[BYTE_OFFSET]] to %T22generic_structs_future13SingleDynamicV + %5 = struct_element_addr %0 : $*ComplexDynamic, #ComplexDynamic.c + %c = struct_element_addr %5 : $*SingleDynamic, #SingleDynamic.x + + // CHECK: [[METADATA:%.*]] = bitcast %swift.type* {{%.*}} to i32* + // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i32, i32* [[METADATA]], i64 12 + // CHECK: [[FIELD_OFFSET:%.*]] = load i32, i32* [[FIELD_OFFSET_ADDR]], align 8 + // CHECK: [[BYTES:%.*]] = bitcast %T22generic_structs_future14ComplexDynamicV* %0 to i8* + // CHECK: [[BYTE_OFFSET:%.*]] = getelementptr inbounds i8, i8* [[BYTES]], i32 [[FIELD_OFFSET]] + // CHECK: bitcast i8* [[BYTE_OFFSET]] to %T22generic_structs_future7CharethV + %d = struct_element_addr %0 : $*ComplexDynamic, #ComplexDynamic.d + copy_addr %a to %1 : $*Byteful + copy_addr %b to %2 : $*A + copy_addr %c to %3 : $*B + copy_addr %d to %4 : $*Chareth + %v = tuple () + return %v : $() +} + +// CHECK-LABEL: define{{( protected)?}} internal %swift.type* @"$s22generic_structs_future13SingleDynamicVMi"(%swift.type_descriptor*, i8**, i8*) +// CHECK: [[T0:%.*]] = bitcast i8** %1 to %swift.type** +// CHECK: %T = load %swift.type*, %swift.type** [[T0]], align 8 +// CHECK: [[METADATA:%.*]] = call %swift.type* @swift_allocateGenericValueMetadata(%swift.type_descriptor* %0, i8** %1, i8* %2, i64 24) +// CHECK-NEXT: ret %swift.type* [[METADATA]] +// CHECK: } + +// CHECK-LABEL: define{{( protected)?}} internal swiftcc %swift.metadata_response @"$s22generic_structs_future13SingleDynamicVMr" +// CHECK-SAME: (%swift.type* [[METADATA:%.*]], i8*, i8**) {{.*}} { +// Lay out fields. +// CHECK: [[T0:%.*]] = bitcast %swift.type* [[METADATA]] to i32* +// CHECK: [[T1:%.*]] = getelementptr inbounds i32, i32* [[T0]], i64 6 +// CHECK: [[T2:%.*]] = getelementptr inbounds i8**, i8*** [[TYPES:%.*]], i32 0 +// CHECK: call void @swift_initStructMetadata(%swift.type* [[METADATA]], i64 0, i64 1, i8*** [[TYPES]], i32* [[T1]]) +// CHECK: ret %swift.metadata_response +// CHECK: } + +// Check that we directly delegate buffer witnesses to a single dynamic field: + +// initializeBufferWithCopyOfBuffer +// CHECK-LABEL: define internal %swift.opaque* @"$s22generic_structs_future13SingleDynamicVwCP"([24 x i8]* noalias %dest, [24 x i8]* noalias %src, %swift.type* %"SingleDynamic") {{.*}} { +// CHECK: %T = load %swift.type*, +// CHECK: [[T0:%.*]] = bitcast %swift.type* %T to i8*** +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], i64 -1 +// CHECK-NEXT: %T.valueWitnesses = load i8**, i8*** [[T1]] +// CHECK-NEXT: [[T1:%.*]] = load i8*, i8** %T.valueWitnesses, +// CHECK-NEXT: [[FN:%.*]] = bitcast i8* [[T1]] to %swift.opaque* ([24 x i8]*, [24 x i8]*, %swift.type*)* +// CHECK-NEXT: [[T0:%.*]] = call %swift.opaque* [[FN]]([24 x i8]* noalias %dest, [24 x i8]* noalias %src, %swift.type* %T) +// CHECK-NEXT: [[T1:%.*]] = bitcast %swift.opaque* [[T0]] to {{.*}} +// CHECK-NEXT: [[T2:%.*]] = bitcast {{.*}} [[T1]] to %swift.opaque* +// CHECK-NEXT: ret %swift.opaque* [[T2]] + + +protocol HasAssociatedType { + associatedtype Assoc +} +protocol ParentHasAssociatedType : HasAssociatedType { + associatedtype Assoc : HasAssociatedType +} + +struct GenericLayoutWithAssocType { + var x: T.Assoc + var y: T.Assoc.Assoc +} +// CHECK-LABEL: define internal %swift.type* @"$s22generic_structs_future26GenericLayoutWithAssocTypeVMi"( +// CHECK: [[T0:%.*]] = bitcast i8** %1 to %swift.type** +// CHECK: %T = load %swift.type*, %swift.type** [[T0]], align 8 +// CHECK: [[T1:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T0]], i32 1 +// CHECK: [[T2:%.*]] = bitcast %swift.type** [[T1]] to i8*** +// CHECK: %T.ParentHasAssociatedType = load i8**, i8*** [[T2]], + +// CHECK: [[METADATA:%.*]] = call %swift.type* @swift_allocateGenericValueMetadata + +// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$s22generic_structs_future26GenericLayoutWithAssocTypeVMr"( + +// CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState(i64 0, %swift.type* %T) +// CHECK: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK: [[T0_GEP:%.*]] = getelementptr inbounds i8*, i8** %T.ParentHasAssociatedType, i32 1 +// CHECK: [[T0:%.*]] = load i8*, i8** [[T0_GEP]] +// CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to i8** +// CHECK: [[T4:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(i64 0, i8** %T.HasAssociatedType, %swift.type* %T, %swift.protocol_requirement* @"$s22generic_structs_future17HasAssociatedTypeTL", %swift.protocol_requirement* @"$s5Assoc22generic_structs_future17HasAssociatedTypePTl") + +// CHECK: %T.Assoc = extractvalue %swift.metadata_response [[T4]], 0 +// CHECK: %T.Assoc.HasAssociatedType = call swiftcc i8** @swift_getAssociatedConformanceWitness(i8** %T.ParentHasAssociatedType, %swift.type* %T, %swift.type* %T.Assoc, + +// CHECK: [[T2:%.*]] = call swiftcc %swift.metadata_response @swift_getAssociatedTypeWitness(i64 0, i8** %T.Assoc.HasAssociatedType, %swift.type* %T.Assoc, %swift.protocol_requirement* @"$s22generic_structs_future17HasAssociatedTypeTL", %swift.protocol_requirement* @"$s5Assoc22generic_structs_future17HasAssociatedTypePTl") +// CHECK: %T.Assoc.Assoc = extractvalue %swift.metadata_response [[T2]], 0 diff --git a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift index 8c77d62e50823..dbbf31395e0ae 100644 --- a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -parse-stdlib -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift index b1dff106c6518..4f35766fc6205 100644 --- a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift index 9a5f1d7f91bef..01d8958d36005 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift index 13da1ee56a613..e5ae3c72e67a3 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift index 42dff49c51270..38726367123ce 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift index 3848a109a2764..1564106debd13 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift index 054f4cf84b7ea..c9a18506caa2d 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift index 048a8db3dc457..feff292a16633 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift index a795c6e59df83..01b74ed23ad7a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift index 0d24ee487f082..685b2c15e113a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift index ef389a11c716c..eedd046af82cb 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift index 521dd19ffe7d2..5fa79b3afab95 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift index 3102bc9930274..e8f4767c3ebd2 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift index f3d26c4aa84b6..26f12e2651200 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift index ca964defcb8fd..94eb0802f846f 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift index e01d083d6cb94..474315005252b 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift index 0680062c7c2f7..88d9a94fa7843 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift index 1786c68b9a46e..a612984c89ee6 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift index 1c9b9fd78db4d..9155b1914cfcf 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift index abc8ff26c07d9..0006020cf3ada 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift index 9b7ff2a4cf9f9..69289ba0b1c52 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift @@ -11,7 +11,7 @@ // CHECK-SAME: %swift.type*, // CHECK-SAME: %swift.type*, // CHECK-SAME: i32, -// CHECK-SAME: {{(\[4 x i8\])?}}, +// CHECK-SAME: {{(\[4 x i8\],)?}} // CHECK-SAME: i64 // CHECK-SAME: }> <{ // i8** getelementptr inbounds ( @@ -29,7 +29,7 @@ // CHECK-SAME: %swift.type* @"$sSiN", // CHECK-SAME: %swift.type* @"$sSSN", // CHECK-SAME: i32 0, -// CHECK-SAME: {{(\[4 x i8\] zeroinitializer)?}}, +// CHECK-SAME: {{(\[4 x i8\] zeroinitializer,)?}} // CHECK-SAME: i64 3 // CHECK-SAME: }>, // CHECK-SAME: align [[ALIGNMENT]] @@ -60,7 +60,7 @@ func consume(_ t: T) { // CHECK-SAME: %swift.type_descriptor*, // CHECK-SAME: %swift.type*, // CHECK-SAME: i32, -// CHECK-SAME: {{(\[4 x i8\])?}}, +// CHECK-SAME: {{(\[4 x i8\],)?}} // CHECK-SAME: i64 // CHECK-SAME: }>* @"$s4main9NamespaceVAAq_RszrlE5ValueVyS2i_SSGMf" // CHECK-SAME: to %swift.full_type* @@ -99,7 +99,7 @@ doit() // CHECK-SAME: %swift.type*, // CHECK-SAME: %swift.type*, // CHECK-SAME: i32, -// CHECK-SAME: {{(\[4 x i8\])?}}, +// CHECK-SAME: {{(\[4 x i8\],)?}} // CHECK-SAME: i64 // CHECK-SAME: }>* @"$s4main9NamespaceVAAq_RszrlE5ValueVyS2i_SSGMf" // CHECK-SAME: to %swift.full_type* diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift index 00c7687d1ea0c..88cc65b0f2a73 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift index 8fd6839978295..680cd10186adb 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift index c8a714665d342..022675f3bb28c 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift index ce3a93ad70ff4..be834acba381b 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift index fe4e7d319c0e3..96c665dc678cb 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift index ae00a9425fbdb..3afa79e005cbc 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift index 77c31c0675727..1f49e91840efb 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift index c51b6b64eb0d3..4569fd17af210 100644 --- a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift @@ -1,4 +1,4 @@ -// RUN: %swift -target %module-target-future -emit-ir -prespecialize-generic-metadata %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios diff --git a/test/IRGen/signature_conformances_multifile.swift b/test/IRGen/signature_conformances_multifile.swift index 04467441de505..3b6333fa3b87f 100644 --- a/test/IRGen/signature_conformances_multifile.swift +++ b/test/IRGen/signature_conformances_multifile.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-ir -primary-file %s %S/Inputs/signature_conformances_other.swift | %FileCheck %s +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -emit-ir -primary-file %s %S/Inputs/signature_conformances_other.swift | %FileCheck %s // Make sure we correctly determine the witness table is dependent, even though // it was defined in a different file. diff --git a/test/IRGen/signature_conformances_multifile_future.swift b/test/IRGen/signature_conformances_multifile_future.swift new file mode 100644 index 0000000000000..b1514034b3ba9 --- /dev/null +++ b/test/IRGen/signature_conformances_multifile_future.swift @@ -0,0 +1,65 @@ +// RUN: %target-swift-frontend -target %module-target-future -emit-ir -primary-file %s %S/Inputs/signature_conformances_other.swift | %FileCheck %s -DINT=i%target-ptrsize + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +// Make sure we correctly determine the witness table is dependent, even though +// it was defined in a different file. + +// CHECK-LABEL: define hidden swiftcc void @"$s39signature_conformances_multifile_future5passQyyF"() +func passQ() { + // CHECK: call swiftcc void @"$s39signature_conformances_multifile_future12AlsoConformsVACyxGycfC"(%swift.type* @"$sSiN") + // CHECK: [[WITNESS_TABLE:%[0-9]+]] = call i8** @"$s39signature_conformances_multifile_future12AlsoConformsVySiGACyxGAA1QAAWl"() + // CHECK: call swiftcc void @"$s39signature_conformances_multifile_future6takesQyyxAA1QRzlF"( + // CHECK-SAME: %swift.opaque* noalias nocapture undef, + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, + // CHECK-SAME: %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s39signature_conformances_multifile_future12AlsoConformsVySiGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ), + // CHECK-SAME: i8** [[WITNESS_TABLE]] + // CHECK-SAME: ) + takesQ(AlsoConforms()) + + // CHECK: ret void +} + +// CHECK-LABEL: define hidden swiftcc void @"$s39signature_conformances_multifile_future5passPyyF"() +func passP() { + // CHECK: call swiftcc void @"$s39signature_conformances_multifile_future8ConformsVACyxq_GycfC"(%swift.type* @"$sSiN", %swift.type* @"$sSSN") + // CHECK: [[WITNESS_TABLE:%[0-9]+]] = call i8** @"$s39signature_conformances_multifile_future8ConformsVySiSSGACyxq_GAA1PAAWl"() + // CHECK: call swiftcc void @"$s39signature_conformances_multifile_future6takesPyyxAA1PRzlF"( + // CHECK-SAME: %swift.opaque* noalias nocapture undef, + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, + // CHECK-SAME: %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s39signature_conformances_multifile_future8ConformsVySiSSGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ), + // CHECK-SAME: i8** [[WITNESS_TABLE]] + // CHECK-SAME: ) + takesP(Conforms()) + + // CHECK: ret void +} diff --git a/test/IRGen/synthesized_conformance.swift b/test/IRGen/synthesized_conformance.swift index 855ac8ca6df2b..750303b705cae 100644 --- a/test/IRGen/synthesized_conformance.swift +++ b/test/IRGen/synthesized_conformance.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-ir %s -swift-version 4 | %FileCheck %s +// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -emit-ir %s -swift-version 4 | %FileCheck %s struct Struct { var x: T diff --git a/test/IRGen/synthesized_conformance_future.swift b/test/IRGen/synthesized_conformance_future.swift new file mode 100644 index 0000000000000..b71c03a18dd4e --- /dev/null +++ b/test/IRGen/synthesized_conformance_future.swift @@ -0,0 +1,100 @@ +// RUN: %target-swift-frontend -target %module-target-future -emit-ir %s -swift-version 4 | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment + +// UNSUPPORTED: CPU=i386 && OS=ios +// UNSUPPORTED: CPU=armv7 && OS=ios +// UNSUPPORTED: CPU=armv7s && OS=ios + +struct Struct { + var x: T +} + +extension Struct: Equatable where T: Equatable {} +extension Struct: Hashable where T: Hashable {} +extension Struct: Codable where T: Codable {} + +enum Enum { + case a(T), b(T) +} + +extension Enum: Equatable where T: Equatable {} +extension Enum: Hashable where T: Hashable {} + +final class Final { + var x: T + init(x: T) { self.x = x } +} + +extension Final: Encodable where T: Encodable {} +extension Final: Decodable where T: Decodable {} + +class Nonfinal { + var x: T + init(x: T) { self.x = x } +} +extension Nonfinal: Encodable where T: Encodable {} + +func doEquality(_: T) {} +// CHECK-LABEL: define{{( dllexport| protected)?}} swiftcc void @"$s30synthesized_conformance_future8equalityyyF"() +public func equality() { + // CHECK: [[Struct_Equatable:%.*]] = call i8** @"$s30synthesized_conformance_future6StructVySiGACyxGSQAASQRzlWl"() + + // CHECK-NEXT: call swiftcc void @"$s30synthesized_conformance_future10doEqualityyyxSQRzlF"( + // CHECK-SAME: %swift.opaque* noalias nocapture {{[^,]*}}, + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, + // CHECK-SAME: %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i32, + // CHECK-SAME: {{(\[4 x i8\],)?}} + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s30synthesized_conformance_future6StructVySiGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ), + // CHECK-SAME: i8** [[Struct_Equatable]] + // CHECK-SAME: ) + doEquality(Struct(x: 1)) + // CHECK: [[Enum_Equatable:%.*]] = call i8** @"$s30synthesized_conformance_future4EnumOySiGACyxGSQAASQRzlWl"() + // CHECK-NEXT: call swiftcc void @"$s30synthesized_conformance_future10doEqualityyyxSQRzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Enum_Equatable]]) + doEquality(Enum.a(1)) +} + +func doEncodable(_: T) {} +// CHECK-LABEL: define{{( dllexport| protected)?}} swiftcc void @"$s30synthesized_conformance_future9encodableyyF"() +public func encodable() { + // CHECK: [[Struct_Encodable:%.*]] = call i8** @"$s30synthesized_conformance_future6StructVySiGACyxGSEAASeRzSERzlWl"() + // CHECK-NEXT: call swiftcc void @"$s30synthesized_conformance_future11doEncodableyyxSERzlF"( + // CHECK-SAME: %swift.opaque* noalias nocapture {{[^,]*}}, + // CHECK-SAME: %swift.type* getelementptr inbounds ( + // CHECK-SAME: %swift.full_type, + // CHECK-SAME: %swift.full_type* bitcast ( + // CHECK-SAME: <{ + // CHECK-SAME: i8**, + // CHECK-SAME: [[INT]], + // CHECK-SAME: %swift.type_descriptor*, + // CHECK-SAME: %swift.type*, + // CHECK-SAME: i32, + // CHECK-SAME: {{(\[4 x i8\],)?}} + // CHECK-SAME: i64 + // CHECK-SAME: }>* @"$s30synthesized_conformance_future6StructVySiGMf" + // CHECK-SAME: to %swift.full_type* + // CHECK-SAME: ), + // CHECK-SAME: i32 0, + // CHECK-SAME: i32 1 + // CHECK-SAME: ), + // CHECK-SAME: i8** [[Struct_Encodable]] + // CHECK-SAME: ) + doEncodable(Struct(x: 1)) + // CHECK: [[Final_Encodable:%.*]] = call i8** @"$s30synthesized_conformance_future5FinalCySiGACyxGSEAASERzlWl"() + // CHECK-NEXT: call swiftcc void @"$s30synthesized_conformance_future11doEncodableyyxSERzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Final_Encodable]]) + doEncodable(Final(x: 1)) + // CHECK: [[Nonfinal_Encodable:%.*]] = call i8** @"$s30synthesized_conformance_future8NonfinalCySiGACyxGSEAASERzlWl"() + // CHECK-NEXT: call swiftcc void @"$s30synthesized_conformance_future11doEncodableyyxSERzlF"(%swift.opaque* noalias nocapture {{%.*}}, %swift.type* {{%.*}}, i8** [[Nonfinal_Encodable]]) + doEncodable(Nonfinal(x: 1)) +} diff --git a/test/Inputs/conditional_conformance_basic_conformances.swift b/test/Inputs/conditional_conformance_basic_conformances.swift index bfba78057fc8e..7743db428d8a8 100644 --- a/test/Inputs/conditional_conformance_basic_conformances.swift +++ b/test/Inputs/conditional_conformance_basic_conformances.swift @@ -72,6 +72,45 @@ public func single_concrete() { // CHECK-NEXT: ret void // CHECK-NEXT: } +// CHECK-PRESPECIALIZED: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s42conditional_conformance_basic_conformances15single_concreteyyF"() +// CHECK-PRESPECIALIZED-NEXT: entry: +// CHECK-PRESPECIALIZED-NEXT: [[Single_P1:%.*]] = call i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// CHECK-PRESPECIALIZED-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances8takes_p1yyxmAA2P1RzlF"( +// CHECK-PRESPECIALIZED-SAME: %swift.type* getelementptr inbounds ( +// CHECK-PRESPECIALIZED-SAME: %swift.full_type, +// CHECK-PRESPECIALIZED-SAME: %swift.full_type* bitcast ( +// CHECK-PRESPECIALIZED-SAME: <{ +// CHECK-PRESPECIALIZED-SAME: i8**, +// CHECK-PRESPECIALIZED-SAME: [[INT]], +// CHECK-PRESPECIALIZED-SAME: %swift.type_descriptor*, +// CHECK-PRESPECIALIZED-SAME: %swift.type*, +// CHECK-PRESPECIALIZED-SAME: i64 +// CHECK-PRESPECIALIZED-SAME: }>* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMf" +// CHECK-PRESPECIALIZED-SAME: to %swift.full_type* +// CHECK-PRESPECIALIZED-SAME: ), +// CHECK-PRESPECIALIZED-SAME: i32 0, +// CHECK-PRESPECIALIZED-SAME: i32 1 +// CHECK-PRESPECIALIZED-SAME: ), +// CHECK-PRESPECIALIZED-SAME: %swift.type* getelementptr inbounds ( +// CHECK-PRESPECIALIZED-SAME: %swift.full_type, +// CHECK-PRESPECIALIZED-SAME: %swift.full_type* bitcast ( +// CHECK-PRESPECIALIZED-SAME: <{ +// CHECK-PRESPECIALIZED-SAME: i8**, +// CHECK-PRESPECIALIZED-SAME: [[INT]], +// CHECK-PRESPECIALIZED-SAME: %swift.type_descriptor*, +// CHECK-PRESPECIALIZED-SAME: %swift.type*, +// CHECK-PRESPECIALIZED-SAME: i64 +// CHECK-PRESPECIALIZED-SAME: }>* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMf" +// CHECK-PRESPECIALIZED-SAME: to %swift.full_type* +// CHECK-PRESPECIALIZED-SAME: ), +// CHECK-PRESPECIALIZED-SAME: i32 0, +// CHECK-PRESPECIALIZED-SAME: i32 1 +// CHECK-PRESPECIALIZED-SAME: ), +// CHECK-PRESPECIALIZED-SAME: i8** [[Single_P1]] +// CHECK-PRESPECIALIZED-SAME: ) +// CHECK-PRESPECIALIZED-NEXT: ret void +// CHECK-PRESPECIALIZED-NEXT: } + // Lazy witness table accessor for the concrete Single : P1. @@ -134,6 +173,27 @@ public func single_concrete() { // TYPEBYNAME-NEXT: ret i8** [[T0]] // TYPEBYNAME-NEXT: } +// TYPEBYNAME_PRESPECIALIZED-LABEL: define linkonce_odr hidden i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// TYPEBYNAME_PRESPECIALIZED-NEXT: entry: +// TYPEBYNAME_PRESPECIALIZED-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL", align 8 +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// TYPEBYNAME_PRESPECIALIZED-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// TYPEBYNAME_PRESPECIALIZED: cacheIsNull: +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// TYPEBYNAME_PRESPECIALIZED-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP2VAA0F0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 + +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[Single_P1:%.*]] = call i8** @swift_getWitnessTable +// TYPEBYNAME_PRESPECIALIZED-NEXT: store atomic i8** [[Single_P1]], i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL" release, align 8 +// TYPEBYNAME_PRESPECIALIZED-NEXT: br label %cont + +// TYPEBYNAME_PRESPECIALIZED: cont: +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Single_P1]], %cacheIsNull ] +// TYPEBYNAME_PRESPECIALIZED-NEXT: ret i8** [[T0]] +// TYPEBYNAME_PRESPECIALIZED-NEXT: } + public struct Double {} extension Double: P1 where B: P2, C: P3 { diff --git a/test/Inputs/conditional_conformance_basic_conformances_future.swift b/test/Inputs/conditional_conformance_basic_conformances_future.swift new file mode 100644 index 0000000000000..43eb345e7361e --- /dev/null +++ b/test/Inputs/conditional_conformance_basic_conformances_future.swift @@ -0,0 +1,428 @@ +public func takes_p1(_: T.Type) {} +public protocol P1 { + func normal() + func generic(_: T) +} +public protocol P2 {} +public protocol P3 {} + +public struct IsP2: P2 {} +public struct IsP3: P3 {} + + +public struct Single {} +extension Single: P1 where A: P2 { + public func normal() {} + public func generic(_: T) {} +} + +// witness method for Single.normal + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s42conditional_conformance_basic_conformances6SingleVyxGAA2P1A2A2P2RzlAaEP6normalyyFTW"(%T42conditional_conformance_basic_conformances6SingleV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[A_P2_i8star:%.*]] = load i8*, i8** [[A_P2_PTR]], align 8 +// CHECK-NEXT: [[A_P2:%.*]] = bitcast i8* [[A_P2_i8star]] to i8** +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[A_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY]], i64 2 +// CHECK-NEXT: [[A:%.*]] = load %swift.type*, %swift.type** [[A_PTR]], align 8 +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances6SingleVA2A2P2RzlE6normalyyF"(%swift.type* [[A]], i8** [[A_P2]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// witness method for Single.generic + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s42conditional_conformance_basic_conformances6SingleVyxGAA2P1A2A2P2RzlAaEP7genericyyqd__AA2P3Rd__lFTW"(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_1_0", i8** %"\CF\84_1_0.P3", %T42conditional_conformance_basic_conformances6SingleV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[A_P2_i8star:%.*]] = load i8*, i8** [[A_P2_PTR]], align 8 +// CHECK-NEXT: [[A_P2:%.*]] = bitcast i8* [[A_P2_i8star]] to i8** +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[A_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY]], i64 2 +// CHECK-NEXT: [[A:%.*]] = load %swift.type*, %swift.type** [[A_PTR]], align 8 +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances6SingleVA2A2P2RzlE7genericyyqd__AA2P3Rd__lF"(%swift.opaque* noalias nocapture %0, %swift.type* [[A]], %swift.type* %"\CF\84_1_0", i8** [[A_P2]], i8** %"\CF\84_1_0.P3") +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +public func single_generic(_: T.Type) { + takes_p1(Single.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s42conditional_conformance_basic_conformances14single_genericyyxmAA2P2RzlF"(%swift.type*, %swift.type* %T, i8** %T.P2) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6SingleVMa"(i64 0, %swift.type* %T) +// CHECK-NEXT: [[Single_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[T_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** %T.P2, i8*** [[T_P2_PTR]], align 8 +// CHECK-NEXT: [[Single_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances8takes_p1yyxmAA2P1RzlF"(%swift.type* [[Single_TYPE]], %swift.type* [[Single_TYPE]], i8** [[Single_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + + +public func single_concrete() { + takes_p1(Single.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s42conditional_conformance_basic_conformances15single_concreteyyF"() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[Single_P1:%.*]] = call i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances8takes_p1yyxmAA2P1RzlF"( +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: i8** [[Single_P1]] +// CHECK-SAME: ) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + + +// Lazy witness table accessor for the concrete Single : P1. + +// CHECK-LABEL: define linkonce_odr hidden i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// CHECK-NEXT: entry: +// CHECK-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL", align 8 +// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// CHECK: cacheIsNull: +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP2VAA0F0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 + +// CHECK-NEXT: [[Single_P1:%.*]] = call i8** @swift_getWitnessTable( +// CHECK-SAME: %swift.protocol_conformance_descriptor* bitcast ( +// CHECK-SAME: { i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }* +// CHECK-SAME: @"$s42conditional_conformance_basic_conformances6SingleVyxGAA2P1A2A2P2RzlMc" +// CHECK-SAME: to %swift.protocol_conformance_descriptor* +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: i8*** [[CONDITIONAL_REQUIREMENTS]] +// CHECK-SAME: ) +// CHECK-NEXT: store atomic i8** [[Single_P1]], i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL" release, align 8 +// CHECK-NEXT: br label %cont + +// CHECK: cont: +// CHECK-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Single_P1]], %cacheIsNull ] +// CHECK-NEXT: ret i8** [[T0]] +// CHECK-NEXT: } + +// TYPEBYNAME-LABEL: define linkonce_odr hidden i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// TYPEBYNAME-NEXT: entry: +// TYPEBYNAME-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// TYPEBYNAME-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL", align 8 +// TYPEBYNAME-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// TYPEBYNAME-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// TYPEBYNAME: cacheIsNull: +// TYPEBYNAME-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGMD") +// TYPEBYNAME-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// TYPEBYNAME-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// TYPEBYNAME-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP2VAA0F0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 + +// TYPEBYNAME-NEXT: [[Single_P1:%.*]] = call i8** @swift_getWitnessTable +// TYPEBYNAME-NEXT: store atomic i8** [[Single_P1]], i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL" release, align 8 +// TYPEBYNAME-NEXT: br label %cont + +// TYPEBYNAME: cont: +// TYPEBYNAME-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Single_P1]], %cacheIsNull ] +// TYPEBYNAME-NEXT: ret i8** [[T0]] +// TYPEBYNAME-NEXT: } + +// TYPEBYNAME_PRESPECIALIZED-LABEL: define linkonce_odr hidden i8** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWl"() +// TYPEBYNAME_PRESPECIALIZED-NEXT: entry: +// TYPEBYNAME_PRESPECIALIZED-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL", align 8 +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// TYPEBYNAME_PRESPECIALIZED-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// TYPEBYNAME_PRESPECIALIZED: cacheIsNull: +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// TYPEBYNAME_PRESPECIALIZED-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP2VAA0F0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 + +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[Single_P1:%.*]] = call i8** @swift_getWitnessTable +// TYPEBYNAME_PRESPECIALIZED-NEXT: store atomic i8** [[Single_P1]], i8*** @"$s42conditional_conformance_basic_conformances6SingleVyAA4IsP2VGACyxGAA2P1A2A0G0RzlWL" release, align 8 +// TYPEBYNAME_PRESPECIALIZED-NEXT: br label %cont + +// TYPEBYNAME_PRESPECIALIZED: cont: +// TYPEBYNAME_PRESPECIALIZED-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Single_P1]], %cacheIsNull ] +// TYPEBYNAME_PRESPECIALIZED-NEXT: ret i8** [[T0]] +// TYPEBYNAME_PRESPECIALIZED-NEXT: } + + +public struct Double {} +extension Double: P1 where B: P2, C: P3 { + public func normal() {} + public func generic(_: T) {} +} + +// witness method for Double.normal + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s42conditional_conformance_basic_conformances6DoubleVyxq_GAA2P1A2A2P2RzAA2P3R_rlAaEP6normalyyFTW"(%T42conditional_conformance_basic_conformances6DoubleV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[B_P2_i8star:%.*]] = load i8*, i8** [[B_P2_PTR]], align 8 +// CHECK-NEXT: [[B_P2:%.*]] = bitcast i8* [[B_P2_i8star]] to i8** + +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -2 +// CHECK-NEXT: [[C_P3_i8star:%.*]] = load i8*, i8** [[C_P3_PTR]], align 8 +// CHECK-NEXT: [[C_P3:%.*]] = bitcast i8* [[C_P3_i8star]] to i8** + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[B_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY]], i64 2 +// CHECK-NEXT: [[B:%.*]] = load %swift.type*, %swift.type** [[B_PTR]], align 8 + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY_2:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[C_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY_2]], i64 3 +// CHECK-NEXT: [[C:%.*]] = load %swift.type*, %swift.type** [[C_PTR]], align 8 + +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances6DoubleVA2A2P2RzAA2P3R_rlE6normalyyF"(%swift.type* [[B]], %swift.type* [[C]], i8** [[B_P2]], i8** [[C_P3]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// witness method for Double.generic + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s42conditional_conformance_basic_conformances6DoubleVyxq_GAA2P1A2A2P2RzAA2P3R_rlAaEP7genericyyqd__AaGRd__lFTW"(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_1_0", i8** %"\CF\84_1_0.P3", %T42conditional_conformance_basic_conformances6DoubleV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: + +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[B_P2_i8star:%.*]] = load i8*, i8** [[B_P2_PTR]], align 8 +// CHECK-NEXT: [[B_P2:%.*]] = bitcast i8* [[B_P2_i8star]] to i8** + +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -2 +// CHECK-NEXT: [[C_P3_i8star:%.*]] = load i8*, i8** [[C_P3_PTR]], align 8 +// CHECK-NEXT: [[C_P3:%.*]] = bitcast i8* [[C_P3_i8star]] to i8** + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[B_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY]], i64 2 +// CHECK-NEXT: [[B:%.*]] = load %swift.type*, %swift.type** [[B_PTR]], align 8 + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY_2:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[C_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY_2]], i64 3 +// CHECK-NEXT: [[C:%.*]] = load %swift.type*, %swift.type** [[C_PTR]], align 8 + +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances6DoubleVA2A2P2RzAA2P3R_rlE7genericyyqd__AaERd__lF"(%swift.opaque* noalias nocapture %0, %swift.type* [[B]], %swift.type* [[C]], %swift.type* %"\CF\84_1_0", i8** [[B_P2]], i8** [[C_P3]], i8** %"\CF\84_1_0.P3") +// CHECK-NEXT: ret void +// CHECK-NEXT: } + + +public func double_generic_generic(_: U.Type, _: V.Type) { + takes_p1(Double.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s42conditional_conformance_basic_conformances015double_generic_F0yyxm_q_mtAA2P2RzAA2P3R_r0_lF"(%swift.type*, %swift.type*, %swift.type* %U, %swift.type* %V, i8** %U.P2, i8** %V.P3) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [2 x i8**], align 8 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVMa"(i64 0, %swift.type* %U, %swift.type* %V) +// CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [2 x i8**], [2 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** %U.P2, i8*** [[B_P2_PTR]], align 8 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** %V.P3, i8*** [[C_P3_PTR]], align 8 + +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances8takes_p1yyxmAA2P1RzlF"(%swift.type* [[Double_TYPE]], %swift.type* [[Double_TYPE]], i8** [[Double_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +public func double_generic_concrete(_: X.Type) { + takes_p1(Double.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s42conditional_conformance_basic_conformances23double_generic_concreteyyxmAA2P2RzlF"(%swift.type*, %swift.type* %X, i8** %X.P2) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [2 x i8**], align 8 +// CHECK: [[Double_TYPE_Response:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s42conditional_conformance_basic_conformances6DoubleVMa"( +// CHECK-SAME: i64 0, +// CHECK-SAME: %swift.type* +// CHECK-SAME: %X, +// CHECK-SAME: %swift.type* bitcast ( +// CHECK-SAME: i64* getelementptr inbounds ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: i64, +// CHECK-SAME: <{ i32, i32, i32, i32, i32, i32, i32 }>*, +// CHECK-SAME: i64 +// CHECK-SAME: }>, +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: i64, +// CHECK-SAME: <{ i32, i32, i32, i32, i32, i32, i32 }>*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances4IsP3VMf", +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) to %swift.type* +// CHECK-SAME: ) +// CHECK-SAME: ) +// CHECK: [[Double_TYPE:%[0-9]+]] = extractvalue %swift.metadata_response [[Double_TYPE_Response]], 0 +// CHECK: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [2 x i8**], [2 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** %X.P2, i8*** [[B_P2_PTR]], align 8 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[C_P3_PTR]], align 8 + +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable( +// CHECK-SAME: %swift.protocol_conformance_descriptor* bitcast ( +// CHECK-SAME: { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }* +// CHECK-SAME: @"$s42conditional_conformance_basic_conformances6DoubleVyxq_GAA2P1A2A2P2RzAA2P3R_rlMc" +// CHECK-SAME: to %swift.protocol_conformance_descriptor* +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* %2, +// CHECK-SAME: i8*** [[CONDITIONAL_REQUIREMENTS]] +// CHECK-SAME: ) + +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances8takes_p1yyxmAA2P1RzlF"( +// CHECK-SAME: %swift.type* [[Double_TYPE]], +// CHECK-SAME: %swift.type* [[Double_TYPE]], +// CHECK-SAME: i8** [[Double_P1]] +// CHECK-SAME: ) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +public func double_concrete_concrete() { + takes_p1(Double.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s42conditional_conformance_basic_conformances016double_concrete_F0yyF"() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGACyxq_GAA2P1A2A0G0RzAA0H0R_rlWl"() +// CHECK-NEXT: call swiftcc void @"$s42conditional_conformance_basic_conformances8takes_p1yyxmAA2P1RzlF"( +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: i8** [[Double_P1]] +// CHECK-SAME: ) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// Lazy witness table accessor for the concrete Double : P1. + +// CHECK-LABEL: define linkonce_odr hidden i8** @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGACyxq_GAA2P1A2A0G0RzAA0H0R_rlWl"() +// CHECK-NEXT: entry: +// CHECK-NEXT: %conditional.requirement.buffer = alloca [2 x i8**], align 8 +// CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGACyxq_GAA2P1A2A0G0RzAA0H0R_rlWL", align 8 +// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// CHECK: cacheIsNull: +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [2 x i8**], [2 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP2VAA0F0AAWP", i32 0, i32 0), i8*** [[B_P2_PTR]], align 8 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s42conditional_conformance_basic_conformances4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[C_P3_PTR]], align 8 + +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable( +// CHECK-SAME: %swift.protocol_conformance_descriptor* bitcast ( +// CHECK-SAME: { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }* +// CHECK-SAME: @"$s42conditional_conformance_basic_conformances6DoubleVyxq_GAA2P1A2A2P2RzAA2P3R_rlMc" +// CHECK-SAME: to %swift.protocol_conformance_descriptor* +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: i64, +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: i8*** [[CONDITIONAL_REQUIREMENTS]] +// CHECK-SAME: ) +// CHECK-NEXT: store atomic i8** [[Double_P1]], i8*** @"$s42conditional_conformance_basic_conformances6DoubleVyAA4IsP2VAA0F2P3VGACyxq_GAA2P1A2A0G0RzAA0H0R_rlWL" release, align 8 +// CHECK-NEXT: br label %cont + +// CHECK: cont: +// CHECK-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Double_P1]], %cacheIsNull ] +// CHECK-NEXT: ret i8** [[T0]] +// CHECK-NEXT: } + + +func dynamicCastToP1(_ value: Any) -> P1? { + return value as? P1 +} + +protocol P4 {} +typealias P4Typealias = P4 +protocol P5 {} + +struct SR7101 {} +extension SR7101 : P5 where T == P4Typealias {} diff --git a/test/Inputs/conditional_conformance_subclass_future.swift b/test/Inputs/conditional_conformance_subclass_future.swift new file mode 100644 index 0000000000000..84b9e773c483d --- /dev/null +++ b/test/Inputs/conditional_conformance_subclass_future.swift @@ -0,0 +1,176 @@ +public func takes_p1(_: T.Type) {} +public protocol P1 { + func normal() + func generic(_: T) +} +public protocol P2 {} +public protocol P3 {} + +public struct IsP2: P2 {} + +public class Base {} +extension Base: P1 where A: P2 { + public func normal() {} + public func generic(_: T) {} +} + +// witness method for Base.normal + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s32conditional_conformance_subclass4BaseCyxGAA2P1A2A2P2RzlAaEP6normalyyFTW"(%T32conditional_conformance_subclass4BaseC.0** noalias nocapture swiftself dereferenceable(8), %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[A_P2:%.*]] = load i8*, i8** [[A_P2_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_0.P2" = bitcast i8* [[A_P2]] to i8** +// CHECK-NEXT: [[SELF:%.]] = load %T32conditional_conformance_subclass4BaseC.0*, %T32conditional_conformance_subclass4BaseC.0** %0 +// CHECK-NEXT: call swiftcc void @"$s32conditional_conformance_subclass4BaseCA2A2P2RzlE6normalyyF"(i8** %"\CF\84_0_0.P2", %T32conditional_conformance_subclass4BaseC.0* swiftself [[SELF]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// witness method for Base.generic + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s32conditional_conformance_subclass4BaseCyxGAA2P1A2A2P2RzlAaEP7genericyyqd__AA2P3Rd__lFTW"(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_1_0", i8** %"\CF\84_1_0.P3", %T32conditional_conformance_subclass4BaseC.1** noalias nocapture swiftself dereferenceable(8), %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[A_P2:%.*]] = load i8*, i8** [[A_P2_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_0.P2" = bitcast i8* [[A_P2]] to i8** +// CHECK-NEXT: [[SELF:%.]] = load %T32conditional_conformance_subclass4BaseC.1*, %T32conditional_conformance_subclass4BaseC.1** %1, align 8 +// CHECK-NEXT: call swiftcc void @"$s32conditional_conformance_subclass4BaseCA2A2P2RzlE7genericyyqd__AA2P3Rd__lF"(%swift.opaque* noalias nocapture %0, %swift.type* %"\CF\84_1_0", i8** %"\CF\84_0_0.P2", i8** %"\CF\84_1_0.P3", %T32conditional_conformance_subclass4BaseC.1* swiftself [[SELF]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + + +public class SubclassGeneric: Base {} +public class SubclassConcrete: Base {} +public class SubclassGenericConcrete: SubclassGeneric {} + +public func subclassgeneric_generic(_: T.Type) { + takes_p1(SubclassGeneric.self) +} + +// CHECK-LABEL: define{{( dllexport| protected)?}} swiftcc void @"$s32conditional_conformance_subclass23subclassgeneric_genericyyxmAA2P2RzlF"(%swift.type*, %swift.type* %T, i8** %T.P2) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass15SubclassGenericCMa"(i64 0, %swift.type* %T) +// CHECK-NEXT: [[SubclassGeneric_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[T_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** %T.P2, i8*** [[T_P2_PTR]], align 8 +// CHECK-NEXT: [[Base_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: call swiftcc void @"$s32conditional_conformance_subclass8takes_p1yyxmAA2P1RzlF"(%swift.type* [[SubclassGeneric_TYPE]], %swift.type* [[SubclassGeneric_TYPE]], i8** [[Base_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +public func subclassgeneric_concrete() { + takes_p1(SubclassGeneric.self) +} + +// CHECK-LABEL: define{{( dllexport| protected)?}} swiftcc void @"$s32conditional_conformance_subclass24subclassgeneric_concreteyyF"() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[SubclassGeneric_TYPE:%.*]] = call {{.*}}@"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD" +// CHECK-NEXT: [[Base_P1:%.*]] = call i8** @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGAA4BaseCyxGAA2P1A2A0G0RzlWl"() +// CHECK-NEXT: call swiftcc void @"$s32conditional_conformance_subclass8takes_p1yyxmAA2P1RzlF"(%swift.type* [[SubclassGeneric_TYPE]], %swift.type* [[SubclassGeneric_TYPE]], i8** [[Base_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// Lazy witness table accessor for the concrete SubclassGeneric : Base. + +// CHECK-LABEL: define linkonce_odr hidden i8** @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGAA4BaseCyxGAA2P1A2A0G0RzlWl"() +// CHECK-NEXT: entry: +// CHECK-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGAA4BaseCyxGAA2P1A2A0G0RzlWL", align 8 +// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// CHECK: cacheIsNull: +// macosx-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// ios-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// watchos-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// tvos-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// linux-gnu-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// linux-android-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") +// windows-msvc-NEXT: [[T0:%.*]] = call %swift.type* @__swift_instantiateConcreteTypeFromMangledNameAbstract({ i32, i32 }* @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGMD") + +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s32conditional_conformance_subclass4IsP2VAA0E0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 +// CHECK-NEXT: [[Base_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: store atomic i8** [[Base_P1]], i8*** @"$s32conditional_conformance_subclass15SubclassGenericCyAA4IsP2VGAA4BaseCyxGAA2P1A2A0G0RzlWL" release, align 8 +// CHECK-NEXT: br label %cont + +// CHECK: cont: +// CHECK-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Base_P1]], %cacheIsNull ] +// CHECK-NEXT: ret i8** [[T0]] +// CHECK-NEXT: } + +public func subclassconcrete() { + takes_p1(SubclassConcrete.self) +} + +// CHECK-LABEL: define{{( dllexport| protected)?}} swiftcc void @"$s32conditional_conformance_subclass16subclassconcreteyyF"() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass16SubclassConcreteCMa"(i64 0) +// CHECK-NEXT: [[SubclassConcrete_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: [[SubclassConcrete_P1:%.*]] = call i8** @"$s32conditional_conformance_subclass16SubclassConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWl"() +// CHECK-NEXT: call swiftcc void @"$s32conditional_conformance_subclass8takes_p1yyxmAA2P1RzlF"(%swift.type* [[SubclassConcrete_TYPE]], %swift.type* [[SubclassConcrete_TYPE]], i8** [[SubclassConcrete_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK-LABEL: define linkonce_odr hidden i8** @"$s32conditional_conformance_subclass16SubclassConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWl"() +// CHECK-NEXT: entry: +// CHECK-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s32conditional_conformance_subclass16SubclassConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWL", align 8 +// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// CHECK: cacheIsNull: +// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass16SubclassConcreteCMa"(i64 255) +// CHECK-NEXT: [[SubclassConcrete_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 + +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s32conditional_conformance_subclass4IsP2VAA0E0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 +// CHECK-NEXT: [[Base_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: store atomic i8** [[Base_P1]], i8*** @"$s32conditional_conformance_subclass16SubclassConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWL" release, align 8 +// CHECK-NEXT: br label %cont + +// CHECK: cont: +// CHECK-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Base_P1]], %cacheIsNull ] +// CHECK-NEXT: ret i8** [[T0]] +// CHECK-NEXT: } + +public func subclassgenericconcrete() { + takes_p1(SubclassGenericConcrete.self) +} + +// CHECK-LABEL: define{{( dllexport| protected)?}} swiftcc void @"$s32conditional_conformance_subclass23subclassgenericconcreteyyF"() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass23SubclassGenericConcreteCMa"(i64 0) +// CHECK-NEXT: [[SubclassGenericConcrete_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: [[SubclassGenericConcrete_P1:%.*]] = call i8** @"$s32conditional_conformance_subclass23SubclassGenericConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWl"() +// CHECK-NEXT: call swiftcc void @"$s32conditional_conformance_subclass8takes_p1yyxmAA2P1RzlF"(%swift.type* [[SubclassGenericConcrete_TYPE]], %swift.type* [[SubclassGenericConcrete_TYPE]], i8** [[SubclassGenericConcrete_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK-LABEL: define linkonce_odr hidden i8** @"$s32conditional_conformance_subclass23SubclassGenericConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWl"() +// CHECK-NEXT: entry: +// CHECK-NEXT: %conditional.requirement.buffer = alloca [1 x i8**], align 8 +// CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s32conditional_conformance_subclass23SubclassGenericConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWL", align 8 +// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// CHECK: cacheIsNull: +// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s32conditional_conformance_subclass23SubclassGenericConcreteCMa"(i64 255) +// CHECK-NEXT: [[SubclassGenericConcrete_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[A_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"$s32conditional_conformance_subclass4IsP2VAA0E0AAWP", i32 0, i32 0), i8*** [[A_P2_PTR]], align 8 +// CHECK-NEXT: [[Base_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: store atomic i8** [[Base_P1]], i8*** @"$s32conditional_conformance_subclass23SubclassGenericConcreteCAA4BaseCyxGAA2P1A2A2P2RzlWL" release, align 8 +// CHECK-NEXT: br label %cont + +// CHECK: cont: +// CHECK-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Base_P1]], %cacheIsNull ] +// CHECK-NEXT: ret i8** [[T0]] +// CHECK-NEXT: } diff --git a/test/Inputs/conditional_conformance_with_assoc.swift b/test/Inputs/conditional_conformance_with_assoc.swift index 5f5992b715376..c561630f1d43a 100644 --- a/test/Inputs/conditional_conformance_with_assoc.swift +++ b/test/Inputs/conditional_conformance_with_assoc.swift @@ -136,7 +136,24 @@ public func generic_concrete(_: T.Type) // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s34conditional_conformance_with_assoc16generic_concreteyyxmAA2P2RzAaC3AT2RpzAA2P3AD_AdaCP3AT3RPzlF"(%swift.type*, %swift.type* %T, i8** %T.P2, i8** %T.AT2.P2, i8** %T.AT2.AT2.AT3.P3) // CHECK-NEXT: entry: // CHECK: %conditional.requirement.buffer = alloca [3 x i8**], align 8 -// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVMa"(i64 0, %swift.type* %T, %swift.type* bitcast (i64* getelementptr inbounds (<{ i8**, i64, <{ {{.*}} }>* }>, <{ {{.*}} }>* @"$s34conditional_conformance_with_assoc4IsP3VMf", i32 0, i32 1) to %swift.type*), i8** %T.P2) +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVMa"( +// CHECK-SAME: i64 0, +// CHECK-SAME: %swift.type* +// CHECK-SAME: %T, +// CHECK-SAME: %swift.type* bitcast ( +// CHECK-SAME: i64* getelementptr inbounds ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: i64, +// CHECK-SAME: <{ {{[^}]*}} }>* +// CHECK-SAME: }>, +// CHECK-SAME: <{ {{.*}} }>* @"$s34conditional_conformance_with_assoc4IsP3VMf", +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) to %swift.type* +// CHECK-SAME: ), +// CHECK-SAME: i8** %T.P2 +// CHECK-SAME: ) // CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 // CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 diff --git a/test/Inputs/conditional_conformance_with_assoc_future.swift b/test/Inputs/conditional_conformance_with_assoc_future.swift new file mode 100644 index 0000000000000..429d689518e57 --- /dev/null +++ b/test/Inputs/conditional_conformance_with_assoc_future.swift @@ -0,0 +1,308 @@ +public func takes_p1(_: T.Type) {} +public protocol P1 { + associatedtype AT1 + + func normal() + func generic(_: T) +} +public protocol P2 { + associatedtype AT2: P3 +} +public protocol P3 { + associatedtype AT3 +} + +public struct Nothing {} + +public struct IsP2: P2 { + public typealias AT2 = IsP3 +} +public struct IsP3: P3 { + public typealias AT3 = Nothing +} + +public struct IsAlsoP2: P2 { + public typealias AT2 = IsBoth +} +public struct IsBoth: P2, P3 { + public typealias AT2 = HoldsP3 + public typealias AT3 = Nothing +} +public struct HoldsP3: P3 { + public typealias AT3 = IsP3 +} + +public struct Double {} +extension Double: P1 where B.AT2: P2, C: P3, B.AT2.AT2.AT3: P3 { + public typealias AT1 = C + public func normal() {} + public func generic(_: T) {} +} + +// witness method for Double.normal + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s34conditional_conformance_with_assoc6DoubleVyxq_GAA2P1A2A2P3R_AA2P23AT2RpzAafH_AhaGP3AT3RPzrlAaEP6normalyyFTW"(%T34conditional_conformance_with_assoc6DoubleV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[C_P3:%.*]] = load i8*, i8** [[C_P3_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_1.P3" = bitcast i8* [[C_P3]] to i8** + +// CHECK-NEXT: [[B_AT2_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -2 +// CHECK-NEXT: [[B_AT2_P2:%.*]] = load i8*, i8** [[B_AT2_P2_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_0.AT2.P2" = bitcast i8* [[B_AT2_P2]] to i8** + +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -3 +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3:%.*]] = load i8*, i8** [[B_AT2_AT2_AT3_P3_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_0.AT2.AT2.AT3.P3" = bitcast i8* [[B_AT2_AT2_AT3_P3]] to i8** + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[B_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY]], i64 2 +// CHECK-NEXT: [[B:%.*]] = load %swift.type*, %swift.type** [[B_PTR]], align 8 + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY_2:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[C_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY_2]], i64 3 +// CHECK-NEXT: [[C:%.*]] = load %swift.type*, %swift.type** [[C_PTR]], align 8 + +// CHECK-NEXT: [[SELF_AS_WT_ARRAY:%.*]] = bitcast %swift.type* %Self to i8*** +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[SELF_AS_WT_ARRAY]], i64 4 +// CHECK-NEXT: %"\CF\84_0_0.P2" = load i8**, i8*** [[B_P2_PTR]], align 8 + +// CHECK-NEXT: call swiftcc void @"$s34conditional_conformance_with_assoc6DoubleVA2A2P3R_AA2P23AT2RpzAadF_AfaEP3AT3RPzrlE6normalyyF"(%swift.type* %"\CF\84_0_0", %swift.type* %"\CF\84_0_1", i8** %"\CF\84_0_0.P2", i8** %"\CF\84_0_1.P3", i8** %"\CF\84_0_0.AT2.P2", i8** %"\CF\84_0_0.AT2.AT2.AT3.P3") +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// witness method for Double.generic + +// CHECK-LABEL: define linkonce_odr hidden swiftcc void @"$s34conditional_conformance_with_assoc6DoubleVyxq_GAA2P1A2A2P3R_AA2P23AT2RpzAafH_AhaGP3AT3RPzrlAaEP7genericyyqd__AaFRd__lFTW"(%swift.opaque* noalias nocapture, %swift.type* %"\CF\84_1_0", i8** %"\CF\84_1_0.P3", %T34conditional_conformance_with_assoc6DoubleV* noalias nocapture swiftself, %swift.type* %Self, i8** %SelfWitnessTable) +// CHECK-NEXT: entry: +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -1 +// CHECK-NEXT: [[C_P3:%.*]] = load i8*, i8** [[C_P3_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_1.P3" = bitcast i8* [[C_P3]] to i8** + +// CHECK-NEXT: [[B_AT2_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -2 +// CHECK-NEXT: [[B_AT2_P2:%.*]] = load i8*, i8** [[B_AT2_P2_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_0.AT2.P2" = bitcast i8* [[B_AT2_P2]] to i8** + +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3_PTR:%.*]] = getelementptr inbounds i8*, i8** %SelfWitnessTable, i32 -3 +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3:%.*]] = load i8*, i8** [[B_AT2_AT2_AT3_P3_PTR]], align 8 +// CHECK-NEXT: %"\CF\84_0_0.AT2.AT2.AT3.P3" = bitcast i8* [[B_AT2_AT2_AT3_P3]] to i8** + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[B_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY]], i64 2 +// CHECK-NEXT: [[B:%.*]] = load %swift.type*, %swift.type** [[B_PTR]], align 8 + +// CHECK-NEXT: [[SELF_AS_TYPE_ARRAY_2:%.*]] = bitcast %swift.type* %Self to %swift.type** +// CHECK-NEXT: [[C_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[SELF_AS_TYPE_ARRAY_2]], i64 3 +// CHECK-NEXT: [[C:%.*]] = load %swift.type*, %swift.type** [[C_PTR]], align 8 + +// CHECK-NEXT: [[SELF_AS_WT_ARRAY:%.*]] = bitcast %swift.type* %Self to i8*** +// CHECK-NEXT: [[B_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[SELF_AS_WT_ARRAY]], i64 4 +// CHECK-NEXT: %"\CF\84_0_0.P2" = load i8**, i8*** [[B_P2_PTR]], align 8 + +// CHECK-NEXT: call swiftcc void @"$s34conditional_conformance_with_assoc6DoubleVA2A2P3R_AA2P23AT2RpzAadF_AfaEP3AT3RPzrlE7genericyyqd__AaDRd__lF"(%swift.opaque* noalias nocapture %0, %swift.type* %"\CF\84_0_0", %swift.type* %"\CF\84_0_1", %swift.type* %"\CF\84_1_0", i8** %"\CF\84_0_0.P2", i8** %"\CF\84_0_1.P3", i8** %"\CF\84_1_0.P3", i8** %"\CF\84_0_0.AT2.P2", i8** %"\CF\84_0_0.AT2.AT2.AT3.P3") +// CHECK-NEXT: ret void +// CHECK-NEXT: } + + +public func generic_generic(_: T.Type, _: U.Type) + where T.AT2: P2, U: P3, T.AT2.AT2.AT3: P3 +{ + takes_p1(Double.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s34conditional_conformance_with_assoc08generic_E0yyxm_q_mtAA2P2RzAA2P3R_AaC3AT2RpzAadE_AeaCP3AT3RPzr0_lF"(%swift.type*, %swift.type*, %swift.type* %T, %swift.type* %U, i8** %T.P2, i8** %U.P3, i8** %T.AT2.P2, i8** %T.AT2.AT2.AT3.P3) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [3 x i8**], align 8 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVMa"(i64 0, %swift.type* %T, %swift.type* %U, i8** %T.P2) +// CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** %U.P3, i8*** [[C_P3_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** %T.AT2.P2, i8*** [[B_AT2_P2_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 2 +// CHECK-NEXT: store i8** %T.AT2.AT2.AT3.P3, i8*** [[B_AT2_AT2_AT3_P3_PTR]], align 8 + +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: call swiftcc void @"$s34conditional_conformance_with_assoc8takes_p1yyxmAA2P1RzlF"(%swift.type* [[Double_TYPE]], %swift.type* [[Double_TYPE]], i8** [[Double_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +public func generic_concrete(_: T.Type) + where T.AT2: P2, T.AT2.AT2.AT3: P3 +{ + takes_p1(Double.self) +} +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s34conditional_conformance_with_assoc16generic_concreteyyxmAA2P2RzAaC3AT2RpzAA2P3AD_AdaCP3AT3RPzlF"(%swift.type*, %swift.type* %T, i8** %T.P2, i8** %T.AT2.P2, i8** %T.AT2.AT2.AT3.P3) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [3 x i8**], align 8 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVMa"( +// CHECK-SAME: i64 0, +// CHECK-SAME: %swift.type* +// CHECK-SAME: %T, +// CHECK-SAME: %swift.type* bitcast ( +// CHECK-SAME: i64* getelementptr inbounds ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: i64, +// CHECK-SAME: <{ {{[^}]*}} }>* +// CHECK-SAME: }>, +// CHECK-SAME: <{ {{.*}} }>* @"$s34conditional_conformance_with_assoc4IsP3VMf", +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) to %swift.type* +// CHECK-SAME: ), +// CHECK-SAME: i8** %T.P2 +// CHECK-SAME: ) +// CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 + +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"$s34conditional_conformance_with_assoc4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[C_P3_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** %T.AT2.P2, i8*** [[B_AT2_P2_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 2 +// CHECK-NEXT: store i8** %T.AT2.AT2.AT3.P3, i8*** [[B_AT2_AT2_AT3_P3_PTR]], align 8 + +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: call swiftcc void @"$s34conditional_conformance_with_assoc8takes_p1yyxmAA2P1RzlF"(%swift.type* [[Double_TYPE]], %swift.type* [[Double_TYPE]], i8** [[Double_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + + +public func concrete_generic(_: U.Type) + where U: P3 +{ + takes_p1(Double.self) +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s34conditional_conformance_with_assoc16concrete_genericyyxmAA2P3RzlF"(%swift.type*, %swift.type* %U, i8** %U.P3) +// CHECK-NEXT: entry: +// CHECK: %conditional.requirement.buffer = alloca [3 x i8**], align 8 +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s34conditional_conformance_with_assoc6DoubleVMa"(i64 0, %swift.type* bitcast (i64* getelementptr inbounds (<{ {{.*}} }>, <{ {{.*}} }>* @"$s34conditional_conformance_with_assoc8IsAlsoP2VMf", i32 0, i32 1) to %swift.type*), %swift.type* %U, i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @"$s34conditional_conformance_with_assoc8IsAlsoP2VAA0G0AAWP", i32 0, i32 0)) +// CHECK-NEXT: [[Double_TYPE:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** %U.P3, i8*** [[C_P3_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @"$s34conditional_conformance_with_assoc6IsBothVAA2P2AAWP", i32 0, i32 0), i8*** [[B_AT2_P2_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 2 +// CHECK-NEXT: store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"$s34conditional_conformance_with_assoc4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[B_AT2_AT2_AT3_P3_PTR]], align 8 +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable +// CHECK-NEXT: call swiftcc void @"$s34conditional_conformance_with_assoc8takes_p1yyxmAA2P1RzlF"(%swift.type* [[Double_TYPE]], %swift.type* [[Double_TYPE]], i8** [[Double_P1]]) +// CHECK-NEXT: ret void +// CHECK-NEXT:} + + +public func concrete_concrete() { + takes_p1(Double.self) +} + +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s34conditional_conformance_with_assoc09concrete_E0yyF"() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[Z:%.*]] = call i8** @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGACyxq_GAA2P1A2A0I0R_AA0H03AT2RpzAakM_AmaLP3AT3RPzrlWl"() +// CHECK-NEXT: call swiftcc void @"$s34conditional_conformance_with_assoc8takes_p1yyxmAA2P1RzlF"( +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ) +// CHECK-SAME: ) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// Lazy witness table accessor for the concrete Double : P1. + +// CHECK-LABEL: define linkonce_odr hidden i8** @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGACyxq_GAA2P1A2A0I0R_AA0H03AT2RpzAakM_AmaLP3AT3RPzrlWl"() +// CHECK-NEXT: entry: +// CHECK-NEXT: %conditional.requirement.buffer = alloca [3 x i8**], align 8 +// CHECK-NEXT: [[CACHE:%.*]] = load i8**, i8*** @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGACyxq_GAA2P1A2A0I0R_AA0H03AT2RpzAakM_AmaLP3AT3RPzrlWL", align 8 +// CHECK-NEXT: [[IS_NULL:%.*]] = icmp eq i8** [[CACHE]], null +// CHECK-NEXT: br i1 [[IS_NULL]], label %cacheIsNull, label %cont + +// CHECK: cacheIsNull: +// CHECK-NEXT: [[CONDITIONAL_REQUIREMENTS:%.*]] = getelementptr inbounds [3 x i8**], [3 x i8**]* %conditional.requirement.buffer, i32 0, i32 0 +// CHECK-NEXT: [[C_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 0 +// CHECK-NEXT: store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"$s34conditional_conformance_with_assoc4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[C_P3_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_P2_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 1 +// CHECK-NEXT: store i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @"$s34conditional_conformance_with_assoc6IsBothVAA2P2AAWP", i32 0, i32 0), i8*** [[B_AT2_P2_PTR]], align 8 +// CHECK-NEXT: [[B_AT2_AT2_AT3_P3_PTR:%.*]] = getelementptr inbounds i8**, i8*** [[CONDITIONAL_REQUIREMENTS]], i32 2 +// CHECK-NEXT: store i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"$s34conditional_conformance_with_assoc4IsP3VAA0F0AAWP", i32 0, i32 0), i8*** [[B_AT2_AT2_AT3_P3_PTR]], align 8 +// CHECK-NEXT: [[Double_P1:%.*]] = call i8** @swift_getWitnessTable( +// CHECK-SAME: %swift.protocol_conformance_descriptor* bitcast ( +// CHECK-SAME: { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i32, i32 }* +// CHECK-SAME: @"$s34conditional_conformance_with_assoc6DoubleVyxq_GAA2P1A2A2P3R_AA2P23AT2RpzAafH_AhaGP3AT3RPzrlMc" +// CHECK-SAME: to %swift.protocol_conformance_descriptor* +// CHECK-SAME: ), +// CHECK-SAME: %swift.type* getelementptr inbounds ( +// CHECK-SAME: %swift.full_type, +// CHECK-SAME: %swift.full_type* bitcast ( +// CHECK-SAME: <{ +// CHECK-SAME: i8**, +// CHECK-SAME: [[INT]], +// CHECK-SAME: %swift.type_descriptor*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: %swift.type*, +// CHECK-SAME: i8**, +// CHECK-SAME: i64 +// CHECK-SAME: }>* @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGMf" +// CHECK-SAME: to %swift.full_type* +// CHECK-SAME: ), +// CHECK-SAME: i32 0, +// CHECK-SAME: i32 1 +// CHECK-SAME: ), +// CHECK-SAME: i8*** [[CONDITIONAL_REQUIREMENTS]] +// CHECK-SAME: ) +// CHECK-NEXT: store atomic i8** [[Double_P1]], i8*** @"$s34conditional_conformance_with_assoc6DoubleVyAA8IsAlsoP2VAA0F2P3VGACyxq_GAA2P1A2A0I0R_AA0H03AT2RpzAakM_AmaLP3AT3RPzrlWL" release, align 8 +// CHECK-NEXT: br label %cont + +// CHECK: cont: +// CHECK-NEXT: [[T0:%.*]] = phi i8** [ [[CACHE]], %entry ], [ [[Double_P1]], %cacheIsNull ] +// CHECK-NEXT: ret i8** [[T0]] +// CHECK-NEXT: } + + + +protocol Base { +} + +protocol Sub : Base { + associatedtype S : Base +} + +struct X { +} + +extension X: Base where T: Base { } +extension X: Sub where T: Sub, T.S == T { + typealias S = X +} From ae1f41e1b2c619e041ddc78195d3c6581e3e4cec Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 5 Feb 2020 16:05:03 -0800 Subject: [PATCH 180/237] [metadata prespecialization] Only Apple or linux. Temporarily disable metadata prespecialization on platforms other than MacOS, iOS, tvOS, watchOS, or Linux. At the moment, tests are failing on Windows with linker errors such as demangleToMetadata-558ea9.o : error LNK2001: unresolved external symbol $ss5Int64VN demangleToMetadata-558ea9.o : error LNK2001: unresolved external symbol $sSSSHsWP Once the issue leading to those linker errors has been resolved, the feature will be enabled on Windows. --- lib/IRGen/IRGenModule.cpp | 3 ++- test/IRGen/conditional_conformances_future.swift | 1 + test/IRGen/dynamic_self_metadata.swift | 1 + test/IRGen/dynamic_self_metadata_future.swift | 1 + test/IRGen/foreign_types_future.sil | 1 + test/IRGen/generic_metatypes_future.swift | 1 + test/IRGen/generic_structs_future.sil | 1 + ...module-0argument-within-class-1argument-1distinct_use.swift | 1 + .../struct-fileprivate-inmodule-1argument-1distinct_use.swift | 1 + ...module-0argument-within-class-1argument-1distinct_use.swift | 1 + .../prespecialized-metadata/struct-inmodule-0argument.swift | 1 + .../struct-inmodule-1argument-0distinct_use.swift | 1 + .../struct-inmodule-1argument-1conformance-1distinct_use.swift | 1 + ...1argument-1conformance_stdlib_equatable-1distinct_use.swift | 1 + .../struct-inmodule-1argument-1distinct_generic_use.swift | 1 + .../struct-inmodule-1argument-1distinct_use.swift | 1 + .../struct-inmodule-1argument-2conformance-1distinct_use.swift | 1 + .../struct-inmodule-1argument-2distinct_use.swift | 1 + .../struct-inmodule-1argument-3conformance-1distinct_use.swift | 1 + .../struct-inmodule-1argument-3distinct_use.swift | 1 + .../struct-inmodule-1argument-4conformance-1distinct_use.swift | 1 + .../struct-inmodule-1argument-4distinct_use.swift | 1 + .../struct-inmodule-1argument-5conformance-1distinct_use.swift | 1 + .../struct-inmodule-1argument-5distinct_use.swift | 1 + .../struct-inmodule-1argument-clang_node-1distinct_use.swift | 1 + ...module-1argument-within-class-1argument-1distinct_use.swift | 1 + ...nmodule-1argument-within-enum-1argument-1distinct_use.swift | 1 + ...odule-1argument-within-struct-1argument-1distinct_use.swift | 1 + ...t-constrained_extension-equal_arguments-1distinct_use.swift | 1 + .../struct-inmodule-2argument-0distinct_use.swift | 1 + .../struct-inmodule-2argument-1distinct_use.swift | 1 + .../struct-inmodule-2argument-2distinct_use.swift | 1 + .../struct-inmodule-2argument-3distinct_use.swift | 1 + .../struct-inmodule-2argument-4distinct_use.swift | 1 + .../struct-inmodule-2argument-5distinct_use.swift | 1 + ...module-2argument-within-class-1argument-1distinct_use.swift | 1 + .../struct-public-inmodule-1argument-1distinct_use.swift | 1 + test/IRGen/signature_conformances_multifile_future.swift | 1 + test/IRGen/synthesized_conformance_future.swift | 1 + 39 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 3ed07808b4168..dc7084b85a9cc 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1340,7 +1340,8 @@ bool IRGenModule::shouldPrespecializeGenericMetadata() { AvailabilityContext::forDeploymentTarget(context); return IRGen.Opts.PrespecializeGenericMetadata && deploymentAvailability.isContainedIn( - context.getPrespecializedGenericMetadataAvailability()); + context.getPrespecializedGenericMetadataAvailability()) && + (Triple.isOSDarwin() || Triple.isTvOS() || Triple.isOSLinux()); } void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) { diff --git a/test/IRGen/conditional_conformances_future.swift b/test/IRGen/conditional_conformances_future.swift index c4aa7158dec8e..f7bacbe6b9c91 100644 --- a/test/IRGen/conditional_conformances_future.swift +++ b/test/IRGen/conditional_conformances_future.swift @@ -5,4 +5,5 @@ // Too many pointer-sized integers in the IR // REQUIRES: PTRSIZE=64 +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu diff --git a/test/IRGen/dynamic_self_metadata.swift b/test/IRGen/dynamic_self_metadata.swift index 9e068aaad0e18..c03307c070e96 100644 --- a/test/IRGen/dynamic_self_metadata.swift +++ b/test/IRGen/dynamic_self_metadata.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -disable-generic-metadata-prespecialization %s -emit-ir -parse-as-library | %FileCheck %s +// UNSUPPORTED: OS=windows-msvc // REQUIRES: CPU=x86_64 // FIXME: Not a SIL test because we can't parse dynamic Self in SIL. diff --git a/test/IRGen/dynamic_self_metadata_future.swift b/test/IRGen/dynamic_self_metadata_future.swift index 1fd737f852a43..fd87ebe349c70 100644 --- a/test/IRGen/dynamic_self_metadata_future.swift +++ b/test/IRGen/dynamic_self_metadata_future.swift @@ -1,6 +1,7 @@ // RUN: %target-swift-frontend %s -target %module-target-future -emit-ir -parse-as-library | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // REQUIRES: CPU=x86_64 // FIXME: Not a SIL test because we can't parse dynamic Self in SIL. diff --git a/test/IRGen/foreign_types_future.sil b/test/IRGen/foreign_types_future.sil index 9039e985e4fa7..d219e9e1d4a92 100644 --- a/test/IRGen/foreign_types_future.sil +++ b/test/IRGen/foreign_types_future.sil @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -target %module-target-future -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/generic_metatypes_future.swift b/test/IRGen/generic_metatypes_future.swift index 363310ac23142..4165c217fc6e1 100644 --- a/test/IRGen/generic_metatypes_future.swift +++ b/test/IRGen/generic_metatypes_future.swift @@ -10,6 +10,7 @@ // RUN: %swift -module-name generic_metatypes -target armv7k-apple-watchos9.99 -emit-ir -disable-legacy-type-info -parse-stdlib -primary-file %s | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-32 -DINT=i32 %s +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // REQUIRES: CODEGENERATOR=X86 // REQUIRES: CODEGENERATOR=ARM diff --git a/test/IRGen/generic_structs_future.sil b/test/IRGen/generic_structs_future.sil index 772419f0aa677..caffde28b07cb 100644 --- a/test/IRGen/generic_structs_future.sil +++ b/test/IRGen/generic_structs_future.sil @@ -2,6 +2,7 @@ // RUN: %{python} %utils/chex.py < %s > %t/generic_structs_future.sil // RUN: %target-swift-frontend -target %module-target-future %t/generic_structs_future.sil -emit-ir | %FileCheck %t/generic_structs_future.sil +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // REQUIRES: CPU=x86_64 import Builtin diff --git a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift index dbbf31395e0ae..cb276b4c6ed36 100644 --- a/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/class-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -parse-stdlib -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift index 4f35766fc6205..628394450cbee 100644 --- a/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-fileprivate-inmodule-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift index 01d8958d36005..da95112e4b7a0 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument-within-class-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift index e5ae3c72e67a3..3ecfa8978b59b 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-0argument.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift index 38726367123ce..93037c028fdc8 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-0distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift index 1564106debd13..25b929ec02616 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift index c9a18506caa2d..dd97d2f22c53d 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1conformance_stdlib_equatable-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift index feff292a16633..952dd10b23538 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_generic_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift index 01b74ed23ad7a..180109ba53cfd 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift index 685b2c15e113a..601dbd4a2863a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2conformance-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift index eedd046af82cb..a2cb96b957a43 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-2distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift index 5fa79b3afab95..cba697ddf2ab2 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3conformance-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift index e8f4767c3ebd2..80b801f3d114f 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-3distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift index 26f12e2651200..517da257dfe9a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4conformance-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift index 94eb0802f846f..1cf7e89ba4ee0 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-4distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift index 474315005252b..de3c5c0822d66 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5conformance-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift index 88d9a94fa7843..73156db3c7fa4 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-5distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift index d8003b4b7e322..a98483281477b 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-clang_node-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays(mock-sdk-directory: %S/../Inputs) +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs -I %t) -target %module-target-future -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize // UNSUPPORTED: CPU=i386 && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift index a612984c89ee6..b3c093221f6c9 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-class-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift index 9155b1914cfcf..106d42451dbf5 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-enum-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift index 0006020cf3ada..9d73b22b468e1 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift index 69289ba0b1c52..315affc0dda6d 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-1argument-within-struct-2argument-constrained_extension-equal_arguments-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift index 88cc65b0f2a73..32ec219a0435a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-0distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift index 680cd10186adb..90e582c84578a 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift index 022675f3bb28c..6c64ba8f00e81 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-2distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift index be834acba381b..9b204443c6c82 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-3distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift index 96c665dc678cb..ccbf44cd9c5a6 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-4distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift index 3afa79e005cbc..d70ee5ba48434 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-5distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift index 1f49e91840efb..12d7298d34bfc 100644 --- a/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-inmodule-2argument-within-class-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift index 4569fd17af210..dd88e25a4216e 100644 --- a/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift +++ b/test/IRGen/prespecialized-metadata/struct-public-inmodule-1argument-1distinct_use.swift @@ -1,5 +1,6 @@ // RUN: %swift -target %module-target-future -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/signature_conformances_multifile_future.swift b/test/IRGen/signature_conformances_multifile_future.swift index b1514034b3ba9..cf5a9fa9cdf8d 100644 --- a/test/IRGen/signature_conformances_multifile_future.swift +++ b/test/IRGen/signature_conformances_multifile_future.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -target %module-target-future -emit-ir -primary-file %s %S/Inputs/signature_conformances_other.swift | %FileCheck %s -DINT=i%target-ptrsize +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios diff --git a/test/IRGen/synthesized_conformance_future.swift b/test/IRGen/synthesized_conformance_future.swift index b71c03a18dd4e..6b70b84e21925 100644 --- a/test/IRGen/synthesized_conformance_future.swift +++ b/test/IRGen/synthesized_conformance_future.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -target %module-target-future -emit-ir %s -swift-version 4 | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios // UNSUPPORTED: CPU=armv7 && OS=ios // UNSUPPORTED: CPU=armv7s && OS=ios From 8238d2c301d99833a6c6df70cf0226583c94f5db Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 5 Feb 2020 14:25:36 -0800 Subject: [PATCH 181/237] CI: adjust the temporary drive, unzip path This uses `T:` for the temporary drive instead of `S:`. This is needed to ensure that the builds on some machines function which already have the `S` drive in use. Use an absolute path to `unzip` to allow us to use the `unzip` from the Git installation rather than relying on MinGW to provide one. --- utils/build-windows.bat | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 92752877fdeaf..704b2296cce64 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -45,9 +45,9 @@ mkdir %full_build_root% :: Use the shortest path we can for the build directory, to avoid Windows :: path problems as much as we can. -subst S: /d -subst S: %full_build_root% %exitOnError% -set build_root=S: +subst T: /d +subst T: %full_build_root% %exitOnError% +set build_root=T: set install_directory=%build_root%\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr call :clone_repositories %exitOnError% @@ -104,7 +104,7 @@ set file_name=icu4c-%icu_version%-Win64-MSVC2017.zip curl -L -O "https://github.com/unicode-org/icu/releases/download/release-%icu_version_dashed%/%file_name%" %exitOnError% :: unzip warns about the paths in the zip using slashes, which raises the :: errorLevel to 1. We cannot use exitOnError, and have to ignore errors. -unzip -o %file_name% -d "%source_root%\icu-%icu_version%" +"%ProgramFiles%\Git\usr\bin\unzip.exe" -o %file_name% -d "%source_root%\icu-%icu_version%" exit /b 0 goto :eof @@ -118,7 +118,7 @@ setlocal enableextensions enabledelayedexpansion set file_name=sqlite-amalgamation-3270200.zip curl -L -O "https://www.sqlite.org/2019/%file_name%" %exitOnError% -unzip -o %file_name% %exitOnError% +"%ProgramFiles%\Git\usr\bin\unzip.exe" -o %file_name% %exitOnError% goto :eof endlocal From b1761fbe058a3ed6d126a3933e60f03f1fe75c5a Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 5 Feb 2020 14:59:38 -0800 Subject: [PATCH 182/237] CI: tweak git config handling Tweak the git configuration handling on Windows to alter the configuration for the particular repository rather than globally. --- utils/build-windows.bat | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 704b2296cce64..0d0ce6aff3887 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -80,7 +80,10 @@ endlocal :: It supposes the %CD% is the source root. setlocal enableextensions enabledelayedexpansion -git config --global core.autocrlf false +git -C "%source_root%\swift" config --local core.autocrlf input +git -C "%source_root%\swift" config --local core.symlink true +git -C "%source_root%\swift" checkout HEAD + git clone --depth 1 --single-branch https://github.com/apple/swift-cmark cmark %exitOnError% git clone --depth 1 --single-branch --branch swift/master https://github.com/apple/llvm-project llvm-project %exitOnError% mklink /D "%source_root%\clang" "%source_root%\llvm-project\clang" From 102fe353b7c575c268e81cc7cacc0f0cfe343b7d Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 5 Feb 2020 15:00:23 -0800 Subject: [PATCH 183/237] CI: disable python support in lldb LLDB is currently not running tests, and the python detection logic is atrociously broken. Simply disable the python support until that is fixed (which is already done in upstream). --- utils/build-windows.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 0d0ce6aff3887..4fd36ccb2f264 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -290,6 +290,7 @@ cmake "%source_root%\lldb"^ -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -DLLDB_DISABLE_PYTHON=YES^ -DLLDB_INCLUDE_TESTS:BOOL=NO %exitOnError% popd From fd88645cbae2698cd4c0ee50991e425c01e395e2 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Fri, 24 Jan 2020 18:26:09 +0100 Subject: [PATCH 184/237] Added a document that explains how Swift imports C modules --- docs/HowSwiftImportsCAPIs.md | 1050 ++++++++++++++++++++++++++++++++++ 1 file changed, 1050 insertions(+) create mode 100644 docs/HowSwiftImportsCAPIs.md diff --git a/docs/HowSwiftImportsCAPIs.md b/docs/HowSwiftImportsCAPIs.md new file mode 100644 index 0000000000000..02f45d8b86f57 --- /dev/null +++ b/docs/HowSwiftImportsCAPIs.md @@ -0,0 +1,1050 @@ +# How Swift imports C APIs + +When Swift imports a module or parses a bridging header from a C-based language +(C, Objective-C), the APIs are mapped into Swift APIs and can be used directly +from Swift code. This provides the basis for Swift's Foreign Function Interface +(FFI), providing interoperability with existing libraries written in C-based +languages. This document describes how APIs from C-based languages are mapped +into Swift APIs. + +this document is written for a broad audience, including Swift and C users who +might not be language experts. Therefore, it explains some advanced concepts +where necessary. + +* [Names, identifiers and keywords](#names-identifiers-and-keywords) + * [Unicode](#unicode) + * [Names that are keywords in Swift](#names-that-are-keywords-in-swift) + * [Name translation](#name-translation) + * [Name customization](#name-customization) +* [Size, stride, and alignment of types](#size-stride-and-alignment-of-types) +* [Fundamental types](#fundamental-types) +* [Free functions](#free-functions) + * [Argument labels](#argument-labels) + * [Variadic arguments](#variadic-arguments) + * [Inline functions](#inline-functions) +* [Global variables](#global-variables) +* [Pointers to data](#pointers-to-data) +* [Nullable and non-nullable pointers](#nullable-and-non-nullable-pointers) +* [Incomplete types and pointers to them](#incomplete-types-and-pointers-to-them) +* [Function pointers](#function-pointers) +* [Fixed-size arrays](#fixed-size-arrays) +* [Structs](#structs) +* [Unions](#unions) +* [Enums](#enums) +* [Typedefs](#typedefs) +* [Macros](#macros) + +# Names, identifiers and keywords + +## Unicode + +C (and C++) permit non-ASCII Unicode code points in identifiers. While Swift +does not permit arbitrary Unicode code points in identifiers (so compatibility +might not be perfect), it tries to allow reasonable ones. However, C, +Objective-C, and C++ code in practice does not tend to use non-ASCII code points +in identifiers, so mapping them to Swift is not an important concern. + +## Names that are keywords in Swift + +Some C and C++ identifiers are keywords in Swift. Despite that, such names are +imported as-is into Swift, because Swift permits escaping keywords to use them +as identifiers: + +```c +// C header. + +// The name of this function is a keyword in Swift. +void func(); +``` + +```swift +// C header imported in Swift. + +// The name of the function is still `func`, but it is escaped to make the +// keyword into an identifier. +func `func`() +``` + +```swift +// Swift user. + +func test() { + // Call the C function declared above. + `func`() +} +``` + +## Name translation + +Names of some C declarations appear in Swift differently. In particular, names +of enumerators (enum constants) go through a translation process that is +hardcoded into the compiler. For more details, see [Name Translation from C to +Swift](CToSwiftNameTranslation.md). + +## Name customization + +As a general principle of Swift/C interoperability, the C API vendor has broad +control over how their APIs appear in Swift. In particular, the vendor can use +the `swift_name` Clang attribute to customize the names of their C APIs in order +to be more idiomatic in Swift. For more details, see [Name Translation from C to +Swift](CToSwiftNameTranslation.md). + +# Size, stride, and alignment of types + +In C, every type has a size (computed with the `sizeof` operator), and an +alignment (computed with `alignof`). + +In Swift, types have size, stride, and alignment. + +The concept of alignment in C and Swift is exactly the same. + +Size and stride is more complicated. In Swift, stride is the distance between +two elements in an array. The size of a type in Swift is the stride minus the +tail padding. For example: + +```swift +struct SwiftStructWithPadding { + var x: Int16 + var y: Int8 +} + +print(MemoryLayout.size) // 3 +print(MemoryLayout.stride) // 4 +``` + +C's concept of size corresponds to Swift's stride (not size!) C does not have an +equivalent of Swift's size. + +Swift tracks the exact size of the data stored in a type so that it can pack +additional data into bytes that otherwise would be wasted as padding. Swift also +tracks possible and impossible bit patterns for each type, and reuses impossible +bit patterns to encode more information, similarly to `llvm::PointerIntPair` and +`llvm::PointerUnion`. The language does this automatically, and transparently +for users. For example: + +```swift +// This enum takes 1 byte in memory, which has 256 possible bit patterns. +// However, only 2 bit patterns are used. +enum Foo { + case A + case B +} + +print(MemoryLayout.size) // 1 +print(MemoryLayout.size) // also 1: `nil` is represented as one of the 254 bit patterns that are not used by `Foo.A` or `Foo.B`. +``` + +Nevertheless, for types imported from C, the size and the stride are equal. + +```c +// C header. + +struct CStructWithPadding { + int16_t x; + int8_t y; +}; +``` + +```swift +// C header imported in Swift. + +struct CStructWithPadding { + var x: Int16 + var y: Int8 +} +``` + +``` +print(MemoryLayout.size) // 4 +print(MemoryLayout.stride) // 4 +``` + +# Fundamental types + +In C, certain types (`char`, `int`, `float` etc.) are built into the language +and into the compiler. These builtin types have behaviors that are not possible +to imitate in user-defined types (e.g., usual arithmetic conversions). + +C and C++ standard libraries provide headers that define character and integer +types that are typedefs to one of the underlying builtin types, for example, +`int16_t`, `size_t`, and `ptrdiff_t`. + +Swift does not have such builtin types. Swift's equivalents to C's fundamental +types are defined in the Swift standard library as ordinary structs. They are +implemented using compiler intrinsics, but the API surface is defined in +ordinary Swift code: + +```swift +// From the Swift standard library: + +struct Int32 { + internal var _value: Builtin.Int32 + + // Note: `Builtin.Xyz` types are only accessible to the standard library. +} + +func +(lhs: Int32, rhs: Int32) -> Int32 { + return Int32(_value: Builtin.add_Int32(lhs._value, rhs._value)) +} +``` + +Memory layout of these "fundamental" Swift types does not have any surprises, +hidden vtable pointers, metadata, or reference counting; it is exactly what you +expect from a corresponding C type: just the data, stored inline. For example, +Swift's `Int32` is a contiguous chunk of four bytes, all of which store the +number. + +Fundamental types in C, with a few exceptions, have an implementation-defined +size, alignment, and stride (distance between two array elements). + +Swift's integer and floating point types have fixed size, alignment and stride +across platforms, with two exceptions: `Int` and `UInt`. The sizes of `Int` and +`UInt` match the size of the pointer on the platform, similarly to how `size_t`, +`ptrdiff_t`, `intptr_t`, and `uintptr_t` have the same size as a pointer in most +C implementations. `Int` and `UInt` are distinct types, they are not typealiases +to explicitly-sized types. An explicit conversion is required to convert between +`Int`, `UInt`, and any other integer type, even if sizes happen to match on the +current platform. + +The table below summarizes mapping between fundamental types of C and C++ and +Swift types. This table is based on +[`swift.git/include/swift/ClangImporter/BuiltinMappedTypes.def`](../include/swift/ClangImporter/BuiltinMappedTypes.def). + +| C and C++ types | Swift types | +| ------------------------------------ | ----------- | +| C `_Bool`, C++ `bool` | `typealias CBool = Bool` | +| `char`, regardless if the target defines it as signed or unsigned | `typealias CChar = Int8` | +| `signed char`, explicitly signed | `typealias CSignedChar = Int8` | +| `unsigned char`, explicitly unsigned | `typealias CUnsignedChar = UInt8` | +| `short`, `signed short` | `typealias CShort = Int16` | +| `unsigned short` | `typealias CUnsignedShort = UInt16` | +| `int`, `signed int` | `typealias CInt = Int32` | +| `unsigned int` | `typealias CUnsignedInt = UInt32` | +| `long`, `signed long` | Windows x86\_64: `typealias CLong = Int32`
Everywhere else: `typealias CLong = Int` | +| `unsigned long` | Windows x86\_64: `typealias CUnsignedLong = UInt32`
Everywhere else: `typealias CUnsignedLong = UInt` | +| `long long`, `signed long long` | `typealias CLongLong = Int64` | +| `unsigned long long` | `typealias CUnsignedLongLong = UInt64` | +| `wchar_t`, regardless if the target defines it as signed or unsigned | `typealias CWideChar = Unicode.Scalar`
`Unicode.Scalar` is a wrapper around `UInt32` | +| `char8_t` (proposed for C++20) | Not mapped | +| `char16_t` | `typealias CChar16 = UInt16` | +| `char32_t` | `typealias CChar32 = Unicode.Scalar` | +| `float` | `typealias CFloat = Float` | +| `double` | `typealias CDouble = Double` | +| `long double` | `CLongDouble`, which is a typealias to `Float80` or `Double`, depending on the platform.
There is no support for 128-bit floating point. | + +First of all, notice that C types are mapped to Swift typealiases, not directly +to the underlying Swift types. The names of the typealiases are based on the +original C types. These typealiases allow developers to easily write Swift code +and APIs that work with APIs and data imported from C without a lot of `#if` +conditions. + +This table is generally unsurprising: C types are mapped to corresponding +explicitly-sized Swift types, except for the C `long`, which is mapped to `Int` +on 32-bit and 64-bit LP64 platforms. This was done to enhance portability +between 32-bit and 64-bit code. + +The difficulty with the C `long` type is that it can be 32-bit or 64-bit +depending on the platform. If C `long` was mapped to an explicitly-sized Swift +type, it would map to different Swift types on different platforms, making it +more difficult to write portable Swift code (the user would have to compile +Swift code for both platforms to see all errors; fixing compilation errors on +one platform can break another platform.) By mapping `long` a distinct type, +`Int`, the language forces the user to think about both cases when compiling for +either platform. + +Nevertheless, mapping C `long` to `Int` does not work universally. +Specifically, it does not work on LLP64 platforms (for example, Windows +x86\_64), where C `long` is 32-bit and Swift's `Int` is 64-bit. On LLP64 +platforms, C `long` is mapped to Swift's explicitly-sized `Int32`. + +Typedefs for integer types in the C standard library are mapped like this (from +[`swift.git/swift/lib/ClangImporter/MappedTypes.def`](../lib/ClangImporter/MappedTypes.def): + +| C and C++ types | Swift types | +| ---------------- | ----------- | +| `uint8_t` | `UInt8` | +| `uint16_t` | `UInt16` | +| `uint32_t` | `UInt32` | +| `uint64_t` | `UInt64` | +| `int8_t` | `Int8` | +| `int16_t` | `Int16` | +| `int32_t` | `Int32` | +| `int64_t` | `Int64` | +| `intptr_t` | `Int` | +| `uintptr_t` | `UInt` | +| `ptrdiff_t` | `Int` | +| `size_t` | `Int` | +| `rsize_t` | `Int` | +| `ssize_t` | `Int` | + +```c +// C header. + +double Add(int x, long y); +``` + +```swift +// C header imported in Swift. + +func Add(_ x: CInt, _ y: CLong) -> CDouble +``` + +# Free functions + +C functions are imported as free functions in Swift. Each type in the signature +of the C function is mapped to the corresponding Swift type. + +## Argument labels + +Imported C functions don't have argument labels in Swift by default. Argument +labels can be added by API owners through annotations in the C header. + +```c +// C header. + +#define SWIFT_NAME(X) __attribute__((swift_name(#X))) + +// No argument labels by default. +void drawString(const char *, int xPos, int yPos); + +// The attribute specifies the argument labels. +void drawStringRenamed(const char *, int xPos, int yPos) + SWIFT_NAME(drawStringRenamed(_:x:y:)); +``` + +```swift +// C header imported in Swift. + +func drawString(_: UnsafePointer!, _ xPos: Int, _ yPos: Int) +func drawStringRenamed(_: UnsafePointer!, x: Int, y: Int) + +drawString("hello", 10, 20) +drawStringRenamed("hello", x: 10, y: 20) +``` + +## Variadic arguments + +C functions with variadic arguments are not imported into Swift, however, there +are no technical reasons why they can't be imported. + +Note that functions with `va_list` arguments are imported into Swift. `va_list` +corresponds to `CVaListPointer` in Swift. + +C APIs don't define a lot of variadic functions, so this limitation has not +caused a big problem so far. + +Often, for each variadic function there is a corresponding function that takes a +`va_list` which can be called from Swift. A motivated developer can write an +overlay that exposes a Swift variadic function that looks just like the C +variadic function, and implement it in terms of the `va_list`-based C API. You +can find examples of such overlays and wrappers by searching for usages of the +`withVaList` function in the [`swift.git/stdlib`](../stdlib) directory. + +See also Apple's documentation about this topic: [Use a CVaListPointer to Call +Variadic +Functions](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_functions_in_swift#2992073). + +## Inline functions + +Inline C functions that are defined in headers are imported as regular Swift +functions. However, unlike free functions, inline functions require the caller +to emit a definition of the function, because no other translation unit is +guaranteed to provide a definition. + +Therefore, the Swift compiler uses Clang's CodeGen library to emit LLVM IR for +the C inline function. LLVM IR for C inline functions and LLVM IR for Swift code +is put into one LLVM module, allowing all LLVM optimizations (like inlining) to +work transparently across language boundaries. + +# Global variables + +Global C variables are imported as Swift variables or constants, depending on +constness. + +```c +// C header. + +extern int NumAlpacas; +extern const int NumLlamas; +``` + +```swift +// C header imported in Swift. + +var NumAlpacas: CInt +let NumLlamas: CInt +``` + +# Pointers to data + +C has one way to form a pointer to a value of type `T` -- `T*`. + +Swift language does not provide a builtin pointer type. The standard library +defines multiple pointer types: + +* `UnsafePointer`: equivalent to `const T*` in C. + +* `UnsafeMutablePointer`: equivalent to `T*` in C, where `T` is non-const. + +* `UnsafeRawPointer`: a pointer for accessing data that is not statically typed, + similar to `const void*` in C. Unlike C pointer types, `UnsafeRawPointer` + allows type punning, and provides special APIs to do it correctly and safely. + +* `UnsafeMutableRawPointer`: like `UnsafeRawPointer`, but can mutate the data it + points to. + +* `OpaquePointer`: a pointer to typed data, however the type of the pointee is + not known, for example, it is determined by the value of some other variable, + or the type of the pointee is not representable in Swift. + +* `AutoreleasingUnsafeMutablePointer`: only used for Objective-C + interoperability; corresponds to an Objective-C pointer T `__autoreleasing *`, + where `T` is an Objective-C pointer type. + +C pointer types can be trivially imported in Swift as long as the memory layout +of the pointee is identical in C and Swift. So far, this document only described +primitive types, whose memory layout in C and Swift is indeed identical, for +example, `char` in C and `Int8` in Swift. Pointers to such types are trivial to +import into Swift, for example, `char*` in C corresponds to +`UnsafeMutablePointer!` in Swift. + +```c +// C header. + +void AddSecondToFirst(int *x, const long *y); +``` + +```swift +// C header imported in Swift. + +func AddSecondToFirst(_ x: UnsafeMutablePointer!, _ y: UnsafePointer!) +``` + +# Nullable and non-nullable pointers + +Any C pointer can be null. However, in practice, many pointers are never null. +Therefore, code often does not expect certain pointers to be null and does not +handle null values gracefully. + +C does not provide a way to distinguish nullable and non-nullable pointers. +However, Swift makes this distinction: all pointer types (for example, +`UnsafePointer` and `UnsafeMutablePointer`) are non-nullable. Swift +represents the possibility of a missing value with a type called "Optional", +spelled `T?` in the shorthand form, or `Optional` fully. The missing value is +called "nil". For example, `UnsafePointer?` (shorthand for +`Optional>`) can store a nil value. + +Swift also provides a different syntax for declaring an optional, `T!`, which +creates a so-called "implicitly unwrapped optional". These optionals are +automatically checked for nil and unwrapped if it is necessary for the +expression to compile. Unwrapping a `T?` or a `T!` optional that contains nil is +a fatal error (the program is terminated with an error message). + +Formally, since any C pointer can be null, C pointers must be imported as +optional unsafe pointers in Swift. However, that is not idiomatic in Swift: +optional should be used when value can be truly missing, and when it is +meaningful for the API. C APIs do not provide this information in a +machine-readable form. Information about which pointers are nullable is +typically provided in free-form documentation for C APIs, if it is provided at +all. + +Clang implements an [extension to the C language that allows C API vendors to +annotate pointers as nullable or +non-nullable](https://clang.llvm.org/docs/AttributeReference.html#nullability-attributes). + +Quoting the Clang manual: + +> The `_Nonnull` nullability qualifier indicates that null is not a meaningful +> value for a value of the `_Nonnull` pointer type. For example, given a +> declaration such as: + +```c +int fetch(int * _Nonnull ptr); +``` + +> a caller of `fetch` should not provide a null value, and the compiler will +> produce a warning if it sees a literal null value passed to fetch. Note that, +> unlike the declaration attribute `nonnull`, the presence of `_Nonnull` does +> not imply that passing null is undefined behavior: `fetch` is free to consider +> null undefined behavior or (perhaps for backward-compatibility reasons) +> defensively handle null. + +`_Nonnull` C pointers are imported in Swift as non-optional `UnsafePointer` +or `UnsafeMutablePointer`, depending on the constness of the pointer. + +```swift +// C declaration above imported in Swift. + +func fetch(_ ptr: UnsafeMutablePointer) -> CInt +``` + +Quoting the Clang manual: + +> The `_Nullable` nullability qualifier indicates that a value of the +> `_Nullable` pointer type can be null. For example, given: + +```c +int fetch_or_zero(int * _Nullable ptr); +``` + +> a caller of `fetch_or_zero` can provide null. + +`_Nullable` pointers are imported in Swift as `UnsafePointer?` or +`UnsafeMutablePointer?`, depending on the constness of the pointer. + +```swift +// C declaration above imported in Swift. + +func fetch_or_zero(_ ptr: UnsafeMutablePointer?) -> CInt +``` + +Quoting the Clang manual: + +> The `_Null_unspecified` nullability qualifier indicates that neither the +> `_Nonnull` nor `_Nullable` qualifiers make sense for a particular pointer +> type. It is used primarily to indicate that the role of null with specific +> pointers in a nullability-annotated header is unclear, e.g., due to +> overly-complex implementations or historical factors with a long-lived API. + +`_Null_unspecified` and not annotated C pointers are imported in Swift as +implicitly-unwrapped optional pointers, `UnsafePointer!` or +`UnsafeMutablePointer!`. This strategy provides ergonomics equivalent to the +original C API (no need to explicitly unwrap), and safety expected by Swift code +(a dynamic check for null during implicit unwrapping). + +These qualifiers do not affect program semantics in C and C++, allowing C API +vendors to safely add them to headers for the benefit of Swift users without +disturbing existing C and C++ users. + +In C APIs most pointers are non-nullable. To reduce the annotation burden, Clang +provides a way to mass-annotate pointers as non-nullable, and then mark +exceptions with `_Nullable`. + +```c +// C header. + +void Func1(int * _Nonnull x, int * _Nonnull y, int * _Nullable z); + +#pragma clang assume_nonnull begin + +void Func2(int *x, int *y, int * _Nullable z); + +#pragma clang assume_nonnull end +``` + +```swift +// C header imported in Swift. + +// Note that `Func1` and `Func2` are imported identically, but `Func2` required +// fewer annotations in the C header. + +func Func1( + _ x: UnsafeMutablePointer, + _ y: UnsafeMutablePointer, + _ z: UnsafeMutablePointer? +) + +func Func2( + _ x: UnsafeMutablePointer, + _ y: UnsafeMutablePointer, + _ z: UnsafeMutablePointer? +) +``` + +API owners that adopt nullability qualifiers usually wrap all declarations in +the header with a single `assume_nonnull begin/end` pair of pragmas, and then +annotate nullable pointers. + +See also Apple's documentation about this topic: [Designating Nullability in +Objective-C +APIs](https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/designating_nullability_in_objective-c_apis). + +# Incomplete types and pointers to them + +C and C++ have a notion of incomplete types; Swift does not have anything +similar. Incomplete C types are not imported in Swift in any form. + +Sometimes types are incomplete only accidentally, for example, when a file just +happens to forward declare a type instead of including a header with a complete +definition, although it could include that header. In cases like that, to enable +Swift to import the C API, it is recommended to change C headers and to replace +forward declarations with `#include`s of the header that defines the type. + +Incomplete types are often used intentionally to define opaque types. This is +done too often, and Swift could not ignore this use case. Swift imports +pointers to incomplete types as `OpaquePointer`. For example: + +```c +// C header. + +struct Foo; +void Print(const Foo* foo); +``` + +```swift +// C header imported in Swift. + +// Swift can't import the incomplete type `Foo` and has to drop some type +// information when importing a pointer to `Foo`. +func Print(_ foo: OpaquePointer) +``` + +# Function pointers + +C supports only one form of function pointers: `Result (*)(Arg1, Arg2, Arg3)`. + +Swift's closest native equivalent to a function pointer is a closure: `(Arg1, +Arg2, Arg3) -> Result`. Swift closures don't have the same memory layout as C +function pointers: closures in Swift consist of two pointers, a pointer to the +code and a pointer to the captured data (the context). A C function pointer can +be converted to a Swift closure; however, bridging is required to adjust the +memory layout. + +As discussed above, there are cases where bridging that adjusts memory layout is +not possible, for example, when importing pointers to function pointers. For +example, while C's int `(*)(char)` can be imported as `(Int8) -> Int` (requires +an adjustment of memory layout), C's `int (**)(char)` can't be imported as +`UnsafePointer<(Int8) -> Int>`, because the pointee must have identical memory +layout in C and in Swift. + +Therefore, we need a Swift type that has a memory layout identical to C function +pointers, at least for such fallback cases. This type is spelled `@convention(c) +(Arg1, Arg2, Arg3) -> Result`. + +Even though it is possible to import C function pointers as Swift closure with a +context pointer in some cases, C function pointers are always imported as +`@convention(c)` "closures" (in quotes because they don't have a context +pointer, so they are not real closures). Swift provides an implicit conversion +from `@convention(c)` closures to Swift closures with a context. + +Importing C function pointers also takes pointer nullability into account: a +nullable C function pointer is imported as optional. + +```c +// C header. + +void qsort( + void *base, + size_t nmemb, + size_t size, + int (*compar)(const void *, const void *)); + +void qsort_annotated( + void * _Nonnull base, + size_t nmemb, + size_t size, + int (* _Nonnull compar)(const void * _Nonnull, const void * _Nonnull)); +``` + +```swift +// C header imported in Swift. + +func qsort( + _ base: UnsafeMutableRawPointer!, + _ nmemb: CInt, + _ size: CInt, + _ compar: (@convention(c) (UnsafeRawPointer?, UnsafeRawPointer?) -> CInt)! +) + +func qsort_annotated( + _ base: UnsafeMutableRawPointer, + _ nmemb: CInt, + _ size: CInt, + _ compar: @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> CInt +) +``` + +See also Apple's documentation about this topic: [Using Imported C Functions in +Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_functions_in_swift) + +# Fixed-size arrays + +C's fixed-size arrays are imported as Swift tuples. + +```c +// C header. + +extern int x[4]; +``` + +```swift +// C header imported in Swift. + +var x: (CInt, CInt, CInt, CInt) { get set } +``` + +This mapping strategy is widely recognized as being far from optimal because +ergonomics of Swift tuples does not match C's fixed-size arrays. For example, +Swift tuples cannot be accessed through an index that only becomes known at +runtime. If you need to access a tuple element by index, you have to get an +unsafe pointer to values in a homogeneous tuple with +`withUnsafeMutablePointer(&myTuple) { ... }`, and then perform pointer +arithmetic. + +Fixed-size arrays are a commonly requested feature in Swift, and a good proposal +is likely to be accepted. Once Swift has fixed-size arrays natively in the +language, we can use them to improve C interoperability. + +# Structs + +C structs are imported as Swift structs, their fields are mapped to stored Swift +properties. Bitfields are mapped to computed Swift properties. Swift structs +also get a synthesized default initializer (that sets all properties to zero), +and an elementwise initializer (that sets all properties to the provided +values). + +```c +// C header. + +struct Point { + int x; + int y; +}; + +struct Line { + struct Point start; + struct Point end; + unsigned int brush : 4; + unsigned int stroke : 3; +}; +``` + +```swift +// C header imported in Swift. + +struct Point { + var x: CInt { get set } + var y: CInt { get set } + init() + init(x: CInt, y: CInt) +} + +struct Line { + var start: Point { get set } + var end: Point { get set } + var brush: CUnsignedInt { get set } + var stroke: CUnsignedInt { get set } + + // Default initializer that sets all properties to zero. + init() + + // Elementwise initializer. + init(start: Point, end: Point, brush: CUnsignedInt, stroke: CUnsignedInt) +} +``` + +Swift can also import unnamed and anonymous structs. + +```c +// C header. + +struct StructWithAnonymousStructs { + struct { + int x; + }; + struct { + int y; + } containerForY; +}; +``` + +```swift +// C header imported in Swift. +struct StructWithAnonymousStructs { + struct __Unnamed_struct___Anonymous_field0 { + var x: Int32 + init() + init(x: Int32) + } + struct __Unnamed_struct_containerForY { + var y: Int32 + init() + init(y: Int32) + } + var __Anonymous_field0: StructWithAnonymousStructs.__Unnamed_struct___Anonymous_field0 + var x: Int32 + var containerForY: StructWithAnonymousStructs.__Unnamed_struct_containerForY + + // Default initializer that sets all properties to zero. + init() + + // Elementwise initializer. + init( + _ __Anonymous_field0: StructWithAnonymousStructs.__Unnamed_struct___Anonymous_field0, + containerForY: StructWithAnonymousStructs.__Unnamed_struct_containerForY + ) +} +``` + +See also Apple's documentation about this topic: [Using Imported C Structs and +Unions in +Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift). + +# Unions + +Swift does not have a direct equivalent to a C union. C unions are mapped to +Swift structs with computed properties that read from/write to the same +underlying storage. + +```c +// C header. + +union IntOrFloat { + int i; + float f; +}; +``` + +```swift +// C header imported in Swift. + +struct IntOrFloat { + var i: CInt { get set } // Computed property. + var f: Float { get set } // Computed property. + init(i: CInt) + init(f: Float) + init() +} +``` + +See also Apple's documentation about this topic: [Using Imported C Structs and +Unions in +Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift). + +# Enums + +We would have liked to map C enums to Swift enums, like this: + +```c +// C header. + +// Enum that is not explicitly marked as either open or closed. +enum HomeworkExcuse { + EatenByPet, + ForgotAtHome, + ThoughtItWasDueNextWeek, +}; +``` + +```swift +// C header imported in Swift: aspiration, not an actual mapping! + +enum HomeworkExcuse : UInt32 { + case EatenByPet + case ForgotAtHome + case ThoughtItWasDueNextWeek +} +``` + +However, in practice, plain C enums are mapped to Swift structs like this: + +```swift +// C header imported in Swift: actual mapping. + +struct HomeworkExcuse : Equatable, RawRepresentable { + init(_ rawValue: UInt32) + init(rawValue: UInt32) + var rawValue: UInt32 + typealias RawValue = UInt32 +} +var EatenByPet: HomeworkExcuse { get } +var ForgotAtHome: HomeworkExcuse { get } +var ThoughtItWasDueNextWeek: HomeworkExcuse { get } +``` + +To explain why this mapping was chosen, we need to discuss certain features of C +enums: + +* In C, adding new enumerators is not source-breaking; in Itanium C ABI, it is + not ABI breaking either as long as the size of the enum does not change. + Therefore, C library vendors add enumerators without expecting downstream + breakage. + +* In C, it is common to use enums as bitfields: enumerators are assigned values + that are powers of two, and enum values are bitwise-or combinations of + enumerators. + +* Some C API vendors define two sets of enumerators: "public" enumerators that + are listed in the header file, and "private" enumerators that are used only in + the implementation of the library. + +Due to these coding patterns, at runtime, C enums can carry values that were not +listed in the enum declaration at compile time (either such values were added +after the code was compiled, or they are a result of an intentional cast from an +integer to an enum). + +Swift compiler performs exhaustiveness checks for switch statements, which +becomes problematic when performed on C enums, where expectations about +exhaustiveness are different. + +From Swift's point of view, C enums come in two flavors: closed AKA frozen, and +open AKA non-frozen. This distinction is aimed at supporting library evolution +and ABI stability, while allowing the user to ergonomically work with their +code. Swift's solution also supports the unusual C enum coding patterns. + +Frozen enums have a fixed set of cases (enumerators in C terms). A library +vendor can change (add or remove) cases in a frozen enum, however, it will be +both ABI-breaking and source-breaking. In other words, there is a guarantee that +the set of enum cases that was seen at compile time exactly matches the set of +values that an enum variable can carry at runtime. Swift performs an +exhaustiveness check for switch statements on frozen enums: if switch does not +handle all enum cases, the user gets a warning. Moreover, the optimizer can make +an assumption that a variable that has a frozen enum type will only store values +that correspond to enum cases visible at compile time; unused bit patterns can +be reused for other purposes. + +Non-frozen enums have an extensible set of cases. A library vendor can add cases +without breaking ABI or source compatibility. Swift performs a different flavor +of exhaustiveness check for switch statements on non-frozen enums: it always +requires an `@unknown default` clause, but only produces a warning if the code +does not handle all cases available at the compilation time. + +```c +// C header. + +// Enum that is not explicitly marked as either open or closed. +enum HomeworkExcuse { + EatenByPet, + ForgotAtHome, + ThoughtItWasDueNextWeek, +}; + +// An open enum: we expect to add more kinds of input devices in future. +enum InputDevice { + Keyboard, + Mouse, + Touchscreen, +} __attribute__((enum_extensibility(open))); + +// A closed enum: we think we know enough about the geometry of Earth to +// confidently say that these are all cardinal directions we will ever need. +enum CardinalDirection { + East, + West, + North, + South, +} __attribute__((enum_extensibility(closed))); +``` + +```swift +// C header imported in Swift. + +struct HomeworkExcuse : Equatable, RawRepresentable { + init(_ rawValue: UInt32) + init(rawValue: UInt32) + var rawValue: UInt32 + typealias RawValue = UInt32 +} +var EatenByPet: HomeworkExcuse { get } +var ForgotAtHome: HomeworkExcuse { get } +var ThoughtItWasDueNextWeek: HomeworkExcuse { get } + +enum InputDevice : UInt32 { + init?(rawValue: UInt32) + var rawValue: UInt32 { get } + typealias RawValue = UInt32 + case Keyboard + case Mouse + case Touchscreen +} + +@_frozen +enum CardinalDirection : UInt32 { + init?(rawValue: UInt32) + var rawValue: UInt32 { get } + typealias RawValue = UInt32 + case East + case West + case North + case South +} +``` + +C enums that are not marked as open or closed are mapped to structs since Swift +1.0. At that time, we realized that mapping C enums to Swift enums is not +correct if the Swift compiler makes Swift-like assumptions about such imported +enums. Specifically, Swift compiler assumes that the only allowed bit patterns +of an enum value are declared in enum's cases. This assumption is valid for +frozen Swift enums (which were the only flavor of enums in Swift 1.0). However, +this assumption does not hold for C enums, for which any bit pattern is a valid +value; this assumption was creating an undefined behavior hazard. To resolve +this issue in Swift 1.0, C enums were imported as Swift structs by default, and +their enumerators were exposed as global computed variables. Objective-C enums +declared with `NS_ENUM` were assumed to have "enum nature" and were imported as +Swift enums. + +The concept of open enums was added in Swift 5 ([SE-0192 Handling Future +Enum +Cases](https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md)), +but that proposal did not change the importing strategy of non-annotated C +enums, in part because of source compatibility concerns. It might be still +possible to change C enums to be imported as open Swift enums, but as the time +passes, it will be more difficult to change. + +Another feature of C enums is that they expose integer values to the user; +furthermore, enum values are implicitly convertible to integers. Swift enums are +opaque by default. When imported in Swift, C enums conform to the +`RawRepresentable` protocol, allowing the user to explicitly convert between +numeric and typed values. + +```swift +// Converting enum values to integers and back. + +var south: CardinalDirection = .South +// var southAsInteger: UInt32 = south // error: type mismatch +var southAsInteger: UInt32 = south.rawValue // = 3 +var southAsEnum = CardinalDirection(rawValue: 3) // = South +``` + +# Typedefs + +C typedefs are generally mapped to Swift typealiases, except for a few common C +coding patterns that are handled in a special way. + +```c +// C header. + +// An ordinary typedef. +typedef int Money; + +// A special case pattern that is mapped to a named struct. +typedef struct { + int x; + int y; +} Point; +``` + +```swift +// C header imported in Swift. + +typealias Money = Int + +struct Point { + var x: CInt { get set } + var y: CInt { get set } + init() + init(x: CInt, y: CInt) +} +``` + +# Macros + +C macros are generally not imported in Swift. Macros that define constants are +imported as readonly variables. + +```c +// C header. + +#define BUFFER_SIZE 4096 +#define SERVER_VERSION "3.14" +``` + +```swift +// C header imported in Swift. + +var BUFFER_SIZE: CInt { get } +var SERVER_VERSION: String { get } +``` + +See also Apple's documentation about this topic: [Using Imported C Macros in +Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_macros_in_swift). From b3750a7c137aa411225e6ac7d558f0e51ee02c4a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Jan 2020 17:07:06 -0800 Subject: [PATCH 185/237] Add a PrettyStackTrace for working with Clang types. --- include/swift/AST/PrettyStackTrace.h | 15 +++++++++++++++ lib/AST/PrettyStackTrace.cpp | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/swift/AST/PrettyStackTrace.h b/include/swift/AST/PrettyStackTrace.h index b18b8c7a3fa41..0714bd6c83e96 100644 --- a/include/swift/AST/PrettyStackTrace.h +++ b/include/swift/AST/PrettyStackTrace.h @@ -24,6 +24,10 @@ #include "swift/AST/Identifier.h" #include "swift/AST/Type.h" +namespace clang { + class Type; +} + namespace swift { class ASTContext; class Decl; @@ -132,6 +136,17 @@ class PrettyStackTraceType : public llvm::PrettyStackTraceEntry { virtual void print(llvm::raw_ostream &OS) const; }; +/// PrettyStackTraceClangType - Observe that we are processing a +/// specific Clang type. +class PrettyStackTraceClangType : public llvm::PrettyStackTraceEntry { + const clang::Type *TheType; + const char *Action; +public: + PrettyStackTraceClangType(const char *action, const clang::Type *type) + : TheType(type), Action(action) {} + virtual void print(llvm::raw_ostream &OS) const; +}; + /// Observe that we are processing a specific type representation. class PrettyStackTraceTypeRepr : public llvm::PrettyStackTraceEntry { ASTContext &Context; diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index 893a83502d1fd..750e661c66af1 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -27,6 +27,7 @@ #include "swift/AST/TypeRepr.h" #include "swift/AST/TypeVisitor.h" #include "swift/Basic/SourceManager.h" +#include "clang/AST/Type.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/MemoryBuffer.h" @@ -209,6 +210,15 @@ void swift::printTypeDescription(llvm::raw_ostream &out, Type type, if (addNewline) out << '\n'; } +void PrettyStackTraceClangType::print(llvm::raw_ostream &out) const { + out << "While " << Action << ' '; + if (TheType == nullptr) { + out << "NULL clang type!\n"; + return; + } + TheType->dump(out); +} + void PrettyStackTraceTypeRepr::print(llvm::raw_ostream &out) const { out << "While " << Action << " type "; TheType->print(out); From 8679ca24e82ee7c7875d1d8a8e16c496852404d3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 5 Feb 2020 22:26:45 -0800 Subject: [PATCH 186/237] CI: stylistic changes to simplify script Use CMake more effectively to both build and change the working directory. This simplifies the number of steps and reduces possible error points. --- utils/build-windows.bat | 56 +++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 4fd36ccb2f264..ba0ce511ad848 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -146,10 +146,8 @@ endlocal :: Configures, builds, and installs LLVM setlocal enableextensions enabledelayedexpansion -mkdir "%build_root%\llvm" %exitOnError% -pushd "%build_root%\llvm" - -cmake "%source_root%\llvm"^ +cmake^ + -B "%build_root%\llvm"^ -G Ninja^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ -DCMAKE_C_COMPILER=cl^ @@ -175,9 +173,8 @@ cmake "%source_root%\llvm"^ -DCLANG_TOOLS="clang;clang-format;clang-headers;clang-tidy"^ -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO %exitOnError% - -popd + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -S "%source_root%\llvm" %exitOnError% cmake --build "%build_root%\llvm" %exitOnError% cmake --build "%build_root%\llvm" --target install %exitOnError% @@ -190,19 +187,16 @@ endlocal :: Configures and builds CMark setlocal enableextensions enabledelayedexpansion -mkdir "%build_root%\cmark" %exitOnError% -pushd "%build_root%\cmark" - -cmake "%source_root%\cmark"^ +cmake^ + -B "%build_root%\cmark"^ -G Ninja^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ -DCMAKE_C_COMPILER=cl^ -DCMAKE_CXX_COMPILER=cl^ -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO %exitOnError% - -popd + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -S "%source_root%\cmark" %exitOnError% cmake --build "%build_root%\cmark" %exitOnError% @@ -214,12 +208,10 @@ endlocal :: Configures, builds, and installs Swift and the Swift Standard Library setlocal enableextensions enabledelayedexpansion -mkdir "%build_root%\swift" %exitOnError% -pushd "%build_root%\swift" - :: SWIFT_PARALLEL_LINK_JOBS=8 allows the build machine to use as many CPU as :: possible, while not exhausting the RAM. -cmake "%source_root%\swift"^ +cmake^ + -B "%build_root%\swift"^ -G Ninja^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ -DCMAKE_C_COMPILER=cl^ @@ -247,9 +239,8 @@ cmake "%source_root%\swift"^ -DPYTHON_EXECUTABLE:PATH=%PYTHON_HOME%\python.exe^ -DCMAKE_CXX_FLAGS:STRING="/GS- /Oy"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ - -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO %exitOnError% - -popd + -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ + -S "%source_root%\swift" %exitOnError% cmake --build "%build_root%\swift" %exitOnError% cmake --build "%build_root%\swift" --target install %exitOnError% @@ -272,10 +263,8 @@ endlocal :: Configures, builds, and installs LLDB setlocal enableextensions enabledelayedexpansion -mkdir "%build_root%\lldb" %exitOnError% -pushd "%build_root%\lldb" - -cmake "%source_root%\lldb"^ +cmake^ + -B "%build_root%\lldb"^ -G Ninja^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ -DCMAKE_C_COMPILER=clang-cl^ @@ -291,9 +280,8 @@ cmake "%source_root%\lldb"^ -DCMAKE_EXE_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ -DCMAKE_SHARED_LINKER_FLAGS:STRING=/INCREMENTAL:NO^ -DLLDB_DISABLE_PYTHON=YES^ - -DLLDB_INCLUDE_TESTS:BOOL=NO %exitOnError% - -popd + -DLLDB_INCLUDE_TESTS:BOOL=NO^ + -S "%source_root%\lldb" %exitOnError% cmake --build "%build_root%\lldb" %exitOnError% cmake --build "%build_root%\lldb" --target install %exitOnError% @@ -306,10 +294,8 @@ endlocal :: Configures, builds, and installs Dispatch setlocal enableextensions enabledelayedexpansion -mkdir "%build_root%\swift-corelibs-libdispatch" %exitOnError% -pushd "%build_root%\swift-corelibs-libdispatch" - -cmake "%source_root%\swift-corelibs-libdispatch"^ +cmake^ + -B "%build_root%\swift-corelibs-libdispatch"^ -G Ninja^ -DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%^ -DCMAKE_C_COMPILER=clang-cl^ @@ -327,10 +313,8 @@ cmake "%source_root%\swift-corelibs-libdispatch"^ -DCMAKE_SHARED_LINKER_FLAGS:STRING="/INCREMENTAL:NO"^ -DCMAKE_Swift_COMPILER_TARGET:STRING=x86_64-unknown-windows-msvc^ -DCMAKE_Swift_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\""^ - -DCMAKE_Swift_LINK_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\"" %exitOnError% - - -popd + -DCMAKE_Swift_LINK_FLAGS:STRING="-resource-dir \"%install_directory%\lib\swift\""^ + -S "%source_root%\swift-corelibs-libdispatch" %exitOnError% cmake --build "%build_root%\swift-corelibs-libdispatch" %exitOnError% cmake --build "%build_root%\swift-corelibs-libdispatch" --target install %exitOnError% From 96441c112994484aed1ec8940fa21e6013dd7ab0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 5 Feb 2020 15:06:53 -0800 Subject: [PATCH 187/237] [CSFix] Account for nested structural errors related to function argument mismatches When it comes to contextual mismatches `ContextualType` is not guaranteed to be the last element in the patch since contextual type could be a function too which could would have `contextual type -> function argument` path. --- lib/Sema/CSFix.cpp | 14 +++++++++----- test/Constraints/sr10595.swift | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 2eecf54297e86..19f553667c775 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -262,11 +262,15 @@ ContextualMismatch *ContextualMismatch::create(ConstraintSystem &cs, Type lhs, /// and the contextual type. static Optional> getStructuralTypeContext(ConstraintSystem &cs, ConstraintLocator *locator) { - if (auto contextualType = cs.getContextualType(locator->getAnchor())) { - if (auto *anchor = simplifyLocatorToAnchor(locator)) - return std::make_tuple(cs.getContextualTypePurpose(locator->getAnchor()), - cs.getType(anchor), - contextualType); + if (locator->findLast()) { + assert(locator->isLastElement() || + locator->isLastElement()); + + auto *anchor = locator->getAnchor(); + auto contextualType = cs.getContextualType(anchor); + auto exprType = cs.getType(anchor); + return std::make_tuple(cs.getContextualTypePurpose(anchor), exprType, + contextualType); } else if (auto argApplyInfo = cs.getFunctionArgApplyInfo(locator)) { return std::make_tuple(CTP_CallArgument, argApplyInfo->getArgType(), diff --git a/test/Constraints/sr10595.swift b/test/Constraints/sr10595.swift index 9127340c94edd..377228685fc6c 100644 --- a/test/Constraints/sr10595.swift +++ b/test/Constraints/sr10595.swift @@ -15,6 +15,6 @@ class B : Nested { // expected-error {{type 'B' does not conform to protoc class C { func cFunc(_ a: A) { - let function: (B, ReferenceWritableKeyPath) -> Void = a.f // expected-error {{cannot convert value of type '(_, WritableKeyPath) -> ()' to specified type '(B, ReferenceWritableKeyPath) -> Void'}} + let function: (B, ReferenceWritableKeyPath) -> Void = a.f // expected-error {{cannot convert value of type '(B, WritableKeyPath.U>) -> ()' to specified type '(B, ReferenceWritableKeyPath) -> Void'}} } } From 2875aa8e2b52be4e176ebb2d91b26bbbea0da5cd Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 6 Feb 2020 00:27:17 -0800 Subject: [PATCH 188/237] [Diagnostics] Diagnose an attempt to init/use dictionary with array literal in CSGen It's much easier to detect that contextual type is a dictionary but expression is an array literal while generating constraints. --- lib/Sema/CSDiagnostics.cpp | 76 ----------------------- lib/Sema/CSDiagnostics.h | 9 --- lib/Sema/CSGen.cpp | 51 ++++++++++++++- test/Constraints/dictionary_literal.swift | 4 +- 4 files changed, 52 insertions(+), 88 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index ea722ca784485..cfecbcb68ec2a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1891,9 +1891,6 @@ bool ContextualFailure::diagnoseAsError() { if (diagnoseMissingFunctionCall()) return true; - if (diagnoseConversionToDictionary()) - return true; - // Special case of some common conversions involving Swift.String // indexes, catching cases where people attempt to index them with an integer. if (isIntegerToStringIndexConversion()) { @@ -2403,61 +2400,6 @@ bool ContextualFailure::diagnoseConversionToBool() const { return false; } -bool ContextualFailure::isInvalidDictionaryConversion( - ConstraintSystem &cs, Expr *anchor, Type contextualType) { - auto *arrayExpr = dyn_cast(anchor); - if (!arrayExpr) - return false; - - auto type = contextualType->lookThroughAllOptionalTypes(); - if (!conformsToKnownProtocol( - cs, type, KnownProtocolKind::ExpressibleByDictionaryLiteral)) - return false; - - return (arrayExpr->getNumElements() & 1) == 0; -} - -bool ContextualFailure::diagnoseConversionToDictionary() const { - auto &cs = getConstraintSystem(); - auto toType = getToType()->lookThroughAllOptionalTypes(); - - if (!isInvalidDictionaryConversion(cs, getAnchor(), toType)) - return false; - - auto *arrayExpr = cast(getAnchor()); - - // If the contextual type conforms to ExpressibleByDictionaryLiteral and - // this is an empty array, then they meant "[:]". - auto numElements = arrayExpr->getNumElements(); - if (numElements == 0) { - emitDiagnostic(arrayExpr->getStartLoc(), - diag::should_use_empty_dictionary_literal) - .fixItInsert(arrayExpr->getEndLoc(), ":"); - return true; - } - - // If the contextual type conforms to ExpressibleByDictionaryLiteral, then - // they wrote "x = [1,2]" but probably meant "x = [1:2]". - bool isIniting = getContextualTypePurpose() == CTP_Initialization; - emitDiagnostic(arrayExpr->getStartLoc(), diag::should_use_dictionary_literal, - toType, isIniting); - - auto diagnostic = - emitDiagnostic(arrayExpr->getStartLoc(), diag::meant_dictionary_lit); - - // Change every other comma into a colon, only if the number - // of commas present matches the number of elements, because - // otherwise it might a structural problem with the expression - // e.g. ["a""b": 1]. - const auto commaLocs = arrayExpr->getCommaLocs(); - if (commaLocs.size() == numElements - 1) { - for (unsigned i = 0, e = numElements / 2; i != e; ++i) - diagnostic.fixItReplace(commaLocs[i * 2], ":"); - } - - return true; -} - bool ContextualFailure::diagnoseThrowsTypeMismatch() const { // If this is conversion failure due to a return statement with an argument // that cannot be coerced to the result type of the function, emit a @@ -4857,24 +4799,6 @@ bool CollectionElementContextualFailure::diagnoseAsError() { auto *anchor = getAnchor(); auto *locator = getLocator(); - // Check whether this is situation like `let _: [String: Int] = ["A", 0]` - // which attempts to convert an array into a dictionary. We have a tailored - // contextual diagnostic for that, so no need to diagnose element mismatches - // as well. - auto &cs = getConstraintSystem(); - auto *rawAnchor = getRawAnchor(); - if (llvm::any_of(cs.getFixes(), [&](const ConstraintFix *fix) -> bool { - auto *locator = fix->getLocator(); - if (!(fix->getKind() == FixKind::ContextualMismatch && - locator->getAnchor() == rawAnchor)) - return false; - - auto *mismatch = static_cast(fix); - return isInvalidDictionaryConversion(cs, rawAnchor, - mismatch->getToType()); - })) - return false; - auto eltType = getFromType(); auto contextualType = getToType(); diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h index 2f3c878a40777..b91e12efc46cc 100644 --- a/lib/Sema/CSDiagnostics.h +++ b/lib/Sema/CSDiagnostics.h @@ -566,10 +566,6 @@ class ContextualFailure : public FailureDiagnostic { /// Produce a specialized diagnostic if this is an invalid conversion to Bool. bool diagnoseConversionToBool() const; - /// Produce a specialized diagnostic if this is an attempt to initialize - /// or convert an array literal to a dictionary e.g. `let _: [String: Int] = ["A", 0]` - bool diagnoseConversionToDictionary() const; - /// Produce a specialized diagnostic if this is an attempt to throw /// something with doesn't conform to `Error`. bool diagnoseThrowsTypeMismatch() const; @@ -625,11 +621,6 @@ class ContextualFailure : public FailureDiagnostic { /// protocol bool tryProtocolConformanceFixIt(InFlightDiagnostic &diagnostic) const; - /// Check whether this contextual failure represents an invalid - /// conversion from array literal to dictionary. - static bool isInvalidDictionaryConversion(ConstraintSystem &cs, Expr *anchor, - Type contextualType); - private: Type resolve(Type rawType) const { return resolveType(rawType)->getWithoutSpecifierType(); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 071767fe08891..a3bb7d14971bb 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1852,7 +1852,56 @@ namespace { return contextualArrayType; } - + + // Produce a specialized diagnostic if this is an attempt to initialize + // or convert an array literal to a dictionary e.g. + // `let _: [String: Int] = ["A", 0]` + auto isDictionaryContextualType = [&](Type contextualType) -> bool { + if (!contextualType) + return false; + + auto type = contextualType->lookThroughAllOptionalTypes(); + if (conformsToKnownProtocol( + CS, type, KnownProtocolKind::ExpressibleByArrayLiteral)) + return false; + + return conformsToKnownProtocol( + CS, type, KnownProtocolKind::ExpressibleByDictionaryLiteral); + }; + + if (isDictionaryContextualType(contextualType)) { + auto &DE = CS.getASTContext().Diags; + auto numElements = expr->getNumElements(); + + if (numElements == 0) { + DE.diagnose(expr->getStartLoc(), + diag::should_use_empty_dictionary_literal) + .fixItInsert(expr->getEndLoc(), ":"); + return nullptr; + } + + bool isIniting = + CS.getContextualTypePurpose(expr) == CTP_Initialization; + DE.diagnose(expr->getStartLoc(), diag::should_use_dictionary_literal, + contextualType->lookThroughAllOptionalTypes(), isIniting); + + auto diagnostic = + DE.diagnose(expr->getStartLoc(), diag::meant_dictionary_lit); + + // If there is an even number of elements in the array, let's produce + // a fix-it which suggests to replace "," with ":" to form a dictionary + // literal. + if ((numElements & 1) == 0) { + const auto commaLocs = expr->getCommaLocs(); + if (commaLocs.size() == numElements - 1) { + for (unsigned i = 0, e = numElements / 2; i != e; ++i) + diagnostic.fixItReplace(commaLocs[i * 2], ":"); + } + } + + return nullptr; + } + auto arrayTy = CS.createTypeVariable(locator, TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape); diff --git a/test/Constraints/dictionary_literal.swift b/test/Constraints/dictionary_literal.swift index 35defd165c2b5..8d92e97487dfc 100644 --- a/test/Constraints/dictionary_literal.swift +++ b/test/Constraints/dictionary_literal.swift @@ -63,8 +63,8 @@ var _: MyDictionary? = ["foo", 1] // expected-error {{dictionary o var _: MyDictionary? = ["foo", 1, "bar", 42] // expected-error {{dictionary of type 'MyDictionary' cannot be initialized with array literal}} // expected-note @-1 {{did you mean to use a dictionary literal instead?}} {{43-44=:}} {{53-54=:}} -var _: MyDictionary? = ["foo", 1.0, 2] // expected-error {{cannot convert value of type '[Double]' to specified type 'MyDictionary?'}} -// expected-error@-1 {{cannot convert value of type 'String' to expected element type 'Double'}} +var _: MyDictionary? = ["foo", 1.0, 2] // expected-error {{dictionary of type 'MyDictionary' cannot be initialized with array literal}} +// expected-note@-1 {{did you mean to use a dictionary literal instead?}} {{none}} var _: MyDictionary? = ["foo" : 1.0] // expected-error {{cannot convert value of type 'Double' to expected dictionary value type 'MyDictionary.Value' (aka 'Int')}} From 17f7598fdb7cf0ca0f9a8df9e3f337842461b1a8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 6 Feb 2020 00:31:12 -0800 Subject: [PATCH 189/237] [Diagnostics] NFC: Remove obsolete `diagnoseContextualConversionError` --- lib/Sema/CSDiag.cpp | 58 ------------------------------ test/Constraints/closures.swift | 8 ++++- test/Parse/matching_patterns.swift | 4 +-- 3 files changed, 8 insertions(+), 62 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index e8e865c13e43a..8982a62545e44 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -195,12 +195,6 @@ class FailureDiagnosis :public ASTVisitor{ /// Emit an ambiguity diagnostic about the specified expression. void diagnoseAmbiguity(Expr *E); - /// Attempt to produce a diagnostic for a mismatch between an expression's - /// type and its assumed contextual type. - bool diagnoseContextualConversionError(Expr *expr, Type contextualType, - ContextualTypePurpose CTP, - Type suggestedType = Type()); - private: /// Validate potential contextual type for type-checking one of the /// sub-expressions, usually correct/valid types are the ones which @@ -500,51 +494,6 @@ DeclContext *FailureDiagnosis::findDeclContext(Expr *subExpr) const { return finder.DC; } -bool FailureDiagnosis::diagnoseContextualConversionError( - Expr *expr, Type contextualType, ContextualTypePurpose CTP, - Type suggestedType) { - // If the constraint system has a contextual type, then we can test to see if - // this is the problem that prevents us from solving the system. - if (!contextualType) - return false; - - // Try re-type-checking the expression without the contextual type to see if - // it can work without it. If so, the contextual type is the problem. We - // force a recheck, because "expr" is likely in our table with the extra - // contextual constraint that we know we are relaxing. - TCCOptions options = TCC_ForceRecheck; - if (contextualType->is()) - options |= TCC_AllowLValue; - - auto *recheckedExpr = typeCheckChildIndependently(expr, options); - auto exprType = recheckedExpr ? CS.getType(recheckedExpr) : Type(); - - // If there is a suggested type and re-typecheck failed, let's use it. - if (!exprType) - exprType = suggestedType; - - // If it failed and diagnosed something, then we're done. - if (!exprType) - return CS.getASTContext().Diags.hadAnyError(); - - // If we don't have a type for the expression, then we cannot use it in - // conversion constraint diagnostic generation. If the types match, then it - // must not be the contextual type that is the problem. - if (isUnresolvedOrTypeVarType(exprType) || exprType->isEqual(contextualType)) - return false; - - // Don't attempt fixits if we have an unsolved type variable, since - // the recovery path's recursion into the type checker via typeCheckCast() - // will confuse matters. - if (exprType->hasTypeVariable()) - return false; - - ContextualFailure failure( - CS, CTP, exprType, contextualType, - CS.getConstraintLocator(expr, LocatorPathElt::ContextualType())); - return failure.diagnoseAsError(); -} - //===----------------------------------------------------------------------===// // Diagnose assigning variable to itself. //===----------------------------------------------------------------------===// @@ -1256,13 +1205,6 @@ void ConstraintSystem::diagnoseFailureFor(SolutionApplicationTarget target) { if (diagnosis.diagnoseExprFailure()) return; - // If this is a contextual conversion problem, dig out some information. - if (diagnosis.diagnoseContextualConversionError( - expr, - getContextualType(expr), - getContextualTypePurpose(expr))) - return; - // If no one could find a problem with this expression or constraint system, // then it must be well-formed... but is ambiguous. Handle this by diagnostic // various cases that come up. diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index fb15d6de202ce..3699073f692a6 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -456,7 +456,13 @@ extension Collection { } } func fn_r28909024(n: Int) { - return (0..<10).r28909024 { // expected-error {{unexpected non-void return value in void function}} + // FIXME(diagnostics): Unfortunately there is no easy way to fix this diagnostic issue at the moment + // because the problem is related to ordering of the bindings - we'd attempt to bind result of the expression + // to contextual type of `Void` which prevents solver from discovering correct types for range - 0..<10 + // (since both arguments are literal they are ranked lower than contextual type). + // + // Good diagnostic for this is - `unexpected non-void return value in void function` + return (0..<10).r28909024 { // expected-error {{expression type 'Range.Index' (aka 'Int') is ambiguous without more context}} _ in true } } diff --git a/test/Parse/matching_patterns.swift b/test/Parse/matching_patterns.swift index 0448390a53a97..263c24f4c75e4 100644 --- a/test/Parse/matching_patterns.swift +++ b/test/Parse/matching_patterns.swift @@ -332,6 +332,4 @@ case (_?)?: break // expected-warning {{case is already handled by previous patt let (responseObject: Int?) = op1 // expected-error @-1 {{expected ',' separator}} {{25-25=,}} // expected-error @-2 {{expected pattern}} -// expected-error @-3 {{cannot convert value of type 'Int?' to specified type '(responseObject: _)'}} - - +// expected-error @-3 {{expression type 'Int?' is ambiguous without more context}} From 609c84b600e175d09f60cd2038c59cd018112e77 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Thu, 6 Feb 2020 11:03:20 -0800 Subject: [PATCH 190/237] benchmark: Make building with debug info the default (#29669) --- benchmark/CMakeLists.txt | 4 ++++ benchmark/README.md | 3 +++ benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index fbf1709638d3b..d46c6326f174c 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -248,6 +248,10 @@ option(SWIFT_BENCHMARK_GENERATE_OPT_VIEW set(SWIFT_BENCHMARK_OPT_VIEWER "" CACHE FILEPATH "Path to opt-viewer") +option(SWIFT_BENCHMARK_GENERATE_DEBUG_INFO + "Produce debug info for benchmarks" + TRUE) + if(SWIFT_BENCHMARK_OPT_VIEWER) # If the path to the opt-viewer was specified manually and we have no access # to the LLVM tree, assume we have the modules for the opt-viewer installed. diff --git a/benchmark/README.md b/benchmark/README.md index 443d940bbb3af..0e133fbcc746c 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -67,6 +67,9 @@ The following build options are available: * Enable this option to link the benchmark binaries against the target machine's Swift standard library and runtime installed with the OS. (default: OFF) +* `-DSWIFT_BENCHMARK_GENERATE_DEBUG_INFO` + * Enable this option to compile benchmark binaries with debug info. + (default: ON) The following build targets are available: diff --git a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake index bc05c8f864789..e21f9c87566cc 100644 --- a/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake +++ b/benchmark/cmake/modules/AddSwiftBenchmarkSuite.cmake @@ -355,6 +355,10 @@ function (swift_benchmark_compile_archopts) "-target" "${target}" "-${BENCH_COMPILE_ARCHOPTS_OPT}" ${PAGE_ALIGNMENT_OPTION}) + if(SWIFT_BENCHMARK_GENERATE_DEBUG_INFO) + list(APPEND common_options "-g") + endif() + if (is_darwin) list(APPEND common_options "-I" "${srcdir}/utils/ObjectiveCTests" @@ -384,6 +388,10 @@ function (swift_benchmark_compile_archopts) "-target" "${target}" "-${driver_opt}") + if(SWIFT_BENCHMARK_GENERATE_DEBUG_INFO) + list(APPEND common_options_driver "-g") + endif() + if (is_darwin) list(APPEND common_options_driver "-sdk" "${sdk}" From 4300f711b445927b16aa90a52ffef50ce18187fc Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 6 Feb 2020 11:10:54 -0800 Subject: [PATCH 191/237] [metadata prespecialization] Available in 5.3. --- include/swift/AST/ASTContext.h | 4 ++++ lib/AST/Availability.cpp | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index be98a06362e3c..93e87f401ec20 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -631,6 +631,10 @@ class ASTContext final { /// compiler for the target platform. AvailabilityContext getSwift52Availability(); + /// Get the runtime availability of features introduced in the Swift 5.3 + /// compiler for the target platform. + AvailabilityContext getSwift53Availability(); + /// Get the runtime availability of features that have been introduced in the /// Swift compiler for future versions of the target platform. AvailabilityContext getSwiftFutureAvailability(); diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 538924ed8a23b..251783aac808c 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -243,7 +243,7 @@ AvailabilityContext ASTContext::getTypesInAbstractMetadataStateAvailability() { } AvailabilityContext ASTContext::getPrespecializedGenericMetadataAvailability() { - return getSwiftFutureAvailability(); + return getSwift53Availability(); } AvailabilityContext ASTContext::getSwift52Availability() { @@ -263,6 +263,23 @@ AvailabilityContext ASTContext::getSwift52Availability() { } } +AvailabilityContext ASTContext::getSwift53Availability() { + auto target = LangOpts.Target; + + if (target.isMacOSX() ) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(10, 99, 0))); + } else if (target.isiOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(99, 0, 0))); + } else if (target.isWatchOS()) { + return AvailabilityContext( + VersionRange::allGTE(llvm::VersionTuple(9, 99, 0))); + } else { + return AvailabilityContext::alwaysAvailable(); + } +} + AvailabilityContext ASTContext::getSwiftFutureAvailability() { auto target = LangOpts.Target; From 9205758e0032ece535d0d948e35087e4f7b41e99 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 6 Feb 2020 12:47:12 -0800 Subject: [PATCH 192/237] [AutoDiff upstream] Add `SILFunctionType` utilities. (#29674) Upstream AutoDiff-related `SILFunctionType` utilities: - `SILFunctionType::getDifferentiabilityParameterIndices` - `SILFunctionType::getWithDifferentiability` - `SILFunctionType::getWithoutDifferentiability` Resolves TF-1125. --- include/swift/AST/Types.h | 26 ++++++++++++++++++- lib/SIL/SILFunctionType.cpp | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index de3ed8015081d..cdbc1019f3e05 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -3087,6 +3087,14 @@ class AnyFunctionType : public TypeBase { ExtInfo withClangFunctionType(const clang::Type *type) const { return ExtInfo(Bits, Uncommon(type)); } + LLVM_NODISCARD + ExtInfo + withDifferentiabilityKind(DifferentiabilityKind differentiability) const { + return ExtInfo( + (Bits & ~DifferentiabilityMask) | + ((unsigned)differentiability << DifferentiabilityMaskOffset), + Other); + } std::pair getFuncAttrKey() const { return std::make_pair(Bits, Other.ClangFunctionType); @@ -3746,7 +3754,7 @@ class SILParameterInfo { /// Return a version of this parameter info with the type replaced. SILParameterInfo getWithInterfaceType(CanType type) const { - return SILParameterInfo(type, getConvention()); + return SILParameterInfo(type, getConvention(), getDifferentiability()); } /// Transform this SILParameterInfo by applying the user-provided @@ -4435,6 +4443,22 @@ class SILFunctionType final : public TypeBase, public llvm::FoldingSetNode, const clang::FunctionType *getClangFunctionType() const; + /// Given that `this` is a `@differentiable` or `@differentiable(linear)` + /// function type, returns an `IndexSubset` corresponding to the + /// differentiability/linearity parameters (e.g. all parameters except the + /// `@noDerivative` ones). + IndexSubset *getDifferentiabilityParameterIndices(); + + /// Returns the `@differentiable` or `@differentiable(linear)` function type + /// for the given differentiability kind and parameter indices representing + /// differentiability/linearity parameters. + CanSILFunctionType getWithDifferentiability(DifferentiabilityKind kind, + IndexSubset *parameterIndices); + + /// Returns the SIL function type stripping differentiability kind and + /// differentiability from all parameters. + CanSILFunctionType getWithoutDifferentiability(); + /// Returns the type of the derivative function for the given parameter /// indices, result index, derivative function kind, derivative function /// generic signature (optional), and other auxiliary parameters. diff --git a/lib/SIL/SILFunctionType.cpp b/lib/SIL/SILFunctionType.cpp index f31d86ad3b51f..8fbb1e1a8a58c 100644 --- a/lib/SIL/SILFunctionType.cpp +++ b/lib/SIL/SILFunctionType.cpp @@ -191,6 +191,56 @@ SILFunctionType::getWitnessMethodClass(SILModule &M) const { return nullptr; } +IndexSubset * +SILFunctionType::getDifferentiabilityParameterIndices() { + assert(isDifferentiable() && "Must be a differentiable function"); + SmallVector result; + for (auto valueAndIndex : enumerate(getParameters())) + if (valueAndIndex.value().getDifferentiability() != + SILParameterDifferentiability::NotDifferentiable) + result.push_back(valueAndIndex.index()); + return IndexSubset::get(getASTContext(), getNumParameters(), result); +} + +CanSILFunctionType +SILFunctionType::getWithDifferentiability(DifferentiabilityKind kind, + IndexSubset *parameterIndices) { + assert(kind != DifferentiabilityKind::NonDifferentiable && + "Differentiability kind must be normal or linear"); + SmallVector newParameters; + for (auto paramAndIndex : enumerate(getParameters())) { + auto ¶m = paramAndIndex.value(); + unsigned index = paramAndIndex.index(); + newParameters.push_back(param.getWithDifferentiability( + index < parameterIndices->getCapacity() && + parameterIndices->contains(index) + ? SILParameterDifferentiability::DifferentiableOrNotApplicable + : SILParameterDifferentiability::NotDifferentiable)); + } + auto newExtInfo = getExtInfo().withDifferentiabilityKind(kind); + return get(getSubstGenericSignature(), newExtInfo, getCoroutineKind(), + getCalleeConvention(), newParameters, getYields(), getResults(), + getOptionalErrorResult(), getSubstitutions(), + isGenericSignatureImplied(), getASTContext(), + getWitnessMethodConformanceOrInvalid()); +} + +CanSILFunctionType SILFunctionType::getWithoutDifferentiability() { + if (!isDifferentiable()) + return CanSILFunctionType(this); + auto nondiffExtInfo = getExtInfo().withDifferentiabilityKind( + DifferentiabilityKind::NonDifferentiable); + SmallVector newParams; + for (auto ¶m : getParameters()) + newParams.push_back(param.getWithDifferentiability( + SILParameterDifferentiability::DifferentiableOrNotApplicable)); + return SILFunctionType::get(getSubstGenericSignature(), nondiffExtInfo, + getCoroutineKind(), getCalleeConvention(), + newParams, getYields(), getResults(), + getOptionalErrorResult(), getSubstitutions(), + isGenericSignatureImplied(), getASTContext()); +} + CanSILFunctionType SILFunctionType::getAutoDiffDerivativeFunctionType( IndexSubset *parameterIndices, unsigned resultIndex, AutoDiffDerivativeFunctionKind kind, TypeConverter &TC, From f17a70c57bdf5e920e4fb53df19b78c657d0942f Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 6 Feb 2020 16:28:16 -0500 Subject: [PATCH 193/237] Fix a bug with the printing of optional typealiases. --- lib/AST/ASTPrinter.cpp | 47 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index eeabdf4de8a65..45cc7d59752a5 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3529,24 +3529,7 @@ class TypePrinter : public TypeVisitor { return; } - bool isSimple = T->hasSimpleTypeRepr(); - if (!isSimple && T->is()) { - auto opaqueTy = T->castTo(); - switch (Options.OpaqueReturnTypePrinting) { - case PrintOptions::OpaqueReturnTypePrintingMode::StableReference: - case PrintOptions::OpaqueReturnTypePrintingMode::Description: - isSimple = true; - break; - case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword: - isSimple = false; - break; - case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword: { - isSimple = opaqueTy->getExistentialType()->hasSimpleTypeRepr(); - break; - } - } - } - + bool isSimple = isSimpleUnderPrintOptions(T); if (isSimple) { visit(T); } else { @@ -3556,6 +3539,28 @@ class TypePrinter : public TypeVisitor { } } + /// Determinee whether the given type has a simple representation + /// under the current print options. + bool isSimpleUnderPrintOptions(Type T) { + if (auto typealias = dyn_cast(T.getPointer())) { + if (shouldDesugarTypeAliasType(typealias)) + return isSimpleUnderPrintOptions(typealias->getSinglyDesugaredType()); + } else if (auto opaque = + dyn_cast(T.getPointer())) { + switch (Options.OpaqueReturnTypePrinting) { + case PrintOptions::OpaqueReturnTypePrintingMode::StableReference: + case PrintOptions::OpaqueReturnTypePrintingMode::Description: + return true; + case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword: + return false; + case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword: + return isSimpleUnderPrintOptions(opaque->getExistentialType()); + } + llvm_unreachable("bad opaque-return-type printing mode"); + } + return T->hasSimpleTypeRepr(); + } + template void printModuleContext(T *Ty) { FileUnit *File = cast(Ty->getDecl()->getModuleScopeContext()); @@ -3693,8 +3698,12 @@ class TypePrinter : public TypeVisitor { Printer << BUILTIN_TYPE_NAME_SILTOKEN; } + bool shouldDesugarTypeAliasType(TypeAliasType *T) { + return Options.PrintForSIL || Options.PrintTypeAliasUnderlyingType; + } + void visitTypeAliasType(TypeAliasType *T) { - if (Options.PrintForSIL || Options.PrintTypeAliasUnderlyingType) { + if (shouldDesugarTypeAliasType(T)) { visit(T->getSinglyDesugaredType()); return; } From b826b3cc04cc513adc4bab0be4e2c35e831c62f5 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Thu, 6 Feb 2020 14:24:00 -0800 Subject: [PATCH 194/237] [AST] Fix bug in `IndexSubset::findPrevious`. (#29675) Fix incorrect loop decrement in `IndexSubset::findPrevious` Add unit tests. --- lib/AST/IndexSubset.cpp | 2 +- unittests/AST/IndexSubsetTests.cpp | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/AST/IndexSubset.cpp b/lib/AST/IndexSubset.cpp index fe64ee07d58ca..d5be9aa53363e 100644 --- a/lib/AST/IndexSubset.cpp +++ b/lib/AST/IndexSubset.cpp @@ -125,7 +125,7 @@ int IndexSubset::findPrevious(int endIndex) const { offset = (int)indexAndOffset.second - 1; } for (; bitWordIndex >= 0; --bitWordIndex, offset = numBitsPerBitWord - 1) { - for (; offset < (int)numBitsPerBitWord; --offset) { + for (; offset >= 0; --offset) { auto index = bitWordIndex * (int)numBitsPerBitWord + offset; auto bitWord = getBitWord(bitWordIndex); if (!bitWord) diff --git a/unittests/AST/IndexSubsetTests.cpp b/unittests/AST/IndexSubsetTests.cpp index c08ee21de2421..91ce1c2b5b289 100644 --- a/unittests/AST/IndexSubsetTests.cpp +++ b/unittests/AST/IndexSubsetTests.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) 2019 - 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 @@ -187,3 +187,26 @@ TEST(IndexSubset, Insertion) { EXPECT_EQ(indices1->adding(3, ctx.Ctx), IndexSubset::get(ctx.Ctx, 5, {0, 2, 3, 4})); } + +TEST(IndexSubset, FindNext) { + TestContext ctx; + auto *indices1 = IndexSubset::get(ctx.Ctx, 5, {1, 2, 4}); + EXPECT_EQ(indices1->findFirst(), 1); + EXPECT_EQ(indices1->findNext(/*startIndex*/ -1), 1); + EXPECT_EQ(indices1->findNext(/*startIndex*/ 0), 1); + EXPECT_EQ(indices1->findNext(/*startIndex*/ 1), 2); + EXPECT_EQ(indices1->findNext(/*startIndex*/ 2), 4); + EXPECT_EQ(indices1->findNext(/*startIndex*/ 3), 4); +} + +TEST(IndexSubset, FindPrevious) { + TestContext ctx; + auto *indices1 = IndexSubset::get(ctx.Ctx, 5, {0, 2, 4}); + EXPECT_EQ(indices1->findLast(), 4); + EXPECT_EQ(indices1->findPrevious(/*endIndex*/ 5), 4); + EXPECT_EQ(indices1->findPrevious(/*endIndex*/ 4), 2); + EXPECT_EQ(indices1->findPrevious(/*endIndex*/ 3), 2); + EXPECT_EQ(indices1->findPrevious(/*endIndex*/ 2), 0); + EXPECT_EQ(indices1->findPrevious(/*endIndex*/ 1), 0); + EXPECT_EQ(indices1->findPrevious(/*endIndex*/ 0), -1); +} From 7d1411ecdbbd38675019515e92088fb993dfb77d Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Thu, 6 Feb 2020 15:25:30 -0800 Subject: [PATCH 195/237] [Python] Update the utils/80+-check script to be Python 2/3 compatible and conform to the black style. --- utils/80+-check | 108 ++++++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/utils/80+-check b/utils/80+-check index 80f3a4a867eb7..34d0f9563b08c 100755 --- a/utils/80+-check +++ b/utils/80+-check @@ -1,54 +1,74 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import print_function, unicode_literals + +import argparse import sys -import textwrap - - -def get_arguments(): - import argparse - - parser = argparse.ArgumentParser(textwrap.dedent(""" - This is a simple script that takes input from stdin and prints out any - lines that are greater than 80+ characters. In such a case, it will print - out the line number, the length of the line, and the line with whitespace - stripped. The reason why we print out the line with whitespace stripped is - that in the case where there is a lot of redundant whitespace, the output - becomes hard to read and no value is provided in terms of finding the line - in question. - - Exits with 1 if it finds any violating lines, 0 otherwise.""")) - - parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), - default=sys.stdin, - help=textwrap.dedent("""The file to read input from. If - no file is provided, stdin is used.""")) - parser.add_argument('-o', '--output', type=argparse.FileType('w'), - default=sys.stdout, - help=textwrap.dedent("""The file to print to. If no - file is provided, stdout is used"""), - dest='outfile') - parser.add_argument('--max-length', type=int, default=80, metavar='LENGTH', - help=textwrap.dedent("""Maximum line length that the - script should check for""")) - parser.add_argument('--count-newline', action='store_false', - help=textwrap.dedent("""Should we include the newline - in our line length count""")) + + +DESCRIPTION = """ +This is a simple script that takes input from stdin and prints out any lines that are +greater than 80+ characters. In such a case, it will print out the line number, the +length of the line, and the line with whitespace stripped. The reason why we print out +the line with whitespace stripped is that in the case where there is a lot of redundant +whitespace, the output becomes hard to read and no value is provided in terms of finding +the line in question. Exits with 1 if it finds any violating lines, 0 otherwise. +""" + + +def parse_args(): + parser = argparse.ArgumentParser(description=DESCRIPTION) + + parser.add_argument( + "infile", + nargs="?", + type=argparse.FileType("r"), + default=sys.stdin, + help="The file to read input from. If no file is provided, stdin is used.", + ) + parser.add_argument( + "-o", + "--output", + type=argparse.FileType("w"), + default=sys.stdout, + help="The file to print to. If no file is provided, stdout is used", + dest="outfile", + ) + parser.add_argument( + "--max-length", + type=int, + default=80, + metavar="LENGTH", + help="Maximum line length that the script should check for", + ) + parser.add_argument( + "--count-newline", + action="store_false", + help="Should we include the newline in our line length count", + ) + return parser.parse_args() -args = get_arguments() +def main(): + args = parse_args() + + found_violation = False + + for lineno, line in enumerate(args.infile, start=1): + # sys.stdin.readlines() includes a newline. So we subtract 1 from our length to + # get the "true" line length. + length = len(line) - int(args.count_newline) + if length > args.max_length: + found_violation = True + print( + "line: {}. length: {}: {}".format(lineno, length, line.strip()), + file=args.outfile, + ) -found_violation = False + sys.exit(found_violation) -for lineno, line in enumerate(args.infile, start=1): - # sys.stdin.readlines() includes a newline. So we subtract 1 from our - # length to get the "true" line length. - length = len(line) - int(args.count_newline) - if length > args.max_length: - found_violation = True - print("line: {}. length: {}: {}".format(lineno, length, line.strip()), - file=args.outfile) -sys.exit(found_violation) +if __name__ == "__main__": + sys.exit(main()) From 29a7edded67631960014de42039eef9fd93da052 Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Thu, 6 Feb 2020 15:39:25 -0800 Subject: [PATCH 196/237] [Python] Update the .flake8 config file to lint all existing Python scripts. The utils/dev-scripts/at-to-filelist, utils/dev-scripts/split-cmdline and utils/build-parser-lib scripts needed to be reformatted with the black tool to agree with the linter. --- .flake8 | 8 +- utils/build-parser-lib | 515 ++++++++++++++++++++------------ utils/dev-scripts/blockifyasm | 209 ++++++------- utils/dev-scripts/split-cmdline | 128 +++++--- 4 files changed, 522 insertions(+), 338 deletions(-) diff --git a/.flake8 b/.flake8 index 76cf787fad90f..39188cae5c115 100644 --- a/.flake8 +++ b/.flake8 @@ -15,12 +15,15 @@ filename = ./utils/80+-check, ./utils/backtrace-check, + ./utils/build-parser-lib, ./utils/build-script, ./utils/check-incremental, ./utils/coverage/coverage-build-db, ./utils/coverage/coverage-generate-data, ./utils/coverage/coverage-query-db, ./utils/coverage/coverage-touch-tests, + ./utils/dev-scripts/blockifyasm, + ./utils/dev-scripts/split-cmdline, ./utils/gyb, ./utils/line-directive, ./utils/PathSanitizingFileCheck, @@ -39,11 +42,6 @@ filename = # TODO: We should be linting the lit configs. #lit.cfg, - # FIXME: We need to be linting these files. - #./utils/build-parser-lib, - #./utils/dev-scripts/blockifyasm, - #./utils/dev-scripts/split-cmdline, - exclude = .git, __pycache__, diff --git a/utils/build-parser-lib b/utils/build-parser-lib index 34ac8258c55f8..c36aabe12fb41 100755 --- a/utils/build-parser-lib +++ b/utils/build-parser-lib @@ -40,10 +40,13 @@ from swift_build_support.swift_build_support.SwiftBuildSupport import ( ) from swift_build_support.swift_build_support.toolchain import host_toolchain -isDarwin = platform.system() == 'Darwin' +isDarwin = platform.system() == "Darwin" + class Builder(object): - def __init__(self, toolchain, args, host, arch, profile_data=None, native_build_dir=None): + def __init__( + self, toolchain, args, host, arch, profile_data=None, native_build_dir=None + ): self.toolchain = toolchain self.ninja_path = args.ninja_path self.build_release = args.release @@ -65,143 +68,193 @@ class Builder(object): def call(self, command, env=None, without_sleeping=False): if without_sleeping: - shell.call_without_sleeping(command, env=env, dry_run=self.dry_run, echo=self.verbose) + shell.call_without_sleeping( + command, env=env, dry_run=self.dry_run, echo=self.verbose + ) else: shell.call(command, env=env, dry_run=self.dry_run, echo=self.verbose) def configure(self, enable_debuginfo, instrumentation=None, profile_data=None): - cmake_args = [self.toolchain.cmake, '-G', 'Ninja'] - cmake_args += ['-DCMAKE_MAKE_PROGRAM='+self.ninja_path] + cmake_args = [self.toolchain.cmake, "-G", "Ninja"] + cmake_args += ["-DCMAKE_MAKE_PROGRAM=" + self.ninja_path] - isEmbeddedHost = isDarwin and self.host != 'macosx' + isEmbeddedHost = isDarwin and self.host != "macosx" host_triple = None host_sdk = None - llvm_c_flags = "-arch "+self.arch - - if self.host == 'macosx': - deployment_version = '10.12' - host_triple = '%s-apple-macosx%s' % (self.arch, deployment_version) - host_sdk = 'OSX' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_OSX='+deployment_version] - - elif self.host == 'linux': - host_triple = '%s-unknown-linux' % (self.arch) - host_sdk = 'LINUX' - - elif self.host == 'iphonesimulator': - deployment_version = '10.0' - host_triple = '%s-apple-ios%s-simulator' % (self.arch, deployment_version) - host_sdk = 'IOS_SIMULATOR' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_IOS='+deployment_version] - llvm_c_flags += ' -mios-simulator-version-min='+deployment_version - - elif self.host == 'iphoneos': - deployment_version = '10.0' - host_triple = '%s-apple-ios%s' % (self.arch, deployment_version) - host_sdk = 'IOS' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_IOS='+deployment_version] - llvm_c_flags += ' -miphoneos-version-min=='+deployment_version - - elif self.host == 'appletvsimulator': - deployment_version = '10.0' - host_triple = '%s-apple-tvos%s-simulator' % (self.arch, deployment_version) - host_sdk = 'TVOS_SIMULATOR' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS='+deployment_version] - llvm_c_flags += ' -mtvos-simulator-version-min='+deployment_version - - elif self.host == 'appletvos': - deployment_version = '10.0' - host_triple = '%s-apple-tvos%s' % (self.arch, deployment_version) - host_sdk = 'TVOS' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS='+deployment_version] - llvm_c_flags += ' -mtvos-version-min='+deployment_version - - elif self.host == 'watchsimulator': - deployment_version = '3.0' - host_triple = '%s-apple-watchos%s-simulator' % (self.arch, deployment_version) - host_sdk = 'WATCHOS_SIMULATOR' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS='+deployment_version] - llvm_c_flags += ' -mwatchos-simulator-version-min='+deployment_version - - elif self.host == 'watchos': - deployment_version = '3.0' - host_triple = '%s-apple-watchos%s' % (self.arch, deployment_version) - host_sdk = 'WATCHOS' - cmake_args += ['-DCMAKE_OSX_DEPLOYMENT_TARGET='+deployment_version, '-DSWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS='+deployment_version] - llvm_c_flags += ' -mwatchos-version-min='+deployment_version + llvm_c_flags = "-arch " + self.arch + + if self.host == "macosx": + deployment_version = "10.12" + host_triple = "%s-apple-macosx%s" % (self.arch, deployment_version) + host_sdk = "OSX" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_OSX=" + deployment_version, + ] + + elif self.host == "linux": + host_triple = "%s-unknown-linux" % (self.arch) + host_sdk = "LINUX" + + elif self.host == "iphonesimulator": + deployment_version = "10.0" + host_triple = "%s-apple-ios%s-simulator" % (self.arch, deployment_version) + host_sdk = "IOS_SIMULATOR" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_IOS=" + deployment_version, + ] + llvm_c_flags += " -mios-simulator-version-min=" + deployment_version + + elif self.host == "iphoneos": + deployment_version = "10.0" + host_triple = "%s-apple-ios%s" % (self.arch, deployment_version) + host_sdk = "IOS" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_IOS=" + deployment_version, + ] + llvm_c_flags += " -miphoneos-version-min==" + deployment_version + + elif self.host == "appletvsimulator": + deployment_version = "10.0" + host_triple = "%s-apple-tvos%s-simulator" % (self.arch, deployment_version) + host_sdk = "TVOS_SIMULATOR" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS=" + deployment_version, + ] + llvm_c_flags += " -mtvos-simulator-version-min=" + deployment_version + + elif self.host == "appletvos": + deployment_version = "10.0" + host_triple = "%s-apple-tvos%s" % (self.arch, deployment_version) + host_sdk = "TVOS" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_TVOS=" + deployment_version, + ] + llvm_c_flags += " -mtvos-version-min=" + deployment_version + + elif self.host == "watchsimulator": + deployment_version = "3.0" + host_triple = "%s-apple-watchos%s-simulator" % ( + self.arch, + deployment_version, + ) + host_sdk = "WATCHOS_SIMULATOR" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS=" + deployment_version, + ] + llvm_c_flags += " -mwatchos-simulator-version-min=" + deployment_version + + elif self.host == "watchos": + deployment_version = "3.0" + host_triple = "%s-apple-watchos%s" % (self.arch, deployment_version) + host_sdk = "WATCHOS" + cmake_args += [ + "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + deployment_version, + "-DSWIFT_DARWIN_DEPLOYMENT_VERSION_WATCHOS=" + deployment_version, + ] + llvm_c_flags += " -mwatchos-version-min=" + deployment_version assert host_triple assert host_sdk cmake_args += [ - '-DLLVM_HOST_TRIPLE:STRING='+host_triple, - '-DLLVM_TARGET_ARCH='+self.arch, - '-DSWIFT_HOST_VARIANT='+self.host, - '-DSWIFT_HOST_VARIANT_SDK='+host_sdk, - '-DSWIFT_HOST_VARIANT_ARCH='+self.arch, - '-DCMAKE_C_FLAGS='+llvm_c_flags, - '-DCMAKE_CXX_FLAGS='+llvm_c_flags, + "-DLLVM_HOST_TRIPLE:STRING=" + host_triple, + "-DLLVM_TARGET_ARCH=" + self.arch, + "-DSWIFT_HOST_VARIANT=" + self.host, + "-DSWIFT_HOST_VARIANT_SDK=" + host_sdk, + "-DSWIFT_HOST_VARIANT_ARCH=" + self.arch, + "-DCMAKE_C_FLAGS=" + llvm_c_flags, + "-DCMAKE_CXX_FLAGS=" + llvm_c_flags, ] if isEmbeddedHost: cmake_args += [ - '-DCMAKE_OSX_SYSROOT:PATH='+xcrun.sdk_path(self.host), - # For embedded hosts CMake runs the checks and triggers crashes because the test binary was built for embedded host. - '-DHAVE_POSIX_REGEX:BOOL=TRUE', - '-DHAVE_STEADY_CLOCK:BOOL=TRUE', + "-DCMAKE_OSX_SYSROOT:PATH=" + xcrun.sdk_path(self.host), + # For embedded hosts CMake runs the checks and triggers crashes because + # the test binary was built for embedded host. + "-DHAVE_POSIX_REGEX:BOOL=TRUE", + "-DHAVE_STEADY_CLOCK:BOOL=TRUE", ] if isDarwin: if self.native_build_dir is not None: cmake_args += [ - '-DLLVM_TABLEGEN='+os.path.join(self.native_build_dir, 'bin', 'llvm-tblgen'), - '-DCLANG_TABLEGEN='+os.path.join(self.native_build_dir, 'bin', 'clang-tblgen'), - '-DLLVM_NATIVE_BUILD='+self.native_build_dir, - '-DSWIFT_NATIVE_LLVM_TOOLS_PATH:STRING='+os.path.join(self.native_build_dir, 'bin'), - '-DSWIFT_NATIVE_CLANG_TOOLS_PATH:STRING='+os.path.join(self.native_build_dir, 'bin'), - '-DSWIFT_NATIVE_SWIFT_TOOLS_PATH:STRING='+os.path.join(self.native_build_dir, 'bin'), + "-DLLVM_TABLEGEN=" + + os.path.join(self.native_build_dir, "bin", "llvm-tblgen"), + "-DCLANG_TABLEGEN=" + + os.path.join(self.native_build_dir, "bin", "clang-tblgen"), + "-DLLVM_NATIVE_BUILD=" + self.native_build_dir, + "-DSWIFT_NATIVE_LLVM_TOOLS_PATH:STRING=" + + os.path.join(self.native_build_dir, "bin"), + "-DSWIFT_NATIVE_CLANG_TOOLS_PATH:STRING=" + + os.path.join(self.native_build_dir, "bin"), + "-DSWIFT_NATIVE_SWIFT_TOOLS_PATH:STRING=" + + os.path.join(self.native_build_dir, "bin"), ] else: - dispatch_source_path = os.path.join(SWIFT_SOURCE_ROOT, 'swift-corelibs-libdispatch') + dispatch_source_path = os.path.join( + SWIFT_SOURCE_ROOT, "swift-corelibs-libdispatch" + ) cmake_args += [ - '-DSWIFT_PATH_TO_LIBDISPATCH_SOURCE:PATH='+dispatch_source_path, - '-DLLVM_ENABLE_LLD=ON'] - cmake_args += ['-DLLVM_TARGETS_TO_BUILD=X86'] + "-DSWIFT_PATH_TO_LIBDISPATCH_SOURCE:PATH=" + dispatch_source_path, + "-DLLVM_ENABLE_LLD=ON", + ] + cmake_args += ["-DLLVM_TARGETS_TO_BUILD=X86"] if self.build_release: if enable_debuginfo: - cmake_args += ['-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo'] + cmake_args += ["-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo"] else: - cmake_args += ['-DCMAKE_BUILD_TYPE:STRING=Release'] + cmake_args += ["-DCMAKE_BUILD_TYPE:STRING=Release"] else: - cmake_args += ['-DCMAKE_BUILD_TYPE:STRING=Debug'] + cmake_args += ["-DCMAKE_BUILD_TYPE:STRING=Debug"] if self.enable_assertions: - cmake_args += ['-DLLVM_ENABLE_ASSERTIONS:BOOL=ON'] + cmake_args += ["-DLLVM_ENABLE_ASSERTIONS:BOOL=ON"] if instrumentation: - cmake_args += ['-DLLVM_BUILD_INSTRUMENTED='+instrumentation] + cmake_args += ["-DLLVM_BUILD_INSTRUMENTED=" + instrumentation] if profile_data: - cmake_args += ['-DLLVM_PROFDATA_FILE='+profile_data] + cmake_args += ["-DLLVM_PROFDATA_FILE=" + profile_data] if self.lto_type and not instrumentation: - cmake_args += ['-DLLVM_ENABLE_LTO='+self.lto_type.upper()] + cmake_args += ["-DLLVM_ENABLE_LTO=" + self.lto_type.upper()] if self.install_prefix: - cmake_args += ['-DCMAKE_INSTALL_PREFIX:PATH='+self.install_prefix] + cmake_args += ["-DCMAKE_INSTALL_PREFIX:PATH=" + self.install_prefix] if self.version: - cmake_args += ['-DSWIFT_LIBPARSER_VER:STRING='+self.version] - cmake_args += ['-DLLVM_ENABLE_PROJECTS=clang;swift', '-DLLVM_EXTERNAL_PROJECTS=swift'] - cmake_args += ['-DSWIFT_BUILD_ONLY_SYNTAXPARSERLIB=TRUE'] - cmake_args += ['-DSWIFT_BUILD_PERF_TESTSUITE=NO', '-DSWIFT_INCLUDE_DOCS=NO'] - cmake_args += ['-DSWIFT_BUILD_REMOTE_MIRROR=FALSE', '-DSWIFT_BUILD_DYNAMIC_STDLIB=FALSE', - '-DSWIFT_BUILD_STATIC_STDLIB=FALSE', '-DSWIFT_BUILD_DYNAMIC_SDK_OVERLAY=FALSE', - '-DSWIFT_BUILD_STATIC_SDK_OVERLAY=FALSE'] - cmake_args += ['-DLLVM_ENABLE_LIBXML2=FALSE'] - # We are not using cmark but initialize the CMARK variables to something so that configure can succeed. - cmake_args += ['-DCMARK_MAIN_INCLUDE_DIR='+os.path.join(SWIFT_SOURCE_ROOT, 'cmark'), '-DCMARK_BUILD_INCLUDE_DIR='+os.path.join(self.build_dir, 'cmark')] - cmake_args += ['-DLLVM_INCLUDE_TESTS=FALSE', '-DCLANG_INCLUDE_TESTS=FALSE', '-DSWIFT_INCLUDE_TESTS=FALSE'] - cmake_args += [os.path.join(SWIFT_SOURCE_ROOT, 'llvm')] + cmake_args += ["-DSWIFT_LIBPARSER_VER:STRING=" + self.version] + cmake_args += [ + "-DLLVM_ENABLE_PROJECTS=clang;swift", + "-DLLVM_EXTERNAL_PROJECTS=swift", + ] + cmake_args += ["-DSWIFT_BUILD_ONLY_SYNTAXPARSERLIB=TRUE"] + cmake_args += ["-DSWIFT_BUILD_PERF_TESTSUITE=NO", "-DSWIFT_INCLUDE_DOCS=NO"] + cmake_args += [ + "-DSWIFT_BUILD_REMOTE_MIRROR=FALSE", + "-DSWIFT_BUILD_DYNAMIC_STDLIB=FALSE", + "-DSWIFT_BUILD_STATIC_STDLIB=FALSE", + "-DSWIFT_BUILD_DYNAMIC_SDK_OVERLAY=FALSE", + "-DSWIFT_BUILD_STATIC_SDK_OVERLAY=FALSE", + ] + cmake_args += ["-DLLVM_ENABLE_LIBXML2=FALSE"] + # We are not using cmark but initialize the CMARK variables to something so + # that configure can succeed. + cmake_args += [ + "-DCMARK_MAIN_INCLUDE_DIR=" + os.path.join(SWIFT_SOURCE_ROOT, "cmark"), + "-DCMARK_BUILD_INCLUDE_DIR=" + os.path.join(self.build_dir, "cmark"), + ] + cmake_args += [ + "-DLLVM_INCLUDE_TESTS=FALSE", + "-DCLANG_INCLUDE_TESTS=FALSE", + "-DSWIFT_INCLUDE_TESTS=FALSE", + ] + cmake_args += [os.path.join(SWIFT_SOURCE_ROOT, "llvm")] self.call(cmake_args) def build_target(self, build_dir, target, env=None): - invocation = [self.toolchain.cmake, '--build', build_dir] - invocation += ['--', '-j%d'%self.jobs] + invocation = [self.toolchain.cmake, "--build", build_dir] + invocation += ["--", "-j%d" % self.jobs] if self.verbose: - invocation += ['-v'] + invocation += ["-v"] invocation += [target] self.call(invocation, env=env, without_sleeping=True) @@ -209,19 +262,35 @@ class Builder(object): print("--- Installing ---", file=sys.stderr) env = None if self.install_destdir: - env = {'DESTDIR': self.install_destdir} - self.build_target(self.build_dir, 'tools/swift/tools/libSwiftSyntaxParser/install', env=env) + env = {"DESTDIR": self.install_destdir} + self.build_target( + self.build_dir, "tools/swift/tools/libSwiftSyntaxParser/install", env=env + ) def get_profile_data(self, profile_dir): shell.makedirs(profile_dir, dry_run=self.dry_run) - instrumentation = 'IR' if self.pgo_type == 'ir' else 'Frontend' + instrumentation = "IR" if self.pgo_type == "ir" else "Frontend" with shell.pushd(profile_dir, dry_run=self.dry_run): self.configure(enable_debuginfo=False, instrumentation=instrumentation) - self.build_target(profile_dir, 'swift-syntax-parser-test') - # Delete existing profile data that were generated during building from running tablegen. + self.build_target(profile_dir, "swift-syntax-parser-test") + # Delete existing profile data that were generated during building from + # running tablegen. shell.rmtree("profiles", dry_run=self.dry_run) - self.call([os.path.join("bin", "swift-syntax-parser-test"), self.profile_input, '-time']) - self.call([self.toolchain.llvm_profdata, "merge", "-output=profdata.prof", "profiles"]) + self.call( + [ + os.path.join("bin", "swift-syntax-parser-test"), + self.profile_input, + "-time", + ] + ) + self.call( + [ + self.toolchain.llvm_profdata, + "merge", + "-output=profdata.prof", + "profiles", + ] + ) def run(self): shell.makedirs(self.build_dir, dry_run=self.dry_run) @@ -229,7 +298,7 @@ class Builder(object): with shell.pushd(self.build_dir, dry_run=self.dry_run): self.configure(enable_debuginfo=True, profile_data=self.profile_data) - self.build_target(self.build_dir, 'swift-syntax-parser-test') + self.build_target(self.build_dir, "swift-syntax-parser-test") if self.install_destdir: self.install() @@ -238,19 +307,24 @@ class Builder(object): def extract_symbols(install_destdir, install_prefix, install_symroot, jobs): if not isDarwin: return - extract_script = os.path.join(SWIFT_SOURCE_ROOT, "swift", "utils", "parser-lib", "darwin-extract-symbols") + extract_script = os.path.join( + SWIFT_SOURCE_ROOT, "swift", "utils", "parser-lib", "darwin-extract-symbols" + ) print("--- Extracting symbols ---", file=sys.stderr) - env = {'INSTALL_DIR': install_destdir, - 'INSTALL_PREFIX': install_prefix, - 'INSTALL_SYMROOT': install_symroot, - 'BUILD_JOBS': str(jobs)} + env = { + "INSTALL_DIR": install_destdir, + "INSTALL_PREFIX": install_prefix, + "INSTALL_SYMROOT": install_symroot, + "BUILD_JOBS": str(jobs), + } shell.call([extract_script], env=env) def main(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Builds Swift Syntax Parser library.""") + description="""Builds Swift Syntax Parser library.""", + ) optbuilder = parser.to_builder() option = optbuilder.add_option store = optbuilder.actions.store @@ -258,72 +332,123 @@ def main(): store_int = optbuilder.actions.store_int store_path = optbuilder.actions.store_path - toolchain = host_toolchain(xcrun_toolchain='default') - default_host = 'macosx' if isDarwin else 'linux' + toolchain = host_toolchain(xcrun_toolchain="default") + default_host = "macosx" if isDarwin else "linux" default_architectures = platform.machine() - default_profile_input = os.path.join(SWIFT_SOURCE_ROOT, "swift", "utils", "parser-lib", "profile-input.swift") + default_profile_input = os.path.join( + SWIFT_SOURCE_ROOT, "swift", "utils", "parser-lib", "profile-input.swift" + ) default_jobs = multiprocessing.cpu_count() - default_build_dir = os.path.join(SWIFT_BUILD_ROOT, 'parser-lib') - default_install_prefix = defaults.DARWIN_INSTALL_PREFIX if isDarwin else UNIX_INSTALL_PREFIX + default_build_dir = os.path.join(SWIFT_BUILD_ROOT, "parser-lib") + default_install_prefix = ( + defaults.DARWIN_INSTALL_PREFIX if isDarwin else defaults.UNIX_INSTALL_PREFIX + ) default_ninja = toolchain.ninja - option('--release', store_true, - help='build in release mode') - option('--lto', store('lto_type'), - choices=['thin', 'full'], - const='full', - metavar='LTO_TYPE', - help='use lto optimization.' - 'Options: thin, full. If no optional arg is provided, full is ' - 'chosen by default') - option('--pgo', store('pgo_type'), - choices=['frontend', 'ir'], - const='ir', - metavar='PGO_TYPE', - help='use pgo optimization.' - 'Options: frontend, ir. If no optional arg is provided, ir is ' - 'chosen by default') - option('--profile-input', store_path, - default=default_profile_input, - help='the source file to use for PGO profiling input (default = %s)' % default_profile_input) - option('--no-assertions', store_true, - help='disable assertions') - option(['-v', '--verbose'], store_true, - help='print the commands executed during the build') - option('--dry-run', store_true, - help='print the commands to execute but not actually execute them') - option(['-j', '--jobs'], store_int('build_jobs'), - default=default_jobs, - help='the number of parallel build jobs to use (default = %s)' % default_jobs) - option('--build-dir', store_path, - default=default_build_dir, - help='the path where the build products will be placed. (default = %s)' % default_build_dir) - option('--host', store, - choices=['macosx', 'linux', 'iphonesimulator', 'iphoneos', 'appletvsimulator', 'appletvos', 'watchsimulator', 'watchos'], - default=default_host, - help='host platform to build for (default = %s)' % default_host) - option('--architectures', store, - default=default_architectures, - help='space-separated list of architectures to build for. (default = %s)' % default_architectures) - option('--install-symroot', store_path, - help='the path to install debug symbols into') - option('--install-destdir', store_path, - help='the path to use as the filesystem root for the installation (default = \'$(build_dir)/install\')') - option('--install-prefix', store, - default = default_install_prefix, - help='the install prefix to use (default = %s)' % default_install_prefix) - option('--version', store, - help='version string to use for the parser library') - option('--ninja-path', store_path, - default = default_ninja, - help='the path to ninja (default = %s)' % default_ninja) + option("--release", store_true, help="build in release mode") + option( + "--lto", + store("lto_type"), + choices=["thin", "full"], + const="full", + metavar="LTO_TYPE", + help="use lto optimization." + "Options: thin, full. If no optional arg is provided, full is " + "chosen by default", + ) + option( + "--pgo", + store("pgo_type"), + choices=["frontend", "ir"], + const="ir", + metavar="PGO_TYPE", + help="use pgo optimization." + "Options: frontend, ir. If no optional arg is provided, ir is " + "chosen by default", + ) + option( + "--profile-input", + store_path, + default=default_profile_input, + help="the source file to use for PGO profiling input (default = %s)" + % default_profile_input, + ) + option("--no-assertions", store_true, help="disable assertions") + option( + ["-v", "--verbose"], + store_true, + help="print the commands executed during the build", + ) + option( + "--dry-run", + store_true, + help="print the commands to execute but not actually execute them", + ) + option( + ["-j", "--jobs"], + store_int("build_jobs"), + default=default_jobs, + help="the number of parallel build jobs to use (default = %s)" % default_jobs, + ) + option( + "--build-dir", + store_path, + default=default_build_dir, + help="the path where the build products will be placed. (default = %s)" + % default_build_dir, + ) + option( + "--host", + store, + choices=[ + "macosx", + "linux", + "iphonesimulator", + "iphoneos", + "appletvsimulator", + "appletvos", + "watchsimulator", + "watchos", + ], + default=default_host, + help="host platform to build for (default = %s)" % default_host, + ) + option( + "--architectures", + store, + default=default_architectures, + help="space-separated list of architectures to build for. (default = %s)" + % default_architectures, + ) + option( + "--install-symroot", store_path, help="the path to install debug symbols into" + ) + option( + "--install-destdir", + store_path, + help="the path to use as the filesystem root for the installation (default = " + "'$(build_dir)/install')", + ) + option( + "--install-prefix", + store, + default=default_install_prefix, + help="the install prefix to use (default = %s)" % default_install_prefix, + ) + option("--version", store, help="version string to use for the parser library") + option( + "--ninja-path", + store_path, + default=default_ninja, + help="the path to ninja (default = %s)" % default_ninja, + ) parser = optbuilder.build() args = parser.parse_args() if not args.install_destdir: - args.install_destdir = os.path.join(args.build_dir, 'install') + args.install_destdir = os.path.join(args.build_dir, "install") architectures = args.architectures.split(" ") architectures = [arch for arch in architectures if arch != ""] @@ -342,7 +467,7 @@ def main(): profile_data = None dst_dirs = [] - if args.host == 'macosx' and architectures[0] == platform.machine(): + if args.host == "macosx" and architectures[0] == platform.machine(): # Build for the native machine. arch = architectures.pop(0) tmpargs = copy.copy(args) @@ -354,32 +479,37 @@ def main(): dst_dirs.append(tmpargs.install_destdir) if tmpargs.pgo_type: - profile_dir = os.path.join(objroot, platform.machine()+'-profiling') + profile_dir = os.path.join(objroot, platform.machine() + "-profiling") builder = Builder(toolchain, tmpargs, tmpargs.host, arch) builder.get_profile_data(profile_dir) profile_data = os.path.join(profile_dir, "profdata.prof") - builder = Builder(toolchain, tmpargs, tmpargs.host, arch, profile_data=profile_data) + builder = Builder( + toolchain, tmpargs, tmpargs.host, arch, profile_data=profile_data + ) builder.run() else: tmpargs = copy.copy(args) if tmpargs.pgo_type: # Build for the machine and get profile data. - native_build_dir = os.path.join(objroot, platform.machine()+'-profiling') - builder = Builder(toolchain, tmpargs, 'macosx', platform.machine()) + native_build_dir = os.path.join( + objroot, platform.machine() + "-profiling" + ) + builder = Builder(toolchain, tmpargs, "macosx", platform.machine()) builder.get_profile_data(native_build_dir) profile_data = os.path.join(native_build_dir, "profdata.prof") else: - # Build the tablegen binaries so we can use them for the cross-compile build. - native_build_dir = os.path.join(objroot, platform.machine()+'-tblgen') + # Build the tablegen binaries so we can use them for the cross-compile + # build. + native_build_dir = os.path.join(objroot, platform.machine() + "-tblgen") tmpargs.lto_type = None - builder = Builder(toolchain, tmpargs, 'macosx', platform.machine()) + builder = Builder(toolchain, tmpargs, "macosx", platform.machine()) shell.makedirs(native_build_dir, dry_run=tmpargs.dry_run) with shell.pushd(native_build_dir, dry_run=tmpargs.dry_run): builder.configure(enable_debuginfo=False) - builder.build_target(native_build_dir, 'llvm-tblgen') - builder.build_target(native_build_dir, 'clang-tblgen') + builder.build_target(native_build_dir, "llvm-tblgen") + builder.build_target(native_build_dir, "clang-tblgen") for arch in architectures: args.build_dir = os.path.join(objroot, arch, "obj") @@ -388,22 +518,35 @@ def main(): dst_dirs.append(args.install_destdir) - builder = Builder(toolchain, args, args.host, arch, profile_data=profile_data, native_build_dir=native_build_dir) + builder = Builder( + toolchain, + args, + args.host, + arch, + profile_data=profile_data, + native_build_dir=native_build_dir, + ) builder.run() lipo = os.path.join(SWIFT_SOURCE_ROOT, "swift", "utils", "recursive-lipo") - shell.call([lipo, "-v", "--destination", os.path.join(dstroot, "./"+prefix)] + dst_dirs) + shell.call( + [lipo, "-v", "--destination", os.path.join(dstroot, "./" + prefix)] + + dst_dirs + ) if args.install_symroot: extract_symbols(dstroot, prefix, symroot, args.build_jobs) return 0 - assert args.architectures == platform.machine(), "building for non-machine architecture is not supported for non-darwin host" + assert ( + args.architectures == platform.machine() + ), "building for non-machine architecture is not supported for non-darwin host" builder = Builder(toolchain, args, args.host, args.architectures) builder.run() return 0 + if __name__ == "__main__": try: sys.exit(main()) diff --git a/utils/dev-scripts/blockifyasm b/utils/dev-scripts/blockifyasm index 5d7267a7e2dfa..fe0c525fdd6ba 100755 --- a/utils/dev-scripts/blockifyasm +++ b/utils/dev-scripts/blockifyasm @@ -12,11 +12,11 @@ # ---------------------------------------------------------------------------- # # Splits a disassembled function from lldb into basic blocks. -# +# # Useful to show the control flow graph of a disassembled function. # The control flow graph can the be viewed with the viewcfg utility: # -# (lldb) disassemble +# (lldb) disassemble # # $ blockifyasm < file.s | viewcfg # @@ -30,113 +30,120 @@ from collections import defaultdict def help(): - print("""\ + print( + """\ Usage: blockifyasm [-] < file -: only match significant digits of relative branch addresses -""") +""" + ) def main(): - addr_len = 16 - if len(sys.argv) >= 2: - m = re.match('^-([0-9]+)$', sys.argv[1]) - if m: - addr_len = int(m.group(1)) - else: - help() - return - - lines = [] - block_starts = {} - - branch_re1 = re.compile('^\s[-\s>]*0x.*:\s.* 0x([0-9a-f]+)\s*;\s*<[+-]') - branch_re2 = re.compile('^\s[-\s>]*0x.*:\s+tb.* 0x([0-9a-f]+)\s*(;.*)?') - inst_re = re.compile('^\s[-\s>]*0x([0-9a-f]+)[\s<>0-9+-]*:\s+([a-z0-9.]+)\s') - non_fall_through_insts = [ 'b', 'ret', 'brk', 'jmp', 'retq', 'ud2' ] - - def get_branch_addr(line): - bm = branch_re1.match(line) - if bm: - return bm.group(1)[-addr_len:] - bm = branch_re2.match(line) - if bm: - return bm.group(1)[-addr_len:] - return None - - def print_function(): - if not lines: - return - predecessors = defaultdict(list) - block_num = -1 - next_is_block = True - prev_is_fallthrough = False - - # Collect predecessors for all blocks - for line in lines: - m = inst_re.match(line) - assert m, "non instruction line in function" - addr = m.group(1)[-addr_len:] - inst = m.group(2) - if next_is_block or addr in block_starts: - if prev_is_fallthrough: - predecessors[addr].append(block_num) - - block_num += 1 - block_starts[addr] = block_num - next_is_block = False - - prev_is_fallthrough = True - br_addr = get_branch_addr(line) - if br_addr: + addr_len = 16 + if len(sys.argv) >= 2: + m = re.match("^-([0-9]+)$", sys.argv[1]) + if m: + addr_len = int(m.group(1)) + else: + help() + return + + lines = [] + block_starts = {} + + branch_re1 = re.compile(r"^\s[-\s>]*0x.*:\s.* 0x([0-9a-f]+)\s*;\s*<[+-]") + branch_re2 = re.compile(r"^\s[-\s>]*0x.*:\s+tb.* 0x([0-9a-f]+)\s*(;.*)?") + inst_re = re.compile(r"^\s[-\s>]*0x([0-9a-f]+)[\s<>0-9+-]*:\s+([a-z0-9.]+)\s") + non_fall_through_insts = ["b", "ret", "brk", "jmp", "retq", "ud2"] + + def get_branch_addr(line): + bm = branch_re1.match(line) + if bm: + return bm.group(1)[-addr_len:] + bm = branch_re2.match(line) + if bm: + return bm.group(1)[-addr_len:] + return None + + def print_function(): + if not lines: + return + predecessors = defaultdict(list) + block_num = -1 next_is_block = True - predecessors[br_addr].append(block_num) - - prev_is_fallthrough = (not inst in non_fall_through_insts) - - # Print the function with basic block labels - print('{') - for line in lines: - m = inst_re.match(line) - if m: - addr = m.group(1)[-addr_len:] - if addr in block_starts: - blockstr = 'bb' + str(block_starts[addr]) + ':' - if predecessors[addr]: - print(blockstr + ' ' * (55 - len(blockstr)) + '; preds = ', end='') - print(', '.join('bb' + str(pred) for pred in predecessors[addr])) - else: - print(blockstr) - - br_addr = get_branch_addr(line) - if br_addr and block_starts[br_addr] >= 0: - line = re.sub(';\s<[+-].*', '; bb' + str(block_starts[br_addr]), line) - - print(line, end='') - print('}') - - # Read disassembly code from stdin - for line in sys.stdin: - # let the line with the instruction pointer begin with a space - line = re.sub('^-> ', ' ->', line) - - if inst_re.match(line): - lines.append(line) - br_addr = get_branch_addr(line) - if br_addr: - if len(br_addr) < addr_len: - addr_len = len(br_addr) - block_starts[br_addr] = -1 - else: - print_function() - lines = [] - block_starts = {} - print(line, end='') - - print_function() - -if __name__ == '__main__': + prev_is_fallthrough = False + + # Collect predecessors for all blocks + for line in lines: + m = inst_re.match(line) + assert m, "non instruction line in function" + addr = m.group(1)[-addr_len:] + inst = m.group(2) + if next_is_block or addr in block_starts: + if prev_is_fallthrough: + predecessors[addr].append(block_num) + + block_num += 1 + block_starts[addr] = block_num + next_is_block = False + + prev_is_fallthrough = True + br_addr = get_branch_addr(line) + if br_addr: + next_is_block = True + predecessors[br_addr].append(block_num) + + prev_is_fallthrough = inst not in non_fall_through_insts + + # Print the function with basic block labels + print("{") + for line in lines: + m = inst_re.match(line) + if m: + addr = m.group(1)[-addr_len:] + if addr in block_starts: + blockstr = "bb" + str(block_starts[addr]) + ":" + if predecessors[addr]: + print( + blockstr + " " * (55 - len(blockstr)) + "; preds = ", end="" + ) + print( + ", ".join("bb" + str(pred) for pred in predecessors[addr]) + ) + else: + print(blockstr) + + br_addr = get_branch_addr(line) + if br_addr and block_starts[br_addr] >= 0: + line = re.sub(r";\s<[+-].*", "; bb" + str(block_starts[br_addr]), line) + + print(line, end="") + print("}") + + # Read disassembly code from stdin + for line in sys.stdin: + # let the line with the instruction pointer begin with a space + line = re.sub("^-> ", " ->", line) + + if inst_re.match(line): + lines.append(line) + br_addr = get_branch_addr(line) + if br_addr: + if len(br_addr) < addr_len: + addr_len = len(br_addr) + block_starts[br_addr] = -1 + else: + print_function() + lines = [] + block_starts = {} + print(line, end="") + + print_function() + + +if __name__ == "__main__": main() diff --git a/utils/dev-scripts/split-cmdline b/utils/dev-scripts/split-cmdline index 736b1a20cec7e..f09051de9f0c4 100755 --- a/utils/dev-scripts/split-cmdline +++ b/utils/dev-scripts/split-cmdline @@ -26,7 +26,7 @@ # -color-diagnostics \ # -module-name hello \ # -o hello.o -# +# # Example usage in vim: # *) make sure that split-cmdline is in the $PATH # *) copy-paste the swift command line the text buffer @@ -38,58 +38,94 @@ from __future__ import print_function -import re -import sys import os +import re import shlex +import sys -def main(): - for line in sys.stdin: - first = True - is_arg_param = False - # Handle escaped spaces - args = shlex.split(line) - for arg in args: - if arg == '': - continue - if not first: - # Print option arguments in the same line - print(' ' if is_arg_param else ' \\\n ', end='') - first = False - # Expand @ option files - m = re.match('^@(\S+\.txt)$', arg) - if m: - cmd_file = m.group(1) - if os.path.isfile(cmd_file): - with open(cmd_file) as f: - for ln in f.readlines(): - for name in ln.rstrip().split(';'): - if name != '': - print(name + ' \\') +def main(): + for line in sys.stdin: first = True - continue + is_arg_param = False + # Handle escaped spaces + args = shlex.split(line) + for arg in args: + if arg == "": + continue + if not first: + # Print option arguments in the same line + print(" " if is_arg_param else " \\\n ", end="") + first = False + + # Expand @ option files + m = re.match(r"^@(\S+\.txt)$", arg) + if m: + cmd_file = m.group(1) + if os.path.isfile(cmd_file): + with open(cmd_file) as f: + for ln in f.readlines(): + for name in ln.rstrip().split(";"): + if name != "": + print(name + " \\") + first = True + continue + + if " " in arg: + print('"' + arg + '"', end="") + else: + print(arg, end="") - if ' ' in arg: - print('"' + arg + '"', end='') - else: - print(arg, end='') + # A hard-coded list of options which expect a parameter + is_arg_param = arg in [ + "-o", + "-target", + "-isysroot", + "-emit-sil", + "-emit-ir", + "-module-name", + "-framework", + "-Xlinker", + "-arch", + "-D", + "-sdk", + "-module-cache-path", + "-F", + "-output-file-map", + "-emit-module-path", + "-Xcc", + "-I", + "-iquote", + "-emit-objc-header-path", + "-Xfrontend", + "-filelist", + "-num-threads", + "-Xclang", + "-x", + "-L", + "-rpath", + "-macosx_version_min", + "-syslibroot", + "-add_ast_path", + "-import-objc-header", + "-serialize-diagnostics-path", + "-emit-dependencies-path", + "-emit-reference-dependencies-path", + "-primary-file", + "-resource-dir", + "--sdk", + "--toolchain", + "-emit-module-doc-path", + "-module-link-name", + "-group-info-path", + "-fileno", + "-swift-version", + "-Xllvm", + ] - # A hard-coded list of options which expect a parameter - is_arg_param = (arg in [ - '-o', '-target', '-isysroot', '-emit-sil', '-emit-ir', '-module-name', - '-framework', '-Xlinker', '-arch', '-D', '-sdk', '-module-cache-path', - '-F', '-output-file-map', '-emit-module-path', '-Xcc', '-I', '-iquote', - '-emit-objc-header-path', '-Xfrontend', '-filelist', '-num-threads', - '-Xclang', '-x', '-L', '-rpath', '-macosx_version_min', - '-syslibroot', '-add_ast_path', '-import-objc-header', - '-serialize-diagnostics-path', '-emit-dependencies-path', - '-emit-reference-dependencies-path', '-primary-file', '-resource-dir', - '--sdk', '--toolchain', '-emit-module-doc-path', '-module-link-name', - '-group-info-path', '-fileno', '-swift-version', '-Xllvm']) + # Print 2 new lines after each command line + print("\n") - # Print 2 new lines after each command line - print('\n') -if __name__ == '__main__': +if __name__ == "__main__": main() From b903382866e7d90df43deecc0a9f40428f86a06b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 6 Feb 2020 15:52:28 -0500 Subject: [PATCH 197/237] ClangImporter: Fix importEnumCaseAlias() when the original is itself an alias This comes up when an imported error enum has duplicate cases. The FooError.Code enum has an alias for the duplicate case, and the wrapper type FooError defines aliases for every member of FooError.Code. The implementation of importEnumCaseAlias() assumed that 'original' was an EnumElementDecl, and built AST accordingly; however if it is a 'VarDecl', we have to build a MemberRefExpr instead. This regression was introduced in https://github.com/apple/swift/pull/25009. Fixes . --- lib/ClangImporter/ImportDecl.cpp | 41 +++++++++++++------ .../AliasCaseErrorEnum.apinotes | 5 +++ .../AliasCaseErrorEnum/AliasCaseErrorEnum.h | 10 +++++ .../AliasCaseErrorEnum/module.map | 4 ++ .../ClangImporter/enum-error-case-alias.swift | 11 +++++ 5 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.apinotes create mode 100644 test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.h create mode 100644 test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/module.map create mode 100644 test/ClangImporter/enum-error-case-alias.swift diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index c217fb2c86a19..6cc426f87d429 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -5823,23 +5823,38 @@ Decl *SwiftDeclConverter::importEnumCaseAlias( if (!importIntoDC) importIntoDC = importedEnum; - // Construct the original constant. Enum constants without payloads look - // like simple values, but actually have type 'MyEnum.Type -> MyEnum'. - auto constantRef = - new (Impl.SwiftContext) DeclRefExpr(original, DeclNameLoc(), - /*implicit*/ true); - constantRef->setType(original->getInterfaceType()); - Type importedEnumTy = importedEnum->getDeclaredInterfaceType(); - auto typeRef = TypeExpr::createImplicit(importedEnumTy, Impl.SwiftContext); - auto instantiate = new (Impl.SwiftContext) - DotSyntaxCallExpr(constantRef, SourceLoc(), typeRef); - instantiate->setType(importedEnumTy); - instantiate->setThrows(false); + + Expr *result = nullptr; + if (auto *enumElt = dyn_cast(original)) { + assert(!enumElt->hasAssociatedValues()); + + // Construct the original constant. Enum constants without payloads look + // like simple values, but actually have type 'MyEnum.Type -> MyEnum'. + auto constantRef = + new (Impl.SwiftContext) DeclRefExpr(enumElt, DeclNameLoc(), + /*implicit*/ true); + constantRef->setType(enumElt->getInterfaceType()); + + auto instantiate = new (Impl.SwiftContext) + DotSyntaxCallExpr(constantRef, SourceLoc(), typeRef); + instantiate->setType(importedEnumTy); + instantiate->setThrows(false); + + result = instantiate; + } else { + assert(isa(original)); + + result = + new (Impl.SwiftContext) MemberRefExpr(typeRef, SourceLoc(), + original, DeclNameLoc(), + /*implicit*/ true); + result->setType(original->getInterfaceType()); + } Decl *CD = Impl.createConstant(name, importIntoDC, importedEnumTy, - instantiate, ConstantConvertKind::None, + result, ConstantConvertKind::None, /*isStatic*/ true, alias); Impl.importAttributes(alias, CD); return CD; diff --git a/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.apinotes b/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.apinotes new file mode 100644 index 0000000000000..971bb7c6273ee --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.apinotes @@ -0,0 +1,5 @@ +--- +Name: AliasCaseErrorEnum +Tags: +- Name: AliasError + NSErrorDomain: AliasErrorDomain diff --git a/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.h b/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.h new file mode 100644 index 0000000000000..11e2a5db3fa50 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/AliasCaseErrorEnum.h @@ -0,0 +1,10 @@ +@import Foundation; + +#define kAliasErrorDomain "com.acme.AliasErrorDomain" + +extern NSString *const __nonnull AliasErrorDomain; + +typedef NS_ENUM(NSInteger, AliasError) { + AliasErrorRealName = 1, + AliasErrorFakeName = 1, +}; diff --git a/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/module.map b/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/module.map new file mode 100644 index 0000000000000..f5464be186333 --- /dev/null +++ b/test/ClangImporter/Inputs/custom-modules/AliasCaseErrorEnum/module.map @@ -0,0 +1,4 @@ +module AliasCaseErrorEnum { + umbrella header "AliasCaseErrorEnum.h" + export * +} diff --git a/test/ClangImporter/enum-error-case-alias.swift b/test/ClangImporter/enum-error-case-alias.swift new file mode 100644 index 0000000000000..6e407c0a8d81b --- /dev/null +++ b/test/ClangImporter/enum-error-case-alias.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil %s -enable-objc-interop -I %S/Inputs/custom-modules/AliasCaseErrorEnum + +// REQUIRES: objc_interop + +import AliasCaseErrorEnum + +// Make sure that we can reference aliases defined in the wrapper type +// which themselves point at aliases inside the nested 'Code' type. + +_ = AliasError.realName +_ = AliasError.fakeName \ No newline at end of file From c5718d5b1817670c2990a13624d13c06eb290755 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 6 Feb 2020 19:46:03 -0500 Subject: [PATCH 198/237] SILGen: Fix crash with function conversions involving class-constrained protocols If a protocol has a superclass constraint, the existential type can be upcast to the class type given by the constraint. This wasn't implemented in the function conversion code path, leading to a crash. Fixes / . --- lib/SILGen/SILGenPoly.cpp | 2 +- test/SILGen/protocol_with_superclass.swift | 19 +++++++++++++++++++ ...rotocol_with_superclass_where_clause.swift | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 16d45f0274957..35a80f38d75aa 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -569,7 +569,7 @@ ManagedValue Transform::transform(ManagedValue v, instanceType = metatypeType.getInstanceType(); auto layout = instanceType.getExistentialLayout(); - if (layout.explicitSuperclass) { + if (layout.getSuperclass()) { CanType openedType = OpenedArchetypeType::getAny(inputSubstType); SILType loweredOpenedType = SGF.getLoweredType(openedType); diff --git a/test/SILGen/protocol_with_superclass.swift b/test/SILGen/protocol_with_superclass.swift index c6661c611fd0f..ec704d46e6fe6 100644 --- a/test/SILGen/protocol_with_superclass.swift +++ b/test/SILGen/protocol_with_superclass.swift @@ -269,6 +269,25 @@ func passesRefinedProtocol(_ r: RefinedProto) { // CHECK-NEXT: [[RESULT:%.*]] = tuple () // CHECK-NEXT: return [[RESULT]] : $() +func takesFuncTakingRefinedProto(arg: (RefinedProto) -> ()) {} + +func passesFuncTakingBaseClass() { + let closure: (BaseClass) -> () = { _ in } + takesFuncTakingRefinedProto(arg: closure) +} + +// CHECK-LABEL: sil hidden [ossa] @$s24protocol_with_superclass25passesFuncTakingBaseClassyyF : $@convention(thin) () -> () + +// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$s24protocol_with_superclass9BaseClassCIegg_AA12RefinedProto_pIegg_TR : $@convention(thin) (@guaranteed RefinedProto, @guaranteed @callee_guaranteed (@guaranteed BaseClass) -> ()) -> () +// CHECK: [[PAYLOAD:%.*]] = open_existential_ref %0 : $RefinedProto to $@opened("{{.*}}") RefinedProto +// CHECK: [[COPY:%.*]] = copy_value [[PAYLOAD]] +// CHECK: [[UPCAST:%.*]] = upcast [[COPY]] : $@opened("{{.*}}") RefinedProto to $BaseClass +// CHECK: [[BORROW:%.*]] = begin_borrow [[UPCAST]] +// CHECK: apply %1([[BORROW]]) +// CHECK: end_borrow [[BORROW]] +// CHECK: destroy_value [[UPCAST]] +// CHECK: return + // CHECK-LABEL: sil_witness_table hidden ConformsToSillyDefault: SillyDefault module protocol_with_superclass { // CHECK-NEXT: method #SillyDefault.makeT!1: (Self) -> () -> Int : @$s24protocol_with_superclass22ConformsToSillyDefaultCAA0fG0A2aDP5makeTSiyFTW // CHECK-NEXT: } diff --git a/test/SILGen/protocol_with_superclass_where_clause.swift b/test/SILGen/protocol_with_superclass_where_clause.swift index 5de9f2a19230a..a38c176179a96 100644 --- a/test/SILGen/protocol_with_superclass_where_clause.swift +++ b/test/SILGen/protocol_with_superclass_where_clause.swift @@ -269,6 +269,25 @@ func passesRefinedProtocol(_ r: RefinedProto) { // CHECK-NEXT: [[RESULT:%.*]] = tuple () // CHECK-NEXT: return [[RESULT]] : $() +func takesFuncTakingRefinedProto(arg: (RefinedProto) -> ()) {} + +func passesFuncTakingBaseClass() { + let closure: (BaseClass) -> () = { _ in } + takesFuncTakingRefinedProto(arg: closure) +} + +// CHECK-LABEL: sil hidden [ossa] @$s24protocol_with_superclass25passesFuncTakingBaseClassyyF : $@convention(thin) () -> () + +// CHECK-LABEL: sil shared [transparent] [serializable] [reabstraction_thunk] [ossa] @$s24protocol_with_superclass9BaseClassCIegg_AA12RefinedProto_pIegg_TR : $@convention(thin) (@guaranteed RefinedProto, @guaranteed @callee_guaranteed (@guaranteed BaseClass) -> ()) -> () +// CHECK: [[PAYLOAD:%.*]] = open_existential_ref %0 : $RefinedProto to $@opened("{{.*}}") RefinedProto +// CHECK: [[COPY:%.*]] = copy_value [[PAYLOAD]] +// CHECK: [[UPCAST:%.*]] = upcast [[COPY]] : $@opened("{{.*}}") RefinedProto to $BaseClass +// CHECK: [[BORROW:%.*]] = begin_borrow [[UPCAST]] +// CHECK: apply %1([[BORROW]]) +// CHECK: end_borrow [[BORROW]] +// CHECK: destroy_value [[UPCAST]] +// CHECK: return + // CHECK-LABEL: sil_witness_table hidden ConformsToSillyDefault: SillyDefault module protocol_with_superclass { // CHECK-NEXT: method #SillyDefault.makeT!1: (Self) -> () -> Int : @$s24protocol_with_superclass22ConformsToSillyDefaultCAA0fG0A2aDP5makeTSiyFTW // CHECK-NEXT: } From 2113df4a758eebb4301a0f3f877826c422bc15f2 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 6 Feb 2020 17:27:41 -0800 Subject: [PATCH 199/237] Improve testcase This patch makes BoundGenericEnum more robust by splitting it into three separately executed FileCheck invocation. It further corrects an error in case (1) were the wrong variable was being checked. Finally, by enabling optimizations, it is easier to follow why the generic arguments have different sizes. --- test/DebugInfo/BoundGenericEnum.swift | 59 +++++++++++++-------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/test/DebugInfo/BoundGenericEnum.swift b/test/DebugInfo/BoundGenericEnum.swift index 204a1d47aff00..c79b966a76fad 100644 --- a/test/DebugInfo/BoundGenericEnum.swift +++ b/test/DebugInfo/BoundGenericEnum.swift @@ -1,5 +1,7 @@ -// RUN: %target-swift-frontend %s -Onone -emit-ir -g -o - -module-name a \ -// RUN: -disable-debugger-shadow-copies | %FileCheck %s +// RUN: %target-swift-frontend %s -O -emit-ir -g -o %t.ll -module-name a +// RUN: cat %t.ll | %FileCheck %s --check-prefix=CASE_0 +// RUN: cat %t.ll | %FileCheck %s --check-prefix=CASE_1 +// RUN: cat %t.ll | %FileCheck %s --check-prefix=CASE_2 public enum Result { case success(Value) case failure(Error) @@ -16,7 +18,9 @@ extension Result { } } +@inline(never) func use(_ t : T) { + print(t) } public class SomeClass { @@ -52,36 +56,29 @@ y.g() // identifier. // (0) unsized. -// CHECK: !DISubprogram(name: "map", {{.*}}line: 9, type: ![[SBTY:[0-9]+]] -// CHECK: ![[SBTY]] = !DISubroutineType(types: ![[SBTYS:[0-9]+]]) -// CHECK: ![[SBTYS]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[SELFTY:[0-9]+]]} -// CHECK: ![[SELFTY]] = -// CHECK-SAME: !DICompositeType(tag: DW_TAG_structure_type, {{.*}}line: 3, -// CHECK-SAME: elements: ![[UNSIZED_ELTS:[0-9]+]] -// CHECK: ![[UNSIZED_ELTS]] = !{![[UNSIZED_MEM:[0-9]+]]} -// CHECK: ![[UNSIZED_MEM]] = !DIDerivedType(tag: DW_TAG_member, -// CHECK-SAME: baseType: ![[UNIQ:[0-9]+]] +// CASE_0-DAG: !DISubprogram(name: "map", {{.*}}line: 11, type: ![[SBTY:[0-9]+]] +// CASE_0-DAG: ![[SBTY]] = !DISubroutineType(types: ![[SBTYS:[0-9]+]]) +// CASE_0-DAG: ![[SBTYS]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[SELFTY:[0-9]+]]} +// CASE_0-DAG: ![[SELFTY]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}line: 5, {{.*}}elements: ![[UNSIZED_ELTS:[0-9]+]] +// CASE_0-DAG: ![[UNSIZED_ELTS]] = !{![[UNSIZED_MEM:[0-9]+]]} +// CASE_0-DAG: ![[UNSIZED_MEM]] = !DIDerivedType(tag: DW_TAG_member, {{.*}} baseType: ![[UNIQ:[0-9]+]] // The unique unsized type. -// CHECK: ![[UNIQ]] = !DICompositeType( -// CHECK-SAME: tag: DW_TAG_structure_type, name: "Result", -// CHECK-SAME: line: 3, -// CHECK-NOT: size: -// CHECK-SAME: runtimeLang: DW_LANG_Swift, -// CHECK-SAME: identifier: "$s1a6ResultOyxGD") - -// (2) -// CHECK: !DILocalVariable(name: "self", arg: 2, {{.*}}line: 9, -// CHECK-SAME: type: ![[C_TUP:[0-9]+]] -// CHECK: ![[C_TUP]] = !DIDerivedType(tag: DW_TAG_const_type, -// CHECK-SAME: baseType: ![[TUP:[0-9]+]]) -// CHECK: ![[TUP]] = !DICompositeType(tag: DW_TAG_structure_type, -// CHECK-SAME: line: 3, size: {{256|512}}, +// CASE_0: ![[UNIQ]] = !DICompositeType( +// CASE_0-SAME: tag: DW_TAG_structure_type, name: "Result", +// CASE_0-SAME: line: 5, +// CASE_0-NOT: size: +// CASE_0-SAME: runtimeLang: DW_LANG_Swift, +// CASE_0-SAME: identifier: "$s1a6ResultOyxGD") // (1) -// CHECK: !DILocalVariable(name: "self", arg: 1, {{.*}}line: 27, -// CHECK-SAME: type: ![[C_CLASS:[0-9]+]] -// CHECK: ![[C_CLASS]] = !DIDerivedType(tag: DW_TAG_const_type, -// CHECK-SAME: baseType: ![[CLASS:[0-9]+]]) -// CHECK: ![[CLASS]] = !DICompositeType(tag: DW_TAG_structure_type, -// CHECK-SAME: line: 3, size: +// CASE_1-DAG: ![[F:[0-9]+]] = distinct !DISubprogram(name: "f", +// CASE_1-DAG: !DILocalVariable(name: "self", arg: 1, scope: ![[F]],{{.*}} line: 31,{{.*}} type: ![[C_CLASS:[0-9]+]] +// CASE_1-DAG: ![[C_CLASS]] = !DIDerivedType(tag: DW_TAG_const_type,{{.*}} baseType: ![[CLASS:[0-9]+]]) +// CASE_1-DAG: ![[CLASS]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}} line: 5, size: {{40|72}} + +// (2) +// CASE_2-DAG: ![[G:[0-9]+]] = distinct !DISubprogram(name: "g", +// CASE_2-DAG: !DILocalVariable(name: "self", arg: 1, scope: ![[G]],{{.*}} line: 38,{{.*}} type: ![[C_TUP:[0-9]+]] +// CASE_2-DAG: ![[C_TUP]] = !DIDerivedType(tag: DW_TAG_const_type,{{.*}} baseType: ![[TUP:[0-9]+]]) +// CASE_2-DAG: ![[TUP]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}} line: 5, size: {{264|512}}, From 07bee5cdda3538c0e1f9400e85d05f315aa17318 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Thu, 6 Feb 2020 17:29:56 -0800 Subject: [PATCH 200/237] Bump SymbolGraph format version (NFC) rdar://58853310 --- lib/SymbolGraphGen/FormatVersion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SymbolGraphGen/FormatVersion.h b/lib/SymbolGraphGen/FormatVersion.h index 74ac1c196c5b4..baf3b54654479 100644 --- a/lib/SymbolGraphGen/FormatVersion.h +++ b/lib/SymbolGraphGen/FormatVersion.h @@ -14,7 +14,7 @@ #define SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H #define SWIFT_SYMBOLGRAPH_FORMAT_MAJOR 0 -#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 1 +#define SWIFT_SYMBOLGRAPH_FORMAT_MINOR 2 #define SWIFT_SYMBOLGRAPH_FORMAT_PATCH 0 #endif // SWIFT_SYMBOLGRAPHGEN_FORMATVERSION_H From faee21b626877b526241feb9788b76fc758b6b2e Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Jan 2020 17:07:36 -0800 Subject: [PATCH 201/237] Implement Swift serialization and deserialization of Clang types. As part of this, we have to change the type export rules to prevent `@convention(c)` function types from being used in exported interfaces if they aren't serializable. This is a more conservative version of the original rule I had, which was to import such function-pointer types as opaque pointers. That rule would've completely prevented importing function-pointer types defined in bridging headers and so simply doesn't work, so we're left trying to catch the unsupportable cases retroactively. This has the unfortunate consequence that we can't necessarily serialize the internal state of the compiler, but that was already true due to normal type uses of aggregate types from bridging headers; if we can teach the compiler to reliably serialize such types, we should be able to use the same mechanisms for function types. This PR doesn't flip the switch to use Clang function types by default, so many of the clang-function-type-serialization FIXMEs are still in place. --- include/swift/AST/ASTContext.h | 4 + include/swift/AST/ClangModuleLoader.h | 101 ++++++ include/swift/AST/DiagnosticsSema.def | 4 + include/swift/ClangImporter/ClangImporter.h | 10 + .../ClangImporter/SwiftAbstractBasicReader.h | 95 +++++ .../ClangImporter/SwiftAbstractBasicWriter.h | 90 +++++ lib/AST/ASTContext.cpp | 12 + lib/AST/ASTDumper.cpp | 42 +++ lib/AST/ClangTypeConverter.cpp | 20 ++ lib/AST/ClangTypeConverter.h | 9 + lib/ClangImporter/CMakeLists.txt | 1 + lib/ClangImporter/ImporterImpl.h | 16 + lib/ClangImporter/Serializability.cpp | 329 ++++++++++++++++++ lib/Sema/TypeCheckAccess.cpp | 23 ++ lib/Serialization/DeclTypeRecordNodes.def | 2 + lib/Serialization/Deserialization.cpp | 162 ++++++++- lib/Serialization/ModuleFile.cpp | 4 + lib/Serialization/ModuleFile.h | 8 + lib/Serialization/ModuleFormat.h | 28 +- lib/Serialization/Serialization.cpp | 157 ++++++++- lib/Serialization/Serialization.h | 20 ++ .../unserializable-clang-function-types.swift | 6 + .../clang-importer-sdk/usr/include/ctypes.h | 11 + test/Sema/clang_types_in_ast.swift | 4 +- .../Inputs/def_clang_function_types.swift | 15 + test/Serialization/clang-function-types.swift | 30 ++ 26 files changed, 1185 insertions(+), 18 deletions(-) create mode 100644 include/swift/ClangImporter/SwiftAbstractBasicReader.h create mode 100644 include/swift/ClangImporter/SwiftAbstractBasicWriter.h create mode 100644 lib/ClangImporter/Serializability.cpp create mode 100644 test/ClangImporter/unserializable-clang-function-types.swift create mode 100644 test/Serialization/Inputs/def_clang_function_types.swift create mode 100644 test/Serialization/clang-function-types.swift diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index be98a06362e3c..9b58089141b5b 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -595,6 +595,10 @@ class ASTContext final { const FunctionType::ExtInfo incompleteExtInfo, FunctionTypeRepresentation trueRep); + /// Get the Swift declaration that a Clang declaration was exported from, + /// if applicable. + const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); + /// Determine whether the given Swift type is representable in a /// given foreign language. ForeignRepresentationInfo diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 0e3d109226fd0..595c55b420c33 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -14,10 +14,12 @@ #define SWIFT_AST_CLANG_MODULE_LOADER_H #include "swift/AST/ModuleLoader.h" +#include "swift/Basic/TaggedUnion.h" namespace clang { class ASTContext; class CompilerInstance; +class Decl; class Preprocessor; class Sema; class TargetInfo; @@ -26,6 +28,7 @@ class Type; namespace swift { +class Decl; class DeclContext; class VisibleDeclConsumer; @@ -40,6 +43,69 @@ enum class ClangTypeKind { ObjCProtocol, }; +/// A path for serializing a declaration. +class StableSerializationPath { +public: + struct ExternalPath { + enum ComponentKind { + /// A named record type (but not a template specialization) + Record, + + /// A named enum type + Enum, + + /// A C++ namespace + Namespace, + + /// A typedef + Typedef, + + /// A typedef's anonymous tag declaration. Identifier is empty. + TypedefAnonDecl, + + /// An Objective-C interface. + ObjCInterface, + + /// An Objective-C protocol. + ObjCProtocol, + }; + + static bool requiresIdentifier(ComponentKind kind) { + return kind != TypedefAnonDecl; + } + + SmallVector, 2> Path; + + void add(ComponentKind kind, Identifier name) { + Path.push_back({kind, name}); + } + }; +private: + TaggedUnion Union; + +public: + StableSerializationPath() {} + StableSerializationPath(const Decl *d) : Union(d) {} + StableSerializationPath(ExternalPath ext) : Union(ext) {} + + explicit operator bool() const { return !Union.empty(); } + + bool isSwiftDecl() const { return Union.isa(); } + const Decl *getSwiftDecl() const { + assert(isSwiftDecl()); + return Union.get(); + } + + bool isExternalPath() const { return Union.isa(); } + const ExternalPath &getExternalPath() const { + assert(isExternalPath()); + return Union.get(); + } + + SWIFT_DEBUG_DUMP; + void dump(raw_ostream &os) const; +}; + class ClangModuleLoader : public ModuleLoader { private: virtual void anchor(); @@ -111,6 +177,41 @@ class ClangModuleLoader : public ModuleLoader { /// Print the Clang type. virtual void printClangType(const clang::Type *type, llvm::raw_ostream &os) const = 0; + + /// Try to find a stable serialization path for the given declaration, + /// if there is one. + virtual StableSerializationPath + findStableSerializationPath(const clang::Decl *decl) const = 0; + + /// Try to resolve a stable serialization path down to the original + /// declaration. + virtual const clang::Decl * + resolveStableSerializationPath(const StableSerializationPath &path) const = 0; + + /// Determine whether the given type is serializable. + /// + /// If \c checkCanonical is true, checks the canonical type, + /// not the given possibly-sugared type. In general: + /// - non-canonical representations should be preserving the + /// sugared type even if it isn't serializable, since that + /// maintains greater source fidelity; + /// - semantic checks need to be checking the serializability + /// of the canonical type, since it's always potentially + /// necessary to serialize that (e.g. in SIL); and + /// - serializers can try to serialize the sugared type to + /// maintain source fidelity and just fall back on the canonical + /// type if that's not possible. + /// + /// The expectation here is that this predicate is meaningful + /// independent of the actual form of serialization: the types + /// that we can't reliably binary-serialize without an absolute + /// Clang AST cross-reference are the same types that won't + /// reliably round-trip through a textual format. At the very + /// least, it's probably best to use conservative predicates + /// that work both ways so that language behavior doesn't differ + /// based on subtleties like the target module interface format. + virtual bool isSerializable(const clang::Type *type, + bool checkCanonical) const = 0; }; } // namespace swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 945e7c94fd301..97a5388345962 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2660,6 +2660,10 @@ ERROR(assoc_conformance_from_implementation_only_module,none, "cannot use conformance of %0 to %1 in associated type %3 (inferred as " "%4); %2 has been imported as implementation-only", (Type, DeclName, Identifier, Type, Type)) +ERROR(unexportable_clang_function_type,none, + "cannot export the underlying C type of the function type %0; " + "it may use anonymous types or types defined outside of a module", + (Type)) WARNING(warn_implementation_only_conflict,none, "%0 inconsistently imported as implementation-only", diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 9ac0d73ef7b9b..c736cdd2a9473 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -422,6 +422,16 @@ class ClangImporter final : public ClangModuleLoader { SourceLoc loc) const override; void printClangType(const clang::Type *type, llvm::raw_ostream &os) const override; + + StableSerializationPath + findStableSerializationPath(const clang::Decl *decl) const override; + + const clang::Decl * + resolveStableSerializationPath( + const StableSerializationPath &path) const override; + + bool isSerializable(const clang::Type *type, + bool checkCanonical) const override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/include/swift/ClangImporter/SwiftAbstractBasicReader.h b/include/swift/ClangImporter/SwiftAbstractBasicReader.h new file mode 100644 index 0000000000000..f75f7261e0e60 --- /dev/null +++ b/include/swift/ClangImporter/SwiftAbstractBasicReader.h @@ -0,0 +1,95 @@ +//===- SwiftAbstractBasicReader.h - Clang serialization adapter -*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides an intermediate CRTP class which implements most of +// Clang's AbstractBasicReader interface, paralleling the behavior defined +// in SwiftAbstractBasicWriter. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICREADER_H +#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICREADER_H + +#include "clang/AST/AbstractTypeReader.h" + +// This include is required to instantiate the template code in +// AbstractBasicReader.h, i.e. it's a workaround to an include-what-you-use +// violation. +#include "clang/AST/DeclObjC.h" + +namespace swift { + +/// An implementation of Clang's AbstractBasicReader interface for a Swift +/// datastream-based reader. This is paired with the AbstractBasicWriter +/// implementation in SwiftAbstractBasicWriter.h. Note that the general +/// expectation is that the types and declarations involved will have passed +/// a serializability check when this is used for actual deserialization. +/// +/// The subclass must implement: +/// uint64_t readUInt64(); +/// clang::IdentifierInfo *readIdentifier(); +/// clang::Stmt *readStmtRef(); +/// clang::Decl *readDeclRef(); +template +class DataStreamBasicReader + : public clang::serialization::DataStreamBasicReader { + using super = clang::serialization::DataStreamBasicReader; +public: + using super::asImpl; + using super::getASTContext; + + DataStreamBasicReader(clang::ASTContext &ctx) : super(ctx) {} + + /// Perform all the calls necessary to write out the given type. + clang::QualType readTypeRef() { + auto kind = clang::Type::TypeClass(asImpl().readUInt64()); + return clang::serialization::AbstractTypeReader(asImpl()).read(kind); + } + + bool readBool() { + return asImpl().readUInt64() != 0; + } + + uint32_t readUInt32() { + return uint32_t(asImpl().readUInt64()); + } + + clang::Selector readSelector() { + uint64_t numArgsPlusOne = asImpl().readUInt64(); + + // The null case. + if (numArgsPlusOne == 0) + return clang::Selector(); + + unsigned numArgs = unsigned(numArgsPlusOne - 1); + SmallVector chunks; + for (unsigned i = 0, e = std::max(numArgs, 1U); i != e; ++i) + chunks.push_back(asImpl().readIdentifier()); + + return getASTContext().Selectors.getSelector(numArgs, chunks.data()); + } + + clang::SourceLocation readSourceLocation() { + // Always read null. + return clang::SourceLocation(); + } + + clang::QualType readQualType() { + clang::Qualifiers quals = asImpl().readQualifiers(); + clang::QualType type = asImpl().readTypeRef(); + return getASTContext().getQualifiedType(type, quals); + } +}; + +} + +#endif diff --git a/include/swift/ClangImporter/SwiftAbstractBasicWriter.h b/include/swift/ClangImporter/SwiftAbstractBasicWriter.h new file mode 100644 index 0000000000000..486bee2d01b21 --- /dev/null +++ b/include/swift/ClangImporter/SwiftAbstractBasicWriter.h @@ -0,0 +1,90 @@ +//===- SwiftAbstractBasicWriter.h - Clang serialization adapter -*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides an intermediate CRTP class which implements most of +// Clang's AbstractBasicWriter interface, allowing largely the same logic +// to be used for both the importer's "can this be serialized" checks and +// the serializer's actual serialization logic. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H +#define SWIFT_CLANGIMPORTER_SWIFTABSTRACTBASICWRITER_H + +#include "clang/AST/AbstractTypeWriter.h" + +namespace swift { + +/// An implementation of Clang's AbstractBasicWriter interface for a Swift +/// datastream-based reader. This is paired with the AbstractBasicReader +/// implementation in SwiftAbstractBasicReader.h. Note that the general +/// expectation is that the types and declarations involved will have passed +/// a serializability check when this is used for actual serialization. +/// The code in this class is also used when implementing that +/// serializability check and so must be a little more cautious. +/// +/// The subclass must implement: +/// void writeUInt64(uint64_t value); +/// void writeIdentifier(const clang::IdentifierInfo *ident); +/// void writeStmtRef(const clang::Stmt *stmt); +/// void writeDeclRef(const clang::Decl *decl); +template +class DataStreamBasicWriter + : public clang::serialization::DataStreamBasicWriter { + using super = clang::serialization::DataStreamBasicWriter; +public: + using super::asImpl; + + /// Perform all the calls necessary to write out the given type. + void writeTypeRef(const clang::Type *type) { + asImpl().writeUInt64(uint64_t(type->getTypeClass())); + clang::serialization::AbstractTypeWriter(asImpl()).write(type); + } + + void writeBool(bool value) { + asImpl().writeUInt64(uint64_t(value)); + } + + void writeUInt32(uint32_t value) { + asImpl().writeUInt64(uint64_t(value)); + } + + void writeSelector(clang::Selector selector) { + if (selector.isNull()) { + asImpl().writeUInt64(0); + return; + } + + asImpl().writeUInt64(selector.getNumArgs() + 1); + for (unsigned i = 0, e = std::max(selector.getNumArgs(), 1U); i != e; ++i) + asImpl().writeIdentifier(selector.getIdentifierInfoForSlot(i)); + } + + void writeSourceLocation(clang::SourceLocation loc) { + // DataStreamBasicReader will always read null; the serializability + // check overrides this to complain about non-null source locations. + } + + void writeQualType(clang::QualType type) { + assert(!type.isNull()); + + auto split = type.split(); + asImpl().writeQualifiers(split.Quals); + + // Just recursively visit the given type. + asImpl().writeTypeRef(split.Ty); + } +}; + +} + +#endif diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index a5d494d2805df..fba8d7f465ecd 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4391,6 +4391,18 @@ ASTContext::getClangFunctionType(ArrayRef params, return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep); } +const Decl * +ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) { + auto &impl = getImpl(); + + // If we haven't exported anything yet, this must not be how we found + // this declaration. + if (!impl.Converter) return nullptr; + + return impl.Converter->getSwiftDeclForExportedClangDecl(decl); +} + + CanGenericSignature ASTContext::getSingleGenericParameterSignature() const { if (auto theSig = getImpl().SingleGenericParameterSignature) return theSig; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index dd93348812558..2a34900d480df 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/ASTVisitor.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/Initializer.h" @@ -3808,3 +3809,44 @@ StringRef swift::getAccessorKindString(AccessorKind value) { llvm_unreachable("Unhandled AccessorKind in switch."); } + +void StableSerializationPath::dump() const { + dump(llvm::errs()); +} + +static StringRef getExternalPathComponentKindString( + StableSerializationPath::ExternalPath::ComponentKind kind) { + switch (kind) { +#define CASE(ID, STRING) \ + case StableSerializationPath::ExternalPath::ID: return STRING; + CASE(Record, "record") + CASE(Enum, "enum") + CASE(Namespace, "namespace") + CASE(Typedef, "typedef") + CASE(TypedefAnonDecl, "anonymous tag") + CASE(ObjCInterface, "@interface") + CASE(ObjCProtocol, "@protocol") +#undef CASE + } + llvm_unreachable("bad kind"); +} + +void StableSerializationPath::dump(llvm::raw_ostream &os) const { + if (isSwiftDecl()) { + os << "clang decl of:\n"; + getSwiftDecl()->dump(os, 2); + } else { + auto &path = getExternalPath(); + using ExternalPath = StableSerializationPath::ExternalPath; + os << "external path: "; + size_t index = 0; + for (auto &entry : path.Path) { + if (index++) os << " -> "; + os << getExternalPathComponentKindString(entry.first); + if (ExternalPath::requiresIdentifier(entry.first)) { + os << "(" << entry.second << ")"; + } + } + os << "\n"; + } +} diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 56b07d0dcd17b..14b9aec5f289f 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -397,6 +397,8 @@ clang::QualType ClangTypeConverter::visitProtocolType(ProtocolType *type) { PDecl->getASTContext(), proto->getObjCRuntimeName(runtimeNameBuffer))); + registerExportedClangDecl(proto, PDecl); + auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, &PDecl, 1); return clangCtx.getObjCObjectPointerType(clangType); @@ -446,6 +448,8 @@ clang::QualType ClangTypeConverter::visitClassType(ClassType *type) { CDecl->getASTContext(), swiftDecl->getObjCRuntimeName(runtimeNameBuffer))); + registerExportedClangDecl(swiftDecl, CDecl); + auto clangType = clangCtx.getObjCInterfaceType(CDecl); return clangCtx.getObjCObjectPointerType(clangType); } @@ -726,3 +730,19 @@ clang::QualType ClangTypeConverter::convert(Type type) { Cache.insert({type, result}); return result; } + +void ClangTypeConverter::registerExportedClangDecl(Decl *swiftDecl, + const clang::Decl *clangDecl) { + assert(clangDecl->isCanonicalDecl() && + "generated Clang declaration for Swift declaration should not " + "have multiple declarations"); + ReversedExportMap.insert({clangDecl, swiftDecl}); +} + +Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( + const clang::Decl *decl) const { + // We don't need to canonicalize the declaration because these exported + // declarations are never redeclarations. + auto it = ReversedExportMap.find(decl); + return (it != ReversedExportMap.end() ? it->second : nullptr); +} diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index b25f6b6dffb92..450b9e30cf19a 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -33,6 +33,7 @@ class ClangTypeConverter : using super = TypeVisitor; llvm::DenseMap Cache; + llvm::DenseMap ReversedExportMap; bool StdlibTypesAreCached = false; @@ -73,11 +74,19 @@ class ClangTypeConverter : ArrayRef params, Type resultTy, AnyFunctionType::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. + Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; + private: clang::QualType convert(Type type); clang::QualType convertMemberType(NominalTypeDecl *DC, StringRef memberName); + void registerExportedClangDecl(Decl *swiftDecl, + const clang::Decl *clangDecl); + clang::QualType reverseBuiltinTypeMapping(StructType *type); friend TypeVisitor; diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index 6417184633427..d1fb2a6f8d0a0 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -17,6 +17,7 @@ add_swift_host_library(swiftClangImporter STATIC ImportMacro.cpp ImportName.cpp ImportType.cpp + Serializability.cpp SwiftLookupTable.cpp ) target_link_libraries(swiftClangImporter PRIVATE diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 517fde6fce0bc..f39a1194c7ce1 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -963,6 +963,22 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation bool isOverAligned(const clang::TypeDecl *typeDecl); bool isOverAligned(clang::QualType type); + /// Determines whether the given Clang type is serializable in a + /// Swift AST. This should only be called after successfully importing + /// the type, because it will look for a stable serialization path for any + /// referenced declarations, which may depend on whether there's a known + /// import of it. (It will not try to import the declaration to avoid + /// circularity problems.) + /// + /// Note that this will only check the requested sugaring of the given + /// type (depending on \c checkCanonical); the canonical type may be + /// serializable even if the non-canonical type is not, or vice-versa. + bool isSerializable(clang::QualType type, bool checkCanonical); + + /// Try to find a stable Swift serialization path for the given Clang + /// declaration. + StableSerializationPath findStableSerializationPath(const clang::Decl *decl); + /// Look up and attempt to import a Clang declaration with /// the given name. Decl *importDeclByName(StringRef name); diff --git a/lib/ClangImporter/Serializability.cpp b/lib/ClangImporter/Serializability.cpp new file mode 100644 index 0000000000000..2606964d6d078 --- /dev/null +++ b/lib/ClangImporter/Serializability.cpp @@ -0,0 +1,329 @@ +//===--- Serializability.cpp - Swift serializability of Clang AST refs ----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements support for working with StableSerializationPaths +// and determining whether references to Clang declarations and types are +// serializable. +// +// The expectation here is that the same basic predicates are +// interesting for both binary (.swiftmodule) and textual +// (.swiftinterface) serialization. For textual serialization, the +// key question is whether a printed representation will round-trip. +// +//===----------------------------------------------------------------------===// + +#include "ImporterImpl.h" +#include "swift/ClangImporter/SwiftAbstractBasicWriter.h" + +using namespace swift; + +using ExternalPath = StableSerializationPath::ExternalPath; + +static bool isSameDecl(const clang::Decl *lhs, const clang::Decl *rhs) { + return lhs == rhs || lhs->getCanonicalDecl() == rhs->getCanonicalDecl(); +} + +namespace { +class SerializationPathFinder { + ClangImporter::Implementation &Impl; +public: + SerializationPathFinder(ClangImporter::Implementation &impl) : Impl(impl) {} + + StableSerializationPath find(const clang::Decl *decl) { + // We can't do anything with non-NamedDecl declarations. + auto named = dyn_cast(decl); + if (!named) return StableSerializationPath(); + + if (decl->isFromASTFile()) { + return findImportedPath(named); + } + + // If the declaration isn't from an AST file, it might be something that + // we built automatically when exporting a Swift type. + if (auto swiftDecl = + Impl.SwiftContext.getSwiftDeclForExportedClangDecl(decl)) + return swiftDecl; + + // Otherwise we have no way to find it. + return StableSerializationPath(); + } + +private: + Identifier getIdentifier(const clang::IdentifierInfo *clangIdent) { + return Impl.SwiftContext.getIdentifier(clangIdent->getName()); + } + + StableSerializationPath findImportedPath(const clang::NamedDecl *decl) { + // We've almost certainly imported this declaration, look for it. + if (auto swiftDecl = Impl.importDeclCached(decl, Impl.CurrentVersion)) { + // The serialization code doesn't allow us to cross-reference + // typealias declarations directly. We could fix that, but it's + // easier to just avoid doing so and fall into the external-path code. + if (!isa(swiftDecl)) { + // Only accept this declaration if it round-trips. + if (auto swiftClangDecl = swiftDecl->getClangDecl()) + if (isSameDecl(decl, swiftClangDecl)) + return swiftDecl; + } + } + + // Otherwise, check to see if it's something we can easily find. + ExternalPath path; + if (findExternalPath(decl, path)) + return std::move(path); + + // Otherwise we have no way to find it. + return StableSerializationPath(); + } + + bool findExternalPath(const clang::NamedDecl *decl, ExternalPath &path) { + if (auto tag = dyn_cast(decl)) + return findExternalPath(tag, path); + if (auto alias = dyn_cast(decl)) + return findExternalPath(alias, path); + if (auto proto = dyn_cast(decl)) + return findExternalPath(proto, path); + if (auto iface = dyn_cast(decl)) + return findExternalPath(iface, path); + return false; + } + + bool findExternalPath(const clang::TagDecl *decl, ExternalPath &path) { + // We can't handle class template specializations right now. + if (isa(decl)) + return false; + + // Named tags are straightforward. + if (auto name = decl->getIdentifier()) { + if (!findExternalPath(decl->getDeclContext(), path)) return false; + path.add(decl->isEnum() ? ExternalPath::Enum : ExternalPath::Record, + getIdentifier(name)); + return true; + } + + // We can handle anonymous tags if they're defined in an obvious + // position in a typedef. + if (auto alias = decl->getTypedefNameForAnonDecl()) { + auto aliasTag = alias->getAnonDeclWithTypedefName(/*any*/true); + if (aliasTag && isSameDecl(decl, aliasTag)) { + if (!findExternalPath(alias, path)) return false; + path.add(ExternalPath::TypedefAnonDecl, Identifier()); + return true; + } + } + + // Otherwise we're stuck. + return false; + } + + bool findExternalPath(const clang::TypedefNameDecl *decl, + ExternalPath &path) { + auto name = decl->getIdentifier(); + if (!name) return false; + if (!findExternalPath(decl->getDeclContext(), path)) return false; + path.add(ExternalPath::Typedef, getIdentifier(name)); + return true; + } + + bool findExternalPath(const clang::ObjCProtocolDecl *decl, + ExternalPath &path) { + auto name = decl->getIdentifier(); + if (!name) return false; + path.add(ExternalPath::ObjCProtocol, getIdentifier(name)); + return true; + } + + bool findExternalPath(const clang::ObjCInterfaceDecl *decl, + ExternalPath &path) { + auto name = decl->getIdentifier(); + if (!name) return false; + path.add(ExternalPath::ObjCInterface, getIdentifier(name)); + return true; + } + + bool findExternalPath(const clang::DeclContext *dc, ExternalPath &path) { + // If we've reached the translation unit, we're done. + if (isa(dc)) + return true; + + // Linkage specifications don't contribute to the path. + if (isa(dc)) + return findExternalPath(dc->getParent(), path); + + // Handle namespaces. + if (auto ns = dyn_cast(dc)) { + // Don't try to handle anonymous namespaces. + auto name = ns->getIdentifier(); + if (!name) return false; + + // Drill to the parent. + if (!findExternalPath(dc->getParent(), path)) return false; + + path.Path.push_back({ExternalPath::Namespace, getIdentifier(name)}); + return true; + } + + // Handle types. + if (auto tag = dyn_cast(dc)) + return findExternalPath(tag, path); + + // Can't handle anything else. + return false; + } +}; +} // end anonymous namespace + + +StableSerializationPath +ClangImporter::findStableSerializationPath(const clang::Decl *decl) const { + return Impl.findStableSerializationPath(decl); +} + +StableSerializationPath +ClangImporter::Implementation::findStableSerializationPath( + const clang::Decl *decl) { + return SerializationPathFinder(*this).find(decl); +} + +const clang::Decl * +ClangImporter::resolveStableSerializationPath( + const StableSerializationPath &path) const { + if (!path) return nullptr; + + if (path.isSwiftDecl()) { + return path.getSwiftDecl()->getClangDecl(); + } + + auto &extpath = path.getExternalPath(); + auto &clangCtx = getClangASTContext(); + + const clang::Decl *decl = nullptr; + + // Perform a lookup in the current context (`decl` if set, and + // otherwise the translation unit). + auto lookup = [&](Identifier name) -> clang::DeclContext::lookup_result { + if (name.empty()) return clang::DeclContext::lookup_result(); + + const clang::DeclContext *dc; + if (decl) { + dc = dyn_cast(decl); + if (!dc) return clang::DeclContext::lookup_result(); + } else { + dc = clangCtx.getTranslationUnitDecl(); + } + + auto ident = &clangCtx.Idents.get(name.str()); + return dc->lookup(ident); + }; + + for (auto step : extpath.Path) { + // Handle the non-lookup steps here. + if (step.first == ExternalPath::TypedefAnonDecl) { + if (auto alias = dyn_cast_or_null(decl)) + return alias->getAnonDeclWithTypedefName(); + return nullptr; + } + + assert(ExternalPath::requiresIdentifier(step.first) && + "should've handled all non-lookup kinds above"); + + const clang::Decl *resultDecl = nullptr; + for (auto lookupDecl : lookup(step.second)) { + auto isAcceptable = [](const clang::Decl *decl, + ExternalPath::ComponentKind kind) { + switch (kind) { + case ExternalPath::Record: + return isa(decl); + case ExternalPath::Enum: + return isa(decl); + case ExternalPath::Namespace: + return isa(decl); + case ExternalPath::Typedef: + return isa(decl); + case ExternalPath::ObjCInterface: + return isa(decl); + case ExternalPath::ObjCProtocol: + return isa(decl); + case ExternalPath::TypedefAnonDecl: + llvm_unreachable("should have been filtered above"); + } + llvm_unreachable("bad kind"); + }; + + // Ignore unacceptable declarations. + if (!isAcceptable(lookupDecl, step.first)) + continue; + + // Bail out if we find multiple matching declarations. + // TODO: make an effort to filter by the target module? + if (resultDecl && !isSameDecl(resultDecl, lookupDecl)) + return nullptr; + + resultDecl = lookupDecl; + } + + // Bail out if lookup found nothing. + if (!resultDecl) return nullptr; + + decl = resultDecl; + } + + return decl; +} + +namespace { + /// The logic here for the supported cases must match the logic in + /// ClangToSwiftBasicWriter in Serialization.cpp. + struct ClangTypeSerializationChecker : + DataStreamBasicWriter { + ClangImporter::Implementation &Impl; + bool IsSerializable = true; + + ClangTypeSerializationChecker(ClangImporter::Implementation &impl) + : Impl(impl) {} + + void writeUInt64(uint64_t value) {} + void writeIdentifier(const clang::IdentifierInfo *ident) {} + void writeStmtRef(const clang::Stmt *stmt) { + if (stmt != nullptr) + IsSerializable = false; + } + void writeDeclRef(const clang::Decl *decl) { + if (decl && !Impl.findStableSerializationPath(decl)) + IsSerializable = false; + } + void writeSourceLocation(clang::SourceLocation loc) { + // If a source location is written into a type, it's likely to be + // something like the location of a VLA which we shouldn't simply + // replace with a meaningless location. + if (loc.isValid()) + IsSerializable = false; + } + }; +} + +bool ClangImporter::isSerializable(const clang::Type *type, + bool checkCanonical) const { + return Impl.isSerializable(clang::QualType(type, 0), checkCanonical); +} + +bool ClangImporter::Implementation::isSerializable(clang::QualType type, + bool checkCanonical) { + if (checkCanonical) + type = getClangASTContext().getCanonicalType(type); + + // Make a pass over the type as if we were serializing it, flagging + // anything that we can't stably serialize. + ClangTypeSerializationChecker checker(*this); + checker.writeQualType(type); + return checker.IsSerializable; +} diff --git a/lib/Sema/TypeCheckAccess.cpp b/lib/Sema/TypeCheckAccess.cpp index bc6e859d74ab7..c2bf7c8a00d39 100644 --- a/lib/Sema/TypeCheckAccess.cpp +++ b/lib/Sema/TypeCheckAccess.cpp @@ -18,6 +18,7 @@ #include "TypeAccessScopeChecker.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Pattern.h" @@ -1534,6 +1535,24 @@ class ExportabilityChecker : public DeclVisitor { visitSubstitutionMap(ty->getSubstitutionMap()); return Action::Continue; } + + // We diagnose unserializable Clang function types in the + // post-visitor so that we diagnose any unexportable component + // types first. + Action walkToTypePost(Type T) override { + if (auto fnType = T->getAs()) { + if (auto clangType = fnType->getClangFunctionType()) { + auto loader = T->getASTContext().getClangModuleLoader(); + // Serialization will serialize the sugared type if it can, + // but we need the canonical type to be serializable or else + // canonicalization (e.g. in SIL) might break things. + if (!loader->isSerializable(clangType, /*check canonical*/ true)) { + diagnoser.diagnoseClangFunctionType(T, clangType); + } + } + } + return TypeDeclFinder::walkToTypePost(T); + } }; type.walk(ProblematicTypeFinder(SF, diagnoser)); @@ -1605,6 +1624,10 @@ class ExportabilityChecker : public DeclVisitor { offendingConformance->getProtocol()->getFullName(), static_cast(reason), M->getName()); } + + void diagnoseClangFunctionType(Type fnType, const clang::Type *type) const { + D->diagnose(diag::unexportable_clang_function_type, fnType); + } }; Diagnoser getDiagnoser(const Decl *D, Reason reason = Reason::General) { diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index 70cb3c8b8de68..3bb2f0bc6fcee 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -190,6 +190,8 @@ OTHER(SELF_PROTOCOL_CONFORMANCE, 251) OTHER(XREF_OPAQUE_RETURN_TYPE_PATH_PIECE, 252) +OTHER(CLANG_TYPE, 253) + #undef RECORD #undef DECLTYPERECORDNODES_HAS_RECORD_VAL #undef RECORD_VAL diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 78d5bed929e30..6f83c62ceacbd 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -30,6 +30,7 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/ClangImporter/SwiftAbstractBasicReader.h" #include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Statistic.h" @@ -227,6 +228,24 @@ getActualDefaultArgKind(uint8_t raw) { return None; } +static Optional +getActualClangDeclPathComponentKind(uint64_t raw) { + switch (static_cast(raw)) { +#define CASE(ID) \ + case serialization::ClangDeclPathComponentKind::ID: \ + return StableSerializationPath::ExternalPath::ID; + CASE(Record) + CASE(Enum) + CASE(Namespace) + CASE(Typedef) + CASE(TypedefAnonDecl) + CASE(ObjCInterface) + CASE(ObjCProtocol) +#undef CASE + } + return None; +} + ParameterList *ModuleFile::readParameterList() { using namespace decls_block; @@ -4784,19 +4803,19 @@ class TypeDeserializer { uint8_t rawRepresentation, rawDiffKind; bool noescape = false, throws; GenericSignature genericSig; - clang::Type *clangFunctionType = nullptr; + TypeID clangTypeID; - // FIXME: [clang-function-type-serialization] Deserialize a clang::Type out - // of the record. if (!isGeneric) { decls_block::FunctionTypeLayout::readRecord( - scratch, resultID, rawRepresentation, noescape, throws, rawDiffKind); + scratch, resultID, rawRepresentation, clangTypeID, + noescape, throws, rawDiffKind); } else { GenericSignatureID rawGenericSig; decls_block::GenericFunctionTypeLayout::readRecord( scratch, resultID, rawRepresentation, throws, rawDiffKind, rawGenericSig); genericSig = MF.getGenericSignature(rawGenericSig); + clangTypeID = 0; } auto representation = getActualFunctionTypeRepresentation(rawRepresentation); @@ -4807,6 +4826,14 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); + const clang::Type *clangFunctionType = nullptr; + if (clangTypeID) { + auto loadedClangType = MF.getClangType(clangTypeID); + if (!loadedClangType) + return loadedClangType.takeError(); + clangFunctionType = loadedClangType.get(); + } + auto info = FunctionType::ExtInfo(*representation, noescape, throws, *diffKind, clangFunctionType); @@ -5144,10 +5171,8 @@ class TypeDeserializer { GenericSignatureID rawGenericSig; SubstitutionMapID rawSubs; ArrayRef variableData; - clang::FunctionType *clangFunctionType = nullptr; + ClangTypeID clangFunctionTypeID; - // FIXME: [clang-function-type-serialization] Deserialize a - // clang::FunctionType out of the record. decls_block::SILFunctionTypeLayout::readRecord(scratch, rawCoroutineKind, rawCalleeConvention, @@ -5162,6 +5187,7 @@ class TypeDeserializer { isGenericSignatureImplied, rawGenericSig, rawSubs, + clangFunctionTypeID, variableData); // Process the ExtInfo. @@ -5174,6 +5200,18 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); + const clang::FunctionType *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(); + } + SILFunctionType::ExtInfo extInfo(*representation, pseudogeneric, noescape, *diffKind, clangFunctionType); @@ -5454,6 +5492,116 @@ Expected TypeDeserializer::getTypeCheckedImpl() { } } +namespace { + +class SwiftToClangBasicReader : + public swift::DataStreamBasicReader { + + ModuleFile &MF; + ClangModuleLoader &ClangLoader; + ArrayRef Record; + +public: + SwiftToClangBasicReader(ModuleFile &MF, ClangModuleLoader &clangLoader, + ArrayRef record) + : DataStreamBasicReader(clangLoader.getClangASTContext()), + MF(MF), ClangLoader(clangLoader), Record(record) {} + + uint64_t readUInt64() { + uint64_t value = Record[0]; + Record = Record.drop_front(); + return value; + } + + Identifier readSwiftIdentifier() { + return MF.getIdentifier(IdentifierID(readUInt64())); + } + + clang::IdentifierInfo *readIdentifier() { + Identifier swiftIdent = readSwiftIdentifier(); + return &getASTContext().Idents.get(swiftIdent.str()); + } + + clang::Stmt *readStmtRef() { + // Should only be allowed with null statements. + return nullptr; + } + + clang::Decl *readDeclRef() { + uint64_t refKind = readUInt64(); + + // Null reference. + if (refKind == 0) return nullptr; + + // Swift declaration. + if (refKind == 1) { + swift::Decl *swiftDecl = MF.getDecl(DeclID(readUInt64())); + return const_cast( + ClangLoader.resolveStableSerializationPath(swiftDecl)); + } + + // External path. + if (refKind == 2) { + using ExternalPath = StableSerializationPath::ExternalPath; + ExternalPath path; + uint64_t length = readUInt64(); + path.Path.reserve(length); + for (uint64_t i = 0; i != length; ++i) { + auto kind = getActualClangDeclPathComponentKind(readUInt64()); + if (!kind) return nullptr; + Identifier name = ExternalPath::requiresIdentifier(*kind) + ? readSwiftIdentifier() + : Identifier(); + path.add(*kind, name); + } + return const_cast( + ClangLoader.resolveStableSerializationPath(std::move(path))); + } + + // Unknown kind? + return nullptr; + } +}; + +} // end anonymous namespace + +llvm::Expected +ModuleFile::getClangType(ClangTypeID TID) { + if (TID == 0) + return nullptr; + + assert(TID <= ClangTypes.size() && "invalid type ID"); + auto &typeOrOffset = ClangTypes[TID-1]; + + if (typeOrOffset.isComplete()) + return typeOrOffset; + + BCOffsetRAII restoreOffset(DeclTypeCursor); + fatalIfNotSuccess(DeclTypeCursor.JumpToBit(typeOrOffset)); + + llvm::BitstreamEntry entry = + fatalIfUnexpected(DeclTypeCursor.advance()); + + if (entry.Kind != llvm::BitstreamEntry::Record) { + fatal(); + } + + SmallVector scratch; + StringRef blobData; + unsigned recordID = fatalIfUnexpected( + DeclTypeCursor.readRecord(entry.ID, scratch, &blobData)); + + if (recordID != decls_block::CLANG_TYPE) + fatal(); + + auto &clangLoader = *getContext().getClangModuleLoader(); + auto clangType = + SwiftToClangBasicReader(*this, clangLoader, scratch).readTypeRef() + .getTypePtr(); + typeOrOffset = clangType; + return clangType; +} + Decl *handleErrorAndSupplyMissingClassMember(ASTContext &context, llvm::Error &&error, ClassDecl *containingClass) { diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 510a62db0c63b..c73d573629f08 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -986,6 +986,10 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) { assert(blobData.empty()); allocateBuffer(Types, scratch); break; + case index_block::CLANG_TYPE_OFFSETS: + assert(blobData.empty()); + allocateBuffer(ClangTypes, scratch); + break; case index_block::IDENTIFIER_OFFSETS: assert(blobData.empty()); allocateBuffer(Identifiers, scratch); diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 2ae1a88a3da06..0d45cfb47a65f 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -23,6 +23,7 @@ #include "swift/AST/TypeLoc.h" #include "swift/Serialization/Validation.h" #include "swift/Basic/LLVM.h" +#include "clang/AST/Type.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/TinyPtrVector.h" @@ -318,6 +319,9 @@ class ModuleFile /// Types referenced by this module. MutableArrayRef> Types; + /// Clang types referenced by this module. + MutableArrayRef> ClangTypes; + /// Generic signatures referenced by this module. MutableArrayRef> GenericSignatures; @@ -879,6 +883,10 @@ class ModuleFile /// Returns the type with the given ID, deserializing it if needed. llvm::Expected getTypeChecked(serialization::TypeID TID); + /// Returns the Clang type with the given ID, deserializing it if needed. + llvm::Expected + getClangType(serialization::ClangTypeID TID); + /// Returns the base name with the given ID, deserializing it if needed. DeclBaseName getDeclBaseName(serialization::IdentifierID IID); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index c2769a9c5ef97..f79836e2b9ba1 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 = 535; // top-level var decls +const uint16_t SWIFTMODULE_VERSION_MINOR = 536; // Clang function types /// A standard hash seed used for all string hashes in a serialized module. /// @@ -71,6 +71,10 @@ using TypeIDField = DeclIDField; using TypeIDWithBitField = BCFixed<32>; +// ClangTypeID must be the same as DeclID because it is stored in the same way. +using ClangTypeID = TypeID; +using ClangTypeIDField = TypeIDField; + // IdentifierID must be the same as DeclID because it is stored in the same way. using IdentifierID = DeclID; using IdentifierIDField = DeclIDField; @@ -549,6 +553,18 @@ enum class ImportControl : uint8_t { }; using ImportControlField = BCFixed<2>; +// These IDs must \em not be renumbered or reordered without incrementing +// the module version. +enum class ClangDeclPathComponentKind : uint8_t { + Record = 0, + Enum, + Namespace, + Typedef, + TypedefAnonDecl, + ObjCInterface, + ObjCProtocol, +}; + // Encodes a VersionTuple: // // Major @@ -852,6 +868,11 @@ namespace decls_block { #include "DeclTypeRecordNodes.def" }; + using ClangTypeLayout = BCRecordLayout< + CLANG_TYPE, + BCArray> + >; + using BuiltinAliasTypeLayout = BCRecordLayout< BUILTIN_ALIAS_TYPE, DeclIDField, // typealias decl @@ -903,6 +924,7 @@ namespace decls_block { FUNCTION_TYPE, TypeIDField, // output FunctionTypeRepresentationField, // representation + ClangTypeIDField, // type BCFixed<1>, // noescape? BCFixed<1>, // throws? DifferentiabilityKindField // differentiability kind @@ -1001,6 +1023,7 @@ namespace decls_block { BCFixed<1>, // generic signature implied GenericSignatureIDField, // generic signature SubstitutionMapIDField, // substitutions + ClangTypeIDField, // clang function type, for foreign conventions BCArray // parameter types/conventions, alternating // followed by result types/conventions, alternating // followed by error result type/convention @@ -1914,7 +1937,8 @@ namespace index_block { ORDERED_TOP_LEVEL_DECLS, SUBSTITUTION_MAP_OFFSETS, - LastRecordKind = SUBSTITUTION_MAP_OFFSETS, + CLANG_TYPE_OFFSETS, + LastRecordKind = CLANG_TYPE_OFFSETS, }; constexpr const unsigned RecordIDFieldWidth = 5; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 18abc58342f53..9dcf19486acf9 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -41,6 +41,7 @@ #include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" +#include "swift/ClangImporter/SwiftAbstractBasicWriter.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Serialization/SerializationOptions.h" #include "swift/Strings.h" @@ -69,6 +70,10 @@ using namespace llvm::support; using swift::version::Version; using llvm::BCBlockRAII; +ASTContext &SerializerBase::getASTContext() { + return M->getASTContext(); +} + /// Used for static_assert. static constexpr bool declIDFitsIn32Bits() { using Int32Info = std::numeric_limits; @@ -585,6 +590,29 @@ serialization::TypeID Serializer::addTypeRef(Type ty) { return TypesToSerialize.addRef(ty); } +serialization::ClangTypeID Serializer::addClangTypeRef(const clang::Type *ty) { + if (!ty) return 0; + + // Try to serialize the non-canonical type, but fall back to the + // canonical type if necessary. + auto loader = getASTContext().getClangModuleLoader(); + bool isSerializable; + if (loader->isSerializable(ty, false)) { + isSerializable = true; + } else if (!ty->isCanonicalUnqualified()) { + ty = ty->getCanonicalTypeInternal().getTypePtr(); + isSerializable = loader->isSerializable(ty, false); + } else { + isSerializable = false; + } + if (!isSerializable) { + PrettyStackTraceClangType trace("staging a serialized reference to", ty); + llvm::report_fatal_error("Clang function type is not serializable"); + } + + return ClangTypesToSerialize.addRef(ty); +} + IdentifierID Serializer::addDeclBaseNameRef(DeclBaseName ident) { switch (ident.getKind()) { case DeclBaseName::Kind::Normal: { @@ -745,6 +773,7 @@ void Serializer::writeBlockInfoBlock() { BLOCK_RECORD(index_block, LOCAL_DECL_CONTEXT_OFFSETS); BLOCK_RECORD(index_block, GENERIC_SIGNATURE_OFFSETS); BLOCK_RECORD(index_block, SUBSTITUTION_MAP_OFFSETS); + BLOCK_RECORD(index_block, CLANG_TYPE_OFFSETS); BLOCK_RECORD(index_block, LOCAL_TYPE_DECLS); BLOCK_RECORD(index_block, NORMAL_CONFORMANCE_OFFSETS); BLOCK_RECORD(index_block, SIL_LAYOUT_OFFSETS); @@ -1517,6 +1546,25 @@ getStableCtorInitializerKind(swift::CtorInitializerKind K){ llvm_unreachable("Unhandled CtorInitializerKind in switch."); } +static serialization::ClangDeclPathComponentKind +getStableClangDeclPathComponentKind( + StableSerializationPath::ExternalPath::ComponentKind kind) { + switch (kind) { +#define CASE(ID) \ + case StableSerializationPath::ExternalPath::ID: \ + return serialization::ClangDeclPathComponentKind::ID; + CASE(Record) + CASE(Enum) + CASE(Namespace) + CASE(Typedef) + CASE(TypedefAnonDecl) + CASE(ObjCInterface) + CASE(ObjCProtocol) +#undef CASE + } + llvm_unreachable("bad kind"); +} + void Serializer::writeCrossReference(const DeclContext *DC, uint32_t pathLen) { using namespace decls_block; @@ -4032,11 +4080,14 @@ class Serializer::TypeSerializer : public TypeVisitor { void visitFunctionType(const FunctionType *fnTy) { using namespace decls_block; - // FIXME: [clang-function-type-serialization] Serialize the clang type here + auto resultType = S.addTypeRef(fnTy->getResult()); + auto clangType = S.addClangTypeRef(fnTy->getClangFunctionType()); + unsigned abbrCode = S.DeclTypeAbbrCodes[FunctionTypeLayout::Code]; FunctionTypeLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, - S.addTypeRef(fnTy->getResult()), + resultType, getRawStableFunctionTypeRepresentation(fnTy->getRepresentation()), + clangType, fnTy->isNoEscape(), fnTy->throws(), getRawStableDifferentiabilityKind(fnTy->getDifferentiabilityKind())); @@ -4108,7 +4159,9 @@ class Serializer::TypeSerializer : public TypeVisitor { variableData.push_back(TypeID(conv)); } - auto sig = fnTy->getSubstGenericSignature(); + auto sigID = S.addGenericSignatureRef(fnTy->getSubstGenericSignature()); + auto substMapID = S.addSubstitutionMapRef(fnTy->getSubstitutions()); + auto clangTypeID = S.addClangTypeRef(fnTy->getClangFunctionType()); auto stableCoroutineKind = getRawStableSILCoroutineKind(fnTy->getCoroutineKind()); @@ -4127,9 +4180,7 @@ class Serializer::TypeSerializer : public TypeVisitor { stableDiffKind, fnTy->hasErrorResult(), fnTy->getParameters().size(), fnTy->getNumYields(), fnTy->getNumResults(), fnTy->isGenericSignatureImplied(), - S.addGenericSignatureRef(sig), - S.addSubstitutionMapRef(fnTy->getSubstitutions()), - variableData); + sigID, substMapID, clangTypeID, variableData); if (auto conformance = fnTy->getWitnessMethodConformanceOrInvalid()) S.writeConformance(conformance, S.DeclTypeAbbrCodes); @@ -4221,6 +4272,96 @@ void Serializer::writeASTBlockEntity(Type ty) { TypeSerializer(*this).visit(ty); } +namespace { +class ClangToSwiftBasicWriter : + public swift::DataStreamBasicWriter { + + Serializer &S; + SmallVectorImpl &Record; + using TypeWriter = + clang::serialization::AbstractTypeWriter; + TypeWriter Types; + + ClangModuleLoader *getClangLoader() { + return S.getASTContext().getClangModuleLoader(); + } + +public: + ClangToSwiftBasicWriter(Serializer &S, SmallVectorImpl &record) + : S(S), Record(record), Types(*this) {} + + void writeUInt64(uint64_t value) { + Record.push_back(value); + } + + void writeIdentifier(const clang::IdentifierInfo *value) { + IdentifierID id = 0; + if (value) { + id = S.addDeclBaseNameRef( + S.getASTContext().getIdentifier(value->getName())); + } + Record.push_back(id); + } + + void writeStmtRef(const clang::Stmt *stmt) { + // The deserializer should always read null, and isSerializable + // should be checking that we don't see a non-null statement here. + if (stmt) { + llvm::report_fatal_error("serializing a non-null Clang statement or" + " expression reference"); + } + } + + void writeDeclRef(const clang::Decl *decl) { + if (!decl) { + Record.push_back(/*no declaration*/ 0); + return; + } + + auto path = getClangLoader()->findStableSerializationPath(decl); + if (!path) { + decl->dump(llvm::errs()); + llvm::report_fatal_error("failed to find a stable Swift serialization" + " path for the above Clang declaration"); + } + + if (path.isSwiftDecl()) { + Record.push_back(/*swift declaration*/ 1); + Record.push_back(S.addDeclRef(path.getSwiftDecl())); + return; + } + + assert(path.isExternalPath()); + auto &ext = path.getExternalPath(); + Record.push_back(/*external path*/ 2); + Record.push_back(ext.Path.size()); + for (auto &elt : ext.Path) { + auto kind = elt.first; + auto stableKind = unsigned(getStableClangDeclPathComponentKind(kind)); + Record.push_back(stableKind); + if (ext.requiresIdentifier(kind)) + Record.push_back(S.addDeclBaseNameRef(elt.second)); + } + } +}; + +} + +void Serializer::writeASTBlockEntity(const clang::Type *ty) { + using namespace decls_block; + PrettyStackTraceClangType traceRAII("serializing clang type", ty); + assert(ClangTypesToSerialize.hasRef(ty)); + + // Serialize the type as an opaque sequence of data. + SmallVector typeData; + ClangToSwiftBasicWriter(*this, typeData).writeTypeRef(ty); + + // Write that in an opaque record. + unsigned abbrCode = DeclTypeAbbrCodes[ClangTypeLayout::Code]; + ClangTypeLayout::emitRecord(Out, ScratchRecord, abbrCode, + typeData); +} + template bool Serializer::writeASTBlockEntitiesIfNeeded( SpecificASTBlockRecordKeeper &entities) { @@ -4263,6 +4404,8 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); registerDeclTypeAbbr(); @@ -4347,6 +4490,7 @@ void Serializer::writeAllDeclsAndTypes() { wroteSomething |= writeASTBlockEntitiesIfNeeded(DeclsToSerialize); wroteSomething |= writeASTBlockEntitiesIfNeeded(TypesToSerialize); + wroteSomething |= writeASTBlockEntitiesIfNeeded(ClangTypesToSerialize); wroteSomething |= writeASTBlockEntitiesIfNeeded(LocalDeclContextsToSerialize); wroteSomething |= @@ -4804,6 +4948,7 @@ void Serializer::writeAST(ModuleOrSourceFile DC, index_block::OffsetsLayout Offsets(Out); writeOffsets(Offsets, DeclsToSerialize); writeOffsets(Offsets, TypesToSerialize); + writeOffsets(Offsets, ClangTypesToSerialize); writeOffsets(Offsets, LocalDeclContextsToSerialize); writeOffsets(Offsets, GenericSignaturesToSerialize); writeOffsets(Offsets, SubstitutionMapsToSerialize); diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index 3e66e0ae4877a..a446d34e0b3c7 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -27,6 +27,10 @@ #include #include +namespace clang { + class Type; +} + namespace swift { class SILModule; @@ -63,6 +67,8 @@ class SerializerBase { public: SerializerBase(ArrayRef signature, ModuleOrSourceFile DC); + + ASTContext &getASTContext(); }; class Serializer : public SerializerBase { @@ -193,6 +199,10 @@ class Serializer : public SerializerBase { index_block::TYPE_OFFSETS> TypesToSerialize; + ASTBlockRecordKeeper + ClangTypesToSerialize; + ASTBlockRecordKeeper LocalDeclContextsToSerialize; @@ -313,6 +323,9 @@ class Serializer : public SerializerBase { /// Writes the given type. void writeASTBlockEntity(Type ty); + /// Writes the given Clang type. + void writeASTBlockEntity(const clang::Type *ty); + /// Writes a generic signature. void writeASTBlockEntity(GenericSignature sig); @@ -376,6 +389,13 @@ class Serializer : public SerializerBase { /// \returns The ID for the given Type in this module. TypeID addTypeRef(Type ty); + /// Records the use of the given C type. + /// + /// The type will be scheduled for serialization if necessary., + /// + /// \returns The ID for the given type in this module. + ClangTypeID addClangTypeRef(const clang::Type *ty); + /// Records the use of the given DeclBaseName. /// /// The Identifier will be scheduled for serialization if necessary. diff --git a/test/ClangImporter/unserializable-clang-function-types.swift b/test/ClangImporter/unserializable-clang-function-types.swift new file mode 100644 index 0000000000000..7332e437876cd --- /dev/null +++ b/test/ClangImporter/unserializable-clang-function-types.swift @@ -0,0 +1,6 @@ +// RUN: %target-swift-frontend -typecheck -swift-version 5 -emit-module-interface-path - -sdk %clang-importer-sdk -enable-library-evolution %s -experimental-print-full-convention -verify + +import ctypes + +public var f1 : UnserializableFunctionPointer? +// expected-error@-1 {{cannot export the underlying C type of the function type 'UnserializableFunctionPointer' (aka '@convention(c) () -> Optional'); it may use anonymous types or types defined outside of a module}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h index 4d4a7cf018575..5898fd4bbb9c4 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ctypes.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ctypes.h @@ -224,6 +224,17 @@ typedef struct Dummy { Dummy * (*getFunctionPointer3(void))(Dummy *); +// These two function types should be serializable despite the struct +// declarations being incomplete and therefore (currently) unimportable. +typedef struct ForwardInTypedefForFP *OpaqueTypedefForFP; +typedef OpaqueTypedefForFP (*FunctionPointerReturningOpaqueTypedef)(void); + +typedef struct ForwardInTypedefForFP2 *OpaqueTypedefForFP2; +typedef OpaqueTypedefForFP2 (*FunctionPointerReturningOpaqueTypedef2)(void); + +// This will probably never be serializable. +typedef struct { int x; int y; } *(*UnserializableFunctionPointer)(void); + //===--- // Unions //===--- diff --git a/test/Sema/clang_types_in_ast.swift b/test/Sema/clang_types_in_ast.swift index f5bcbf6f37cfc..e087c0e66151a 100644 --- a/test/Sema/clang_types_in_ast.swift +++ b/test/Sema/clang_types_in_ast.swift @@ -8,9 +8,7 @@ // rdar://problem/57644243 : We shouldn't crash if -use-clang-function-types is not enabled. // RUN: %target-swift-frontend %s -typecheck -DNOCRASH3 -I %t -// FIXME: [clang-function-type-serialization] This should stop crashing once we -// start serializing clang function types. -// RUN: not --crash %target-swift-frontend %s -typecheck -DCRASH -I %t -use-clang-function-types +// RUN: %target-swift-frontend %s -typecheck -DCRASH -I %t -use-clang-function-types #if NOCRASH1 public func my_signal() -> Optional<@convention(c) (Int32) -> Void> { diff --git a/test/Serialization/Inputs/def_clang_function_types.swift b/test/Serialization/Inputs/def_clang_function_types.swift new file mode 100644 index 0000000000000..b5d28f25c0f8f --- /dev/null +++ b/test/Serialization/Inputs/def_clang_function_types.swift @@ -0,0 +1,15 @@ +import ctypes + +public var has_fp_type: FunctionPointerReturningOpaqueTypedef? + +public func use_inout(arg: inout T) {} + +@inlinable +public func use_fp_internally() { + // This currently bypasses the safety checks we do in export-checking + // because we don't check inlinable function bodies. It's plausible + // code, but more importantly it actually appears in the non-Darwin + // Foundation code. + var x: FunctionPointerReturningOpaqueTypedef2? = nil + use_inout(arg: &x) +} diff --git a/test/Serialization/clang-function-types.swift b/test/Serialization/clang-function-types.swift new file mode 100644 index 0000000000000..d85eadf358fe7 --- /dev/null +++ b/test/Serialization/clang-function-types.swift @@ -0,0 +1,30 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %S/Inputs/def_clang_function_types.swift -use-clang-function-types +// RUN: llvm-bcanalyzer %t/def_clang_function_types.swiftmodule | %FileCheck -check-prefix=CHECK-BCANALYZER %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -use-clang-function-types -experimental-print-full-convention -emit-sil -sil-debug-serialization -I %t %s -O | %FileCheck %s + +import def_clang_function_types + +// CHECK-BCANALYZER-LABEL: (INDEX_BLOCK): +// CHECK-BCANALYZER: CLANG_TYPE_OFFSETS + +// CHECK-LABEL: sil hidden @$s4main5test1yyF +func test1() { + // FIXME: this mangling will have to change + // CHECK: global_addr @$s24def_clang_function_types11has_fp_types13OpaquePointerVSgyXCSgvp : $*Optional<@convention(c) () -> Optional> + let fp = has_fp_type + _ = fp?() +} +// CHECK-LABEL: } // end sil function '$s4main5test1yyF' + +// CHECK-LABEL: sil hidden @$s4main5test2yyF +func test2() { + use_fp_internally() +} +// CHECK-LABEL: } // end sil function '$s4main5test2yyF' + +// CHECK-LABEL: sil public_external [canonical] @$s24def_clang_function_types17use_fp_internallyyyF +// CHECK: enum $Optional<@convention(c) () -> Optional>, #Optional.none!enumelt +// CHECK: [[FN:%.*]] = function_ref @$s24def_clang_function_types9use_inout3argyxz_tlF : $@convention(thin) <τ_0_0> (@inout τ_0_0) -> () +// CHECK: apply [[FN]]<(@convention(c) () -> OpaquePointer?)?> +// CHECK-LABEL: } // end sil function '$s24def_clang_function_types17use_fp_internallyyyF' From 9b93a08280722564c1ca5336862ca596221a9ea4 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Fri, 7 Feb 2020 08:20:24 +0000 Subject: [PATCH 202/237] [NFC] Add SE-0249 to changelog (#29686) --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c93a24650a34..84f1c2ecf328a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -193,6 +193,23 @@ Swift 5.2 * `mutating func callAsFunction` is supported. * `func callAsFunction` works with `throws` and `rethrows`. * `func callAsFunction` works with trailing closures. + +* [SE-0249][]: + + A `\Root.value` key path expression is now allowed wherever a `(Root) -> Value` + function is allowed. Such an expression is implicitly converted to a key path + application of `{ $0[keyPath: \Root.value] }`. + + For example: + + ```swift + struct User { + let email: String + let isAdmin: Bool + } + + users.map(\.email) // this is equivalent to: users.map { $0[keyPath: \User.email] } + ``` * [SR-4206][]: @@ -7880,6 +7897,7 @@ Swift 1.0 [SE-0242]: [SE-0244]: [SE-0245]: +[SE-0249]: [SE-0252]: [SE-0253]: [SE-0254]: From a54fbab8fdd12e06e0910d4f6196f3172354874d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 7 Feb 2020 00:26:22 -0800 Subject: [PATCH 203/237] [ConstraintSystem] Favor resolving closures over any disjunction binding type variables Fix a case where favoring didn't account for "bound" type variable being wrapped into optional(s) (disjunctions like that are used for optional conversions). Doing so makes sure that closure result type is not bound before the body of the closure is "opened", that's important because body could provide additional context required to bind result type e.g. `{ $0 as? }`. Resolve: rdar://problem/59208419 --- lib/Sema/CSBindings.cpp | 9 ++++++++- test/Constraints/closures.swift | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 707d57504f251..0153ccc3f9b2c 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -246,7 +246,14 @@ bool ConstraintSystem::PotentialBindings::favoredOverDisjunction( // if it's something like a collection (where it has to pick // between a conversion and bridging conversion) or concrete // type let's prefer the disjunction. - return boundType->is(); + // + // We are looking through optionals here because it could be + // a situation where disjunction is formed to match optionals + // either as deep equality or optional-to-optional conversion. + // Such type variables might be connected to closure as well + // e.g. when result type is optional, so it makes sense to + // open closure before attempting such disjunction. + return boundType->lookThroughAllOptionalTypes()->is(); } return !InvolvesTypeVariables; diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 3699073f692a6..42299524f07ed 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -953,3 +953,21 @@ class Foo { func test_explicit_variadic_is_interpreted_correctly() { _ = { (T: String...) -> String in T[0] + "" } // Ok } + +// rdar://problem/59208419 - closure result type is incorrectly inferred to be a supertype +func test_correct_inference_of_closure_result_in_presence_of_optionals() { + class A {} + class B : A {} + + func foo(_: B) -> Int? { return 42 } + + func bar(_: (A) -> T?) -> T? { + return .none + } + + guard let v = bar({ $0 as? B }), + let _ = foo(v) // Ok, v is inferred as `B` + else { + return; + } +} From 08225c047289f8f37e0ffd48b4e6c5d80a2c6910 Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Fri, 7 Feb 2020 00:58:30 -0800 Subject: [PATCH 204/237] [Python] Added a new python_format.py script to the utils directory we can use to automatically check the formatting for all the Python sources in the project. --- utils/python_format.py | 145 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 utils/python_format.py diff --git a/utils/python_format.py b/utils/python_format.py new file mode 100644 index 0000000000000..3b46d245d54d2 --- /dev/null +++ b/utils/python_format.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 + +# 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 + + +""" +Utility script used to run the black code formatter over all the Python scripts in the +project sources. +""" + + +import argparse +import os +import subprocess +import sys +from pathlib import Path + + +_SWIFT_PATH = Path(__file__).resolve().parents[1] + +_KNOWN_SCRIPT_PATHS = [ + _SWIFT_PATH / "benchmark/scripts/Benchmark_Driver", + _SWIFT_PATH / "benchmark/scripts/Benchmark_DTrace.in", + _SWIFT_PATH / "benchmark/scripts/Benchmark_GuardMalloc.in", + _SWIFT_PATH / "benchmark/scripts/Benchmark_RuntimeLeaksRunner.in", + _SWIFT_PATH / "docs/scripts/ns-html2rst", + _SWIFT_PATH / "test/Driver/Inputs/fake-toolchain/ld", + _SWIFT_PATH / "utils/80+-check", + _SWIFT_PATH / "utils/backtrace-check", + _SWIFT_PATH / "utils/build-parser-lib", + _SWIFT_PATH / "utils/build-script", + _SWIFT_PATH / "utils/check-incremental", + _SWIFT_PATH / "utils/coverage/coverage-build-db", + _SWIFT_PATH / "utils/coverage/coverage-generate-data", + _SWIFT_PATH / "utils/coverage/coverage-query-db", + _SWIFT_PATH / "utils/coverage/coverage-touch-tests", + _SWIFT_PATH / "utils/dev-scripts/blockifyasm", + _SWIFT_PATH / "utils/dev-scripts/split-cmdline", + _SWIFT_PATH / "utils/gyb", + _SWIFT_PATH / "utils/line-directive", + _SWIFT_PATH / "utils/PathSanitizingFileCheck", + _SWIFT_PATH / "utils/recursive-lipo", + _SWIFT_PATH / "utils/round-trip-syntax-test", + _SWIFT_PATH / "utils/rth", + _SWIFT_PATH / "utils/run-test", + _SWIFT_PATH / "utils/scale-test", + _SWIFT_PATH / "utils/submit-benchmark-results", + _SWIFT_PATH / "utils/swift_build_support/tests/mock-distcc", + _SWIFT_PATH / "utils/symbolicate-linux-fatal", + _SWIFT_PATH / "utils/update-checkout", + _SWIFT_PATH / "utils/viewcfg", +] + + +_INSTALL_BLACK_MESSAGE = """\ +The black Python package is required for formatting, but it was not found on +your system. + +You can install it using: + + python3 -m pip install black + +For more help, see https://black.readthedocs.io. +""" + + +def _get_python_sources(): + """Returns a list of path objects for all known Python sources in the Swift + project. + """ + + return list(_SWIFT_PATH.rglob("*.py")) + _KNOWN_SCRIPT_PATHS + + +def _is_package_installed(name): + """Runs the pip command to check if a package is installed. + """ + + command = [ + sys.executable, + "-m", + "pip", + "show", + "--quiet", + name, + ] + + with open(os.devnull, "w") as devnull: + status = subprocess.call(command, stderr=devnull) + + return not status + + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument( + "--check", + action="store_true", + help="Don't format the file, just retun the status.", + ) + + parser.add_argument( + "-v", + "--verbose", + action="store_true", + help="Also emit messages to stderr about files that were not changed", + ) + + return parser.parse_args() + + +def main(): + args = parse_args() + + if not _is_package_installed("black"): + print(_INSTALL_BLACK_MESSAGE) + return 1 + + command = [ + sys.executable, + "-m", + "black", + "--target-version", + "py27", + ] + + if args.check: + command.append("--check") + if args.verbose: + command.append("--verbose") + + command += [str(path) for path in _get_python_sources()] + + return subprocess.call(command) + + +if __name__ == "__main__": + sys.exit(main()) From 0e8f4d4f32f5128f8b0032ad33d34db0c8123278 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Fri, 7 Feb 2020 12:18:39 +0000 Subject: [PATCH 205/237] [SourceKit] Fix cursorinfo.modulename with sourceinfo --- test/SourceKit/CursorInfo/use-swift-source-info.swift | 8 ++++---- tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/SourceKit/CursorInfo/use-swift-source-info.swift b/test/SourceKit/CursorInfo/use-swift-source-info.swift index 8b50f38ccb200..7f905f4cf52ca 100644 --- a/test/SourceKit/CursorInfo/use-swift-source-info.swift +++ b/test/SourceKit/CursorInfo/use-swift-source-info.swift @@ -23,7 +23,7 @@ func bar() { // WITH: source.lang.swift.ref.function.free ({{.*}}/Foo.swift:2:13-2:16) // WITHOUT: source.lang.swift.ref.function.free () // BOTH: foo() -// BOTH: s:3Foo3fooyyF -// BOTH: () -> () -// BOTH: $syycD -// BOTH: Foo +// BOTH-NEXT: s:3Foo3fooyyF +// BOTH-NEXT: () -> () +// BOTH-NEXT: $syycD +// BOTH-NEXT: Foo diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 92db039a37e85..2f3e938fefc54 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -915,7 +915,7 @@ static bool passCursorInfoForDecl(SourceFile* SF, auto ClangMod = Importer->getClangOwningModule(ClangNode); if (ClangMod) ModuleName = ClangMod->getFullModuleName(); - } else if (VD->getLoc().isInvalid() && VD->getModuleContext() != MainModule) { + } else if (VD->getModuleContext() != MainModule) { ModuleName = VD->getModuleContext()->getName().str(); } StringRef ModuleInterfaceName; From 7b064a6500122495d0d4c9c44c49d20401bce842 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Fri, 7 Feb 2020 09:35:16 -0800 Subject: [PATCH 206/237] [AutoDiff upstream] Add SIL differentiability witness serialization. (#29642) SIL differentiability witnesses are a new top-level SIL construct mapping an "original" SIL function and derivative configuration to derivative SIL functions. This patch adds `SILDifferentiabilityWitness` serialization/deserialization. Resolves TF-1136. --- include/swift/SIL/SILModule.h | 4 + .../swift/Serialization/SerializedSILLoader.h | 3 + lib/SIL/SILDifferentiabilityWitness.cpp | 3 - lib/SIL/SILModule.cpp | 11 +- lib/Serialization/DeserializeSIL.cpp | 167 +++++++++++++++++- lib/Serialization/DeserializeSIL.h | 17 ++ lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SILFormat.h | 17 ++ lib/Serialization/SerializeSIL.cpp | 89 +++++++++- lib/Serialization/SerializedSILLoader.cpp | 16 ++ .../sil_differentiability_witness.sil | 15 +- 11 files changed, 331 insertions(+), 13 deletions(-) rename test/AutoDiff/SIL/{Parse => Serialization}/sil_differentiability_witness.sil (91%) diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index f9e02445794f7..285a013fcbdc8 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -637,6 +637,10 @@ class SILModule { llvm::ArrayRef lookUpDifferentiabilityWitnessesForFunction(StringRef name); + /// Attempt to deserialize the SILDifferentiabilityWitness. Returns true if + /// deserialization succeeded, false otherwise. + bool loadDifferentiabilityWitness(SILDifferentiabilityWitness *dw); + // Given a protocol, attempt to create a default witness table declaration // for it. SILDefaultWitnessTable * diff --git a/include/swift/Serialization/SerializedSILLoader.h b/include/swift/Serialization/SerializedSILLoader.h index fd65af939be7d..8afe8fe5f7966 100644 --- a/include/swift/Serialization/SerializedSILLoader.h +++ b/include/swift/Serialization/SerializedSILLoader.h @@ -32,6 +32,7 @@ class SILModule; class SILVTable; class SILWitnessTable; class SILDefaultWitnessTable; +class SILDifferentiabilityWitness; /// Maintains a list of SILDeserializer, one for each serialized modules /// in ASTContext. It provides lookupSILFunction that will perform lookup @@ -64,6 +65,8 @@ class SerializedSILLoader { SILVTable *lookupVTable(const ClassDecl *C); SILWitnessTable *lookupWitnessTable(SILWitnessTable *C); SILDefaultWitnessTable *lookupDefaultWitnessTable(SILDefaultWitnessTable *C); + SILDifferentiabilityWitness * + lookupDifferentiabilityWitness(SILDifferentiabilityWitnessKey key); /// Invalidate the cached entries for deserialized SILFunctions. void invalidateCaches(); diff --git a/lib/SIL/SILDifferentiabilityWitness.cpp b/lib/SIL/SILDifferentiabilityWitness.cpp index e4b8a3f63928d..c3f6df4ccb11d 100644 --- a/lib/SIL/SILDifferentiabilityWitness.cpp +++ b/lib/SIL/SILDifferentiabilityWitness.cpp @@ -12,9 +12,7 @@ #define DEBUG_TYPE "sil-differentiability-witness" -// SWIFT_ENABLE_TENSORFLOW #include "swift/AST/ASTMangler.h" -// SWIFT_ENABLE_TENSORFLOW_END #include "swift/SIL/SILDifferentiabilityWitness.h" #include "swift/SIL/SILModule.h" @@ -51,7 +49,6 @@ SILDifferentiabilityWitness *SILDifferentiabilityWitness::createDefinition( derivativeGenSig, jvp, vjp, /*isDeclaration*/ false, isSerialized, attribute); // Register the differentiability witness in the module. - // Register the differentiability witness in the module. Mangle::ASTMangler mangler; auto mangledKey = mangler.mangleSILDifferentiabilityWitnessKey(diffWitness->getKey()); diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp index cdd7614d82d6a..3e56e838b2b08 100644 --- a/lib/SIL/SILModule.cpp +++ b/lib/SIL/SILModule.cpp @@ -580,9 +580,8 @@ lookUpFunctionInVTable(ClassDecl *Class, SILDeclRef Member) { SILDifferentiabilityWitness * SILModule::lookUpDifferentiabilityWitness(StringRef name) { auto it = DifferentiabilityWitnessMap.find(name); - if (it != DifferentiabilityWitnessMap.end()) { + if (it != DifferentiabilityWitnessMap.end()) return it->second; - } return nullptr; } @@ -599,6 +598,14 @@ SILModule::lookUpDifferentiabilityWitnessesForFunction(StringRef name) { return DifferentiabilityWitnessesByFunction[name]; } +bool SILModule::loadDifferentiabilityWitness(SILDifferentiabilityWitness *dw) { + auto *newDW = getSILLoader()->lookupDifferentiabilityWitness(dw->getKey()); + if (!newDW) + return false; + assert(dw == newDW); + return true; +} + void SILModule::registerDeserializationNotificationHandler( std::unique_ptr &&handler) { deserializationNotificationHandlers.add(std::move(handler)); diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index dff773eb2394e..b30ff8fe71e02 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -165,9 +165,11 @@ SILDeserializer::SILDeserializer( kind == sil_index_block::SIL_GLOBALVAR_NAMES || kind == sil_index_block::SIL_WITNESS_TABLE_NAMES || kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES || - kind == sil_index_block::SIL_PROPERTY_OFFSETS)) && + kind == sil_index_block::SIL_PROPERTY_OFFSETS || + kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES)) && "Expect SIL_FUNC_NAMES, SIL_VTABLE_NAMES, SIL_GLOBALVAR_NAMES, \ - SIL_WITNESS_TABLE_NAMES, or SIL_DEFAULT_WITNESS_TABLE_NAMES."); + SIL_WITNESS_TABLE_NAMES, SIL_DEFAULT_WITNESS_TABLE_NAMES, \ + SIL_PROPERTY_OFFSETS, or SIL_DIFFERENTIABILITY_WITNESS_NAMES."); (void)prevKind; if (kind == sil_index_block::SIL_FUNC_NAMES) @@ -180,6 +182,8 @@ SILDeserializer::SILDeserializer( WitnessTableList = readFuncTable(scratch, blobData); else if (kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES) DefaultWitnessTableList = readFuncTable(scratch, blobData); + else if (kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES) + DifferentiabilityWitnessList = readFuncTable(scratch, blobData); else if (kind == sil_index_block::SIL_PROPERTY_OFFSETS) { // No matching 'names' block for property descriptors needed yet. MF->allocateBuffer(Properties, scratch); @@ -217,6 +221,12 @@ SILDeserializer::SILDeserializer( offKind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_OFFSETS) && "Expect a SIL_DEFAULT_WITNESS_TABLE_OFFSETS record."); MF->allocateBuffer(DefaultWitnessTables, scratch); + } else if (kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES) { + assert((next.Kind == llvm::BitstreamEntry::Record && + offKind == + sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_OFFSETS) && + "Expect a SIL_DIFFERENTIABILITY_WITNESS_OFFSETS record."); + MF->allocateBuffer(DifferentiabilityWitnesses, scratch); } } } @@ -339,6 +349,24 @@ SILType SILDeserializer::getSILType(Type Ty, SILValueCategory Category, .getCategoryType(Category); } +/// Helper function to find a SILDifferentiabilityWitness, given its mangled +/// key. +SILDifferentiabilityWitness * +SILDeserializer::getSILDifferentiabilityWitnessForReference( + StringRef mangledKey) { + // Check to see if we have a witness under this key already. + auto *witness = SILMod.lookUpDifferentiabilityWitness(mangledKey); + if (witness) + return witness; + // Otherwise, look for a witness under this key in the module. + if (!DifferentiabilityWitnessList) + return nullptr; + auto iter = DifferentiabilityWitnessList->find(mangledKey); + if (iter == DifferentiabilityWitnessList->end()) + return nullptr; + return readDifferentiabilityWitness(*iter); +} + /// Helper function to find a SILFunction, given its name and type. SILFunction *SILDeserializer::getFuncForReference(StringRef name, SILType type) { @@ -760,7 +788,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, // SIL_VTABLE or SIL_GLOBALVAR or SIL_WITNESS_TABLE record also means the end // of this SILFunction. while (kind != SIL_FUNCTION && kind != SIL_VTABLE && kind != SIL_GLOBALVAR && - kind != SIL_WITNESS_TABLE) { + kind != SIL_WITNESS_TABLE && kind != SIL_DIFFERENTIABILITY_WITNESS) { if (kind == SIL_BASIC_BLOCK) // Handle a SILBasicBlock record. CurrentBB = readSILBasicBlock(fn, CurrentBB, scratch); @@ -2988,6 +3016,7 @@ void SILDeserializer::readWitnessTableEntries( // Another record means the end of this WitnessTable. while (kind != SIL_WITNESS_TABLE && kind != SIL_DEFAULT_WITNESS_TABLE && + kind != SIL_DIFFERENTIABILITY_WITNESS && kind != SIL_FUNCTION) { if (kind == SIL_DEFAULT_WITNESS_TABLE_NO_ENTRY) { witnessEntries.push_back(SILDefaultWitnessTable::Entry()); @@ -3343,6 +3372,138 @@ SILDeserializer::lookupDefaultWitnessTable(SILDefaultWitnessTable *existingWt) { return Wt; } +SILDifferentiabilityWitness * +SILDeserializer::readDifferentiabilityWitness(DeclID DId) { + if (DId == 0) + return nullptr; + assert(DId <= DifferentiabilityWitnesses.size() && + "Invalid SILDifferentiabilityWitness ID"); + + auto &diffWitnessOrOffset = DifferentiabilityWitnesses[DId - 1]; + if (diffWitnessOrOffset.isFullyDeserialized()) + return diffWitnessOrOffset.get(); + + BCOffsetRAII restoreOffset(SILCursor); + if (auto err = SILCursor.JumpToBit(diffWitnessOrOffset.getOffset())) + MF->fatal(std::move(err)); + llvm::Expected maybeEntry = + SILCursor.advance(AF_DontPopBlockAtEnd); + if (!maybeEntry) + MF->fatal(maybeEntry.takeError()); + auto entry = maybeEntry.get(); + if (entry.Kind == llvm::BitstreamEntry::Error) { + LLVM_DEBUG(llvm::dbgs() << "Cursor advance error in " + "readDefaultWitnessTable.\n"); + return nullptr; + } + + SmallVector scratch; + StringRef blobData; + llvm::Expected maybeKind = + SILCursor.readRecord(entry.ID, scratch, &blobData); + if (!maybeKind) + MF->fatal(maybeKind.takeError()); + unsigned kind = maybeKind.get(); + assert(kind == SIL_DIFFERENTIABILITY_WITNESS && + "Expected sil_differentiability_witness"); + (void)kind; + + DeclID originalNameId, jvpNameId, vjpNameId; + unsigned rawLinkage, isDeclaration, isSerialized, numParameterIndices, + numResultIndices; + GenericSignatureID derivativeGenSigID; + ArrayRef rawParameterAndResultIndices; + + DifferentiabilityWitnessLayout::readRecord( + scratch, originalNameId, rawLinkage, isDeclaration, isSerialized, + derivativeGenSigID, jvpNameId, vjpNameId, numParameterIndices, + numResultIndices, rawParameterAndResultIndices); + + if (isDeclaration) { + assert(!isSerialized && "declaration must not be serialized"); + } + + auto linkageOpt = fromStableSILLinkage(rawLinkage); + assert(linkageOpt && + "Expected value linkage for sil_differentiability_witness"); + auto originalName = MF->getIdentifierText(originalNameId); + auto jvpName = MF->getIdentifierText(jvpNameId); + auto vjpName = MF->getIdentifierText(vjpNameId); + auto *original = getFuncForReference(originalName); + assert(original && "Original function must be found"); + auto *jvp = getFuncForReference(jvpName); + if (!jvpName.empty()) { + assert(!isDeclaration && "JVP must not be defined in declaration"); + assert(jvp && "JVP function must be found if JVP name is not empty"); + } + auto *vjp = getFuncForReference(vjpName); + if (!vjpName.empty()) { + assert(!isDeclaration && "VJP must not be defined in declaration"); + assert(vjp && "VJP function must be found if VJP name is not empty"); + } + auto derivativeGenSig = MF->getGenericSignature(derivativeGenSigID); + + SmallVector parameterAndResultIndices( + rawParameterAndResultIndices.begin(), rawParameterAndResultIndices.end()); + assert(parameterAndResultIndices.size() == + numParameterIndices + numResultIndices && + "Parameter/result indices count mismatch"); + auto *parameterIndices = IndexSubset::get( + MF->getContext(), original->getLoweredFunctionType()->getNumParameters(), + ArrayRef(parameterAndResultIndices) + .take_front(numParameterIndices)); + auto *resultIndices = IndexSubset::get( + MF->getContext(), original->getLoweredFunctionType()->getNumResults(), + ArrayRef(parameterAndResultIndices) + .take_back(numResultIndices)); + + AutoDiffConfig config(parameterIndices, resultIndices, derivativeGenSig); + auto *diffWitness = + SILMod.lookUpDifferentiabilityWitness({originalName, config}); + + // Witnesses that we deserialize are always available externally; we never + // want to emit them ourselves. + auto linkage = swift::addExternalToLinkage(*linkageOpt); + + // If there is no existing differentiability witness, create one. + if (!diffWitness) + diffWitness = SILDifferentiabilityWitness::createDeclaration( + SILMod, linkage, original, parameterIndices, resultIndices, + derivativeGenSig); + + // If the current differentiability witness is merely a declaration, and the + // deserialized witness is a definition, upgrade the current differentiability + // witness to a definition. This can happen in the following situations: + // 1. The witness was just created above. + // 2. The witness started out as a declaration (e.g. the differentiation + // pass emitted a witness for an external function) and now we're loading + // the definition (e.g. an optimization pass asked for the definition and + // we found the definition serialized in this module). + if (diffWitness->isDeclaration() && !isDeclaration) + diffWitness->convertToDefinition(jvp, vjp, isSerialized); + + diffWitnessOrOffset.set(diffWitness, + /*isFullyDeserialized*/ diffWitness->isDefinition()); + return diffWitness; +} + +SILDifferentiabilityWitness *SILDeserializer::lookupDifferentiabilityWitness( + StringRef mangledDiffWitnessKey) { + if (!DifferentiabilityWitnessList) + return nullptr; + auto iter = DifferentiabilityWitnessList->find(mangledDiffWitnessKey); + if (iter == DifferentiabilityWitnessList->end()) + return nullptr; + return readDifferentiabilityWitness(*iter); +} + +void SILDeserializer::getAllDifferentiabilityWitnesses() { + if (!DifferentiabilityWitnessList) + return; + for (unsigned I = 0, E = DifferentiabilityWitnesses.size(); I < E; ++I) + readDifferentiabilityWitness(I + 1); +} + SILDeserializer::~SILDeserializer() { // Drop our references to anything we've deserialized. for (auto &fnEntry : Funcs) { diff --git a/lib/Serialization/DeserializeSIL.h b/lib/Serialization/DeserializeSIL.h index 24147978b6bba..502b20882ae12 100644 --- a/lib/Serialization/DeserializeSIL.h +++ b/lib/Serialization/DeserializeSIL.h @@ -58,6 +58,11 @@ namespace swift { MutableArrayRef> Properties; + std::unique_ptr DifferentiabilityWitnessList; + MutableArrayRef< + ModuleFile::PartiallySerialized> + DifferentiabilityWitnesses; + /// A declaration will only llvm::DenseMap ConformanceToWitnessTableMap; @@ -113,6 +118,9 @@ namespace swift { SILType getSILType(Type ty, SILValueCategory category, SILFunction *inContext); + SILDifferentiabilityWitness * + getSILDifferentiabilityWitnessForReference(StringRef mangledKey); + SILFunction *getFuncForReference(StringRef Name, SILType Ty); SILFunction *getFuncForReference(StringRef Name); SILVTable *readVTable(serialization::DeclID); @@ -129,6 +137,8 @@ namespace swift { SILDefaultWitnessTable * readDefaultWitnessTable(serialization::DeclID, SILDefaultWitnessTable *existingWt); + SILDifferentiabilityWitness * + readDifferentiabilityWitness(serialization::DeclID); Optional readKeyPathComponent(ArrayRef ListOfValues, unsigned &nextValue); @@ -148,6 +158,8 @@ namespace swift { SILWitnessTable *lookupWitnessTable(SILWitnessTable *wt); SILDefaultWitnessTable * lookupDefaultWitnessTable(SILDefaultWitnessTable *wt); + SILDifferentiabilityWitness * + lookupDifferentiabilityWitness(StringRef mangledDiffWitnessKey); /// Invalidate all cached SILFunctions. void invalidateFunctionCache(); @@ -172,6 +184,7 @@ namespace swift { getAllWitnessTables(); getAllDefaultWitnessTables(); getAllProperties(); + getAllDifferentiabilityWitnesses(); } /// Deserialize all SILFunctions inside the module and add them to SILMod. @@ -195,6 +208,10 @@ namespace swift { /// to SILMod. void getAllProperties(); + /// Deserialize all DifferentiabilityWitnesses inside the module and add + /// them to SILMod. + void getAllDifferentiabilityWitnesses(); + SILDeserializer(ModuleFile *MF, SILModule &M, DeserializationNotificationHandlerSet *callback); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index f79836e2b9ba1..6c223c620fa77 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 = 536; // Clang function types +const uint16_t SWIFTMODULE_VERSION_MINOR = 537; // SIL differentiability witnesses /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 0e201761ac36c..404c3f7c377fe 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -97,6 +97,8 @@ namespace sil_index_block { SIL_DEFAULT_WITNESS_TABLE_NAMES, SIL_DEFAULT_WITNESS_TABLE_OFFSETS, SIL_PROPERTY_OFFSETS, + SIL_DIFFERENTIABILITY_WITNESS_NAMES, + SIL_DIFFERENTIABILITY_WITNESS_OFFSETS, }; using ListLayout = BCGenericRecordLayout< @@ -141,6 +143,7 @@ namespace sil_block { SIL_WITNESS_CONDITIONAL_CONFORMANCE, SIL_DEFAULT_WITNESS_TABLE, SIL_DEFAULT_WITNESS_TABLE_NO_ENTRY, + SIL_DIFFERENTIABILITY_WITNESS, SIL_INST_WITNESS_METHOD, SIL_SPECIALIZE_ATTR, SIL_PROPERTY, @@ -250,6 +253,20 @@ namespace sil_block { DeclIDField >; + using DifferentiabilityWitnessLayout = BCRecordLayout< + SIL_DIFFERENTIABILITY_WITNESS, + DeclIDField, // Original function name + SILLinkageField, // Linkage + BCFixed<1>, // Is declaration? + BCFixed<1>, // Is serialized? + GenericSignatureIDField, // Derivative function generic signature + DeclIDField, // JVP function name + DeclIDField, // VJP function name + BCVBR<8>, // Number of parameter indices + BCVBR<8>, // Number of result indices + BCArray // Parameter and result indices + >; + using SILFunctionLayout = BCRecordLayout, // transparent diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 3d9bbec929e62..a45b9392d3697 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -183,6 +183,12 @@ namespace { /// Holds the list of Properties. std::vector PropertyOffset; + /// Maps differentiability witness identifier to an ID. + Table DifferentiabilityWitnessList; + /// Holds the list of SIL differentiability witnesses. + std::vector DifferentiabilityWitnessOffset; + uint32_t /*DeclID*/ NextDifferentiabilityWitnessID = 1; + /// Give each SILBasicBlock a unique ID. llvm::DenseMap BasicBlockMap; @@ -193,6 +199,10 @@ namespace { /// Global variables that we've emitted a reference to. llvm::DenseSet GlobalsToEmit; + /// Referenced differentiability witnesses that need to be emitted. + llvm::DenseSet + DifferentiabilityWitnessesToEmit; + /// Additional functions we might need to serialize. llvm::SmallVector Worklist; @@ -232,6 +242,8 @@ namespace { void writeSILWitnessTable(const SILWitnessTable &wt); void writeSILWitnessTableEntry(const SILWitnessTable::Entry &entry); void writeSILDefaultWitnessTable(const SILDefaultWitnessTable &wt); + void + writeSILDifferentiabilityWitness(const SILDifferentiabilityWitness &dw); void writeSILProperty(const SILProperty &prop); void writeSILBlock(const SILModule *SILMod); @@ -2159,9 +2171,10 @@ static void writeIndexTable(Serializer &S, kind == sil_index_block::SIL_VTABLE_NAMES || kind == sil_index_block::SIL_GLOBALVAR_NAMES || kind == sil_index_block::SIL_WITNESS_TABLE_NAMES || - kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES) && - "SIL function table, global, vtable and (default) witness table " - "are supported"); + kind == sil_index_block::SIL_DEFAULT_WITNESS_TABLE_NAMES || + kind == sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES) && + "SIL function table, global, vtable, (default) witness table, and " + "differentiability witness table are supported"); llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { @@ -2217,6 +2230,15 @@ void SILSerializer::writeIndexTables() { DefaultWitnessTableOffset); } + if (!DifferentiabilityWitnessList.empty()) { + writeIndexTable(S, List, + sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_NAMES, + DifferentiabilityWitnessList); + Offset.emit(ScratchRecord, + sil_index_block::SIL_DIFFERENTIABILITY_WITNESS_OFFSETS, + DifferentiabilityWitnessOffset); + } + if (!PropertyOffset.empty()) { Offset.emit(ScratchRecord, sil_index_block::SIL_PROPERTY_OFFSETS, PropertyOffset); @@ -2413,6 +2435,48 @@ writeSILDefaultWitnessTable(const SILDefaultWitnessTable &wt) { } } +void SILSerializer::writeSILDifferentiabilityWitness( + const SILDifferentiabilityWitness &dw) { + Mangle::ASTMangler mangler; + auto mangledKey = mangler.mangleSILDifferentiabilityWitnessKey(dw.getKey()); + size_t nameLength = mangledKey.size(); + char *stringStorage = + static_cast(StringTable.Allocate(nameLength, 1)); + std::memcpy(stringStorage, mangledKey.data(), nameLength); + DifferentiabilityWitnessList[StringRef(stringStorage, nameLength)] = + NextDifferentiabilityWitnessID++; + DifferentiabilityWitnessOffset.push_back(Out.GetCurrentBitNo()); + + auto *original = dw.getOriginalFunction(); + IdentifierID jvpID = 0; + IdentifierID vjpID = 0; + if (auto *jvp = dw.getJVP()) + jvpID = addSILFunctionRef(jvp); + if (auto *vjp = dw.getVJP()) + vjpID = addSILFunctionRef(vjp); + SmallVector parameterAndResultIndices( + dw.getParameterIndices()->begin(), dw.getParameterIndices()->end()); + parameterAndResultIndices.append(dw.getResultIndices()->begin(), + dw.getResultIndices()->end()); + auto originalFnType = original->getLoweredFunctionType(); + assert(originalFnType->getNumParameters() == + dw.getParameterIndices()->getCapacity() && + "Original function parameter count should match differentiability " + "witness parameter indices capacity"); + assert(originalFnType->getNumResults() == + dw.getResultIndices()->getCapacity() && + "Original function result count should match differentiability " + "witness result indices capacity"); + + DifferentiabilityWitnessLayout::emitRecord( + Out, ScratchRecord, SILAbbrCodes[DifferentiabilityWitnessLayout::Code], + addSILFunctionRef(original), toStableSILLinkage(dw.getLinkage()), + dw.isDeclaration(), dw.isSerialized(), + S.addGenericSignatureRef(dw.getDerivativeGenericSignature()), jvpID, + vjpID, dw.getParameterIndices()->getNumIndices(), + dw.getResultIndices()->getNumIndices(), parameterAndResultIndices); +} + /// Helper function for whether to emit a function body. bool SILSerializer::shouldEmitFunctionBody(const SILFunction *F, bool isReference) { @@ -2473,6 +2537,7 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); + registerSILAbbr(); registerSILAbbr(); registerSILAbbr(); @@ -2550,6 +2615,24 @@ void SILSerializer::writeSILBlock(const SILModule *SILMod) { processSILFunctionWorklist(); } + // Write out differentiability witnesses. + // Note: this must be done after visiting SIL functions above so that + // differentiability witness references (`differentiability_witness_function` + // instructions) have been tracked. + for (const auto &diffWitness : SILMod->getDifferentiabilityWitnessList()) { + // TODO(TF-893): Consider checking + // `SILMod->shouldSerializeEntitiesAssociatedWithDeclContext` on the JVP/VJP + // functions. + if ((ShouldSerializeAll || diffWitness.isSerialized())) + DifferentiabilityWitnessesToEmit.insert(&diffWitness); + } + for (auto *diffWitness : DifferentiabilityWitnessesToEmit) + writeSILDifferentiabilityWitness(*diffWitness); + // Process SIL functions referenced by differentiability witnesses. + // Note: this is necessary despite processing `FuncsToEmit` below because + // `Worklist` is processed separately. + processSILFunctionWorklist(); + // Now write function declarations for every function we've // emitted a reference to without emitting a function body for. for (const SILFunction &F : *SILMod) { diff --git a/lib/Serialization/SerializedSILLoader.cpp b/lib/Serialization/SerializedSILLoader.cpp index 683d7e30573cd..0adbd544b0608 100644 --- a/lib/Serialization/SerializedSILLoader.cpp +++ b/lib/Serialization/SerializedSILLoader.cpp @@ -128,6 +128,22 @@ lookupDefaultWitnessTable(SILDefaultWitnessTable *WT) { return nullptr; } +SILDifferentiabilityWitness * +SerializedSILLoader::lookupDifferentiabilityWitness( + SILDifferentiabilityWitnessKey key) { + Mangle::ASTMangler mangler; + std::string mangledKey = mangler.mangleSILDifferentiabilityWitnessKey(key); + // It is possible that one module has a declaration of a + // SILDifferentiabilityWitness, while another has the full definition. + SILDifferentiabilityWitness *dw = nullptr; + for (auto &Des : LoadedSILSections) { + dw = Des->lookupDifferentiabilityWitness(mangledKey); + if (dw && dw->isDefinition()) + return dw; + } + return dw; +} + void SerializedSILLoader::invalidateCaches() { for (auto &Des : LoadedSILSections) Des->invalidateFunctionCache(); diff --git a/test/AutoDiff/SIL/Parse/sil_differentiability_witness.sil b/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil similarity index 91% rename from test/AutoDiff/SIL/Parse/sil_differentiability_witness.sil rename to test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil index 5d9ccf6d0503b..75cc1d803311d 100644 --- a/test/AutoDiff/SIL/Parse/sil_differentiability_witness.sil +++ b/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil @@ -1,7 +1,20 @@ -// Round-trip parsing/printing test. +// Test round-trip parsing/printing. // RUN: %target-sil-opt %s | %target-sil-opt -emit-sorted-sil | %FileCheck --check-prefix=ROUNDTRIP %s + +// Test round-trip serialization/deserialization. + +// RUN: %empty-directory(%t) +// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name main +// RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.sil -module-name main + +// NOTE(SR-12090): Workaround because import declarations are not preserved in .sib files. +// RUN: sed -e 's/import Swift$/import Swift; import _Differentiation/' %t/tmp.sil > %t/tmp_fixed.sil +// RUN: %target-sil-opt %t/tmp_fixed.sil -module-name main -emit-sorted-sil | %FileCheck --check-prefix=ROUNDTRIP %s + // REQUIRES: differentiable_programming +// NOTE(SR-12090): `shell` is required only to run `sed` as a SR-12090 workaround. +// REQUIRES: shell sil_stage raw From d30ea11d00f096709a363d0496c96a8df7078bc5 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 6 Feb 2020 18:06:26 +0000 Subject: [PATCH 207/237] Update docs/HowSwiftImportsCAPIs.md * Increase heading levels * Update the introduction * Use `Int` in `qsort` examples * Use `CInt` in anonymous structs * Use `CUnsignedInt` in enums * De-dupe `HomeworkExcuse` enum --- docs/HowSwiftImportsCAPIs.md | 123 +++++++++++++++-------------------- 1 file changed, 53 insertions(+), 70 deletions(-) diff --git a/docs/HowSwiftImportsCAPIs.md b/docs/HowSwiftImportsCAPIs.md index 02f45d8b86f57..69435bb020b6b 100644 --- a/docs/HowSwiftImportsCAPIs.md +++ b/docs/HowSwiftImportsCAPIs.md @@ -1,13 +1,13 @@ # How Swift imports C APIs -When Swift imports a module or parses a bridging header from a C-based language +When Swift imports a module, or parses a bridging header from a C-based language (C, Objective-C), the APIs are mapped into Swift APIs and can be used directly from Swift code. This provides the basis for Swift's Foreign Function Interface (FFI), providing interoperability with existing libraries written in C-based -languages. This document describes how APIs from C-based languages are mapped -into Swift APIs. +languages. -this document is written for a broad audience, including Swift and C users who +This document describes how APIs from C-based languages are mapped into Swift +APIs. It is written for a broad audience, including Swift and C users who might not be language experts. Therefore, it explains some advanced concepts where necessary. @@ -34,9 +34,9 @@ where necessary. * [Typedefs](#typedefs) * [Macros](#macros) -# Names, identifiers and keywords +## Names, identifiers and keywords -## Unicode +### Unicode C (and C++) permit non-ASCII Unicode code points in identifiers. While Swift does not permit arbitrary Unicode code points in identifiers (so compatibility @@ -44,7 +44,7 @@ might not be perfect), it tries to allow reasonable ones. However, C, Objective-C, and C++ code in practice does not tend to use non-ASCII code points in identifiers, so mapping them to Swift is not an important concern. -## Names that are keywords in Swift +### Names that are keywords in Swift Some C and C++ identifiers are keywords in Swift. Despite that, such names are imported as-is into Swift, because Swift permits escaping keywords to use them @@ -74,14 +74,14 @@ func test() { } ``` -## Name translation +### Name translation Names of some C declarations appear in Swift differently. In particular, names of enumerators (enum constants) go through a translation process that is hardcoded into the compiler. For more details, see [Name Translation from C to Swift](CToSwiftNameTranslation.md). -## Name customization +### Name customization As a general principle of Swift/C interoperability, the C API vendor has broad control over how their APIs appear in Swift. In particular, the vendor can use @@ -89,7 +89,7 @@ the `swift_name` Clang attribute to customize the names of their C APIs in order to be more idiomatic in Swift. For more details, see [Name Translation from C to Swift](CToSwiftNameTranslation.md). -# Size, stride, and alignment of types +## Size, stride, and alignment of types In C, every type has a size (computed with the `sizeof` operator), and an alignment (computed with `alignof`). @@ -159,7 +159,7 @@ print(MemoryLayout.size) // 4 print(MemoryLayout.stride) // 4 ``` -# Fundamental types +## Fundamental types In C, certain types (`char`, `int`, `float` etc.) are built into the language and into the compiler. These builtin types have behaviors that are not possible @@ -289,12 +289,12 @@ double Add(int x, long y); func Add(_ x: CInt, _ y: CLong) -> CDouble ``` -# Free functions +## Free functions C functions are imported as free functions in Swift. Each type in the signature of the C function is mapped to the corresponding Swift type. -## Argument labels +### Argument labels Imported C functions don't have argument labels in Swift by default. Argument labels can be added by API owners through annotations in the C header. @@ -322,7 +322,7 @@ drawString("hello", 10, 20) drawStringRenamed("hello", x: 10, y: 20) ``` -## Variadic arguments +### Variadic arguments C functions with variadic arguments are not imported into Swift, however, there are no technical reasons why they can't be imported. @@ -344,7 +344,7 @@ See also Apple's documentation about this topic: [Use a CVaListPointer to Call Variadic Functions](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_functions_in_swift#2992073). -## Inline functions +### Inline functions Inline C functions that are defined in headers are imported as regular Swift functions. However, unlike free functions, inline functions require the caller @@ -356,7 +356,7 @@ the C inline function. LLVM IR for C inline functions and LLVM IR for Swift code is put into one LLVM module, allowing all LLVM optimizations (like inlining) to work transparently across language boundaries. -# Global variables +## Global variables Global C variables are imported as Swift variables or constants, depending on constness. @@ -375,7 +375,7 @@ var NumAlpacas: CInt let NumLlamas: CInt ``` -# Pointers to data +## Pointers to data C has one way to form a pointer to a value of type `T` -- `T*`. @@ -420,7 +420,7 @@ void AddSecondToFirst(int *x, const long *y); func AddSecondToFirst(_ x: UnsafeMutablePointer!, _ y: UnsafePointer!) ``` -# Nullable and non-nullable pointers +## Nullable and non-nullable pointers Any C pointer can be null. However, in practice, many pointers are never null. Therefore, code often does not expect certain pointers to be null and does not @@ -559,7 +559,7 @@ See also Apple's documentation about this topic: [Designating Nullability in Objective-C APIs](https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/designating_nullability_in_objective-c_apis). -# Incomplete types and pointers to them +## Incomplete types and pointers to them C and C++ have a notion of incomplete types; Swift does not have anything similar. Incomplete C types are not imported in Swift in any form. @@ -589,7 +589,7 @@ void Print(const Foo* foo); func Print(_ foo: OpaquePointer) ``` -# Function pointers +## Function pointers C supports only one form of function pointers: `Result (*)(Arg1, Arg2, Arg3)`. @@ -641,15 +641,15 @@ void qsort_annotated( func qsort( _ base: UnsafeMutableRawPointer!, - _ nmemb: CInt, - _ size: CInt, + _ nmemb: Int, + _ size: Int, _ compar: (@convention(c) (UnsafeRawPointer?, UnsafeRawPointer?) -> CInt)! ) func qsort_annotated( _ base: UnsafeMutableRawPointer, - _ nmemb: CInt, - _ size: CInt, + _ nmemb: Int, + _ size: Int, _ compar: @convention(c) (UnsafeRawPointer, UnsafeRawPointer) -> CInt ) ``` @@ -657,7 +657,7 @@ func qsort_annotated( See also Apple's documentation about this topic: [Using Imported C Functions in Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_functions_in_swift) -# Fixed-size arrays +## Fixed-size arrays C's fixed-size arrays are imported as Swift tuples. @@ -685,7 +685,7 @@ Fixed-size arrays are a commonly requested feature in Swift, and a good proposal is likely to be accepted. Once Swift has fixed-size arrays natively in the language, we can use them to improve C interoperability. -# Structs +## Structs C structs are imported as Swift structs, their fields are mapped to stored Swift properties. Bitfields are mapped to computed Swift properties. Swift structs @@ -752,17 +752,17 @@ struct StructWithAnonymousStructs { // C header imported in Swift. struct StructWithAnonymousStructs { struct __Unnamed_struct___Anonymous_field0 { - var x: Int32 + var x: CInt init() - init(x: Int32) + init(x: CInt) } struct __Unnamed_struct_containerForY { - var y: Int32 + var y: CInt init() - init(y: Int32) + init(y: CInt) } var __Anonymous_field0: StructWithAnonymousStructs.__Unnamed_struct___Anonymous_field0 - var x: Int32 + var x: CInt var containerForY: StructWithAnonymousStructs.__Unnamed_struct_containerForY // Default initializer that sets all properties to zero. @@ -780,7 +780,7 @@ See also Apple's documentation about this topic: [Using Imported C Structs and Unions in Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift). -# Unions +## Unions Swift does not have a direct equivalent to a C union. C unions are mapped to Swift structs with computed properties that read from/write to the same @@ -811,7 +811,7 @@ See also Apple's documentation about this topic: [Using Imported C Structs and Unions in Swift](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift). -# Enums +## Enums We would have liked to map C enums to Swift enums, like this: @@ -829,7 +829,7 @@ enum HomeworkExcuse { ```swift // C header imported in Swift: aspiration, not an actual mapping! -enum HomeworkExcuse : UInt32 { +enum HomeworkExcuse: CUnsignedInt { case EatenByPet case ForgotAtHome case ThoughtItWasDueNextWeek @@ -841,11 +841,11 @@ However, in practice, plain C enums are mapped to Swift structs like this: ```swift // C header imported in Swift: actual mapping. -struct HomeworkExcuse : Equatable, RawRepresentable { - init(_ rawValue: UInt32) - init(rawValue: UInt32) - var rawValue: UInt32 - typealias RawValue = UInt32 +struct HomeworkExcuse: Equatable, RawRepresentable { + init(_ rawValue: CUnsignedInt) + init(rawValue: CUnsignedInt) + var rawValue: CUnsignedInt { get } + typealias RawValue = CUnsignedInt } var EatenByPet: HomeworkExcuse { get } var ForgotAtHome: HomeworkExcuse { get } @@ -902,13 +902,6 @@ does not handle all cases available at the compilation time. ```c // C header. -// Enum that is not explicitly marked as either open or closed. -enum HomeworkExcuse { - EatenByPet, - ForgotAtHome, - ThoughtItWasDueNextWeek, -}; - // An open enum: we expect to add more kinds of input devices in future. enum InputDevice { Keyboard, @@ -929,30 +922,20 @@ enum CardinalDirection { ```swift // C header imported in Swift. -struct HomeworkExcuse : Equatable, RawRepresentable { - init(_ rawValue: UInt32) - init(rawValue: UInt32) - var rawValue: UInt32 - typealias RawValue = UInt32 -} -var EatenByPet: HomeworkExcuse { get } -var ForgotAtHome: HomeworkExcuse { get } -var ThoughtItWasDueNextWeek: HomeworkExcuse { get } - -enum InputDevice : UInt32 { - init?(rawValue: UInt32) - var rawValue: UInt32 { get } - typealias RawValue = UInt32 +enum InputDevice: CUnsignedInt, Hashable, RawRepresentable { + init?(rawValue: CUnsignedInt) + var rawValue: CUnsignedInt { get } + typealias RawValue = CUnsignedInt case Keyboard case Mouse case Touchscreen } -@_frozen -enum CardinalDirection : UInt32 { - init?(rawValue: UInt32) - var rawValue: UInt32 { get } - typealias RawValue = UInt32 +@frozen +enum CardinalDirection: CUnsignedInt, Hashable, RawRepresentable { + init?(rawValue: CUnsignedInt) + var rawValue: CUnsignedInt { get } + typealias RawValue = CUnsignedInt case East case West case North @@ -991,12 +974,12 @@ numeric and typed values. // Converting enum values to integers and back. var south: CardinalDirection = .South -// var southAsInteger: UInt32 = south // error: type mismatch -var southAsInteger: UInt32 = south.rawValue // = 3 -var southAsEnum = CardinalDirection(rawValue: 3) // = South +// var southAsInteger: CUnsignedInt = south // error: type mismatch +var southAsInteger: CUnsignedInt = south.rawValue // = 3 +var southAsEnum = CardinalDirection(rawValue: 3) // = .South ``` -# Typedefs +## Typedefs C typedefs are generally mapped to Swift typealiases, except for a few common C coding patterns that are handled in a special way. @@ -1027,7 +1010,7 @@ struct Point { } ``` -# Macros +## Macros C macros are generally not imported in Swift. Macros that define constants are imported as readonly variables. From 3fb1707fd174b1bb3f1d5cdb992ef1a446494268 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 7 Feb 2020 10:11:40 -0800 Subject: [PATCH 208/237] SIL: add missing newline (NFC) This fixes an accidental joining of lines. --- include/swift/SIL/SILModule.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index 285a013fcbdc8..d38c5b7741fa7 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -475,7 +475,8 @@ class SILModule { using differentiability_witness_iterator = DifferentiabilityWitnessListType::iterator; using differentiability_witness_const_iterator = DifferentiabilityWitnessListType::const_iterator; DifferentiabilityWitnessListType &getDifferentiabilityWitnessList() { return differentiabilityWitnesses; } - const DifferentiabilityWitnessListType &getDifferentiabilityWitnessList() const { return differentiabilityWitnesses; } differentiability_witness_iterator differentiability_witness_begin() { return differentiabilityWitnesses.begin(); } + const DifferentiabilityWitnessListType &getDifferentiabilityWitnessList() const { return differentiabilityWitnesses; } + differentiability_witness_iterator differentiability_witness_begin() { return differentiabilityWitnesses.begin(); } differentiability_witness_iterator differentiability_witness_end() { return differentiabilityWitnesses.end(); } differentiability_witness_const_iterator differentiability_witness_begin() const { return differentiabilityWitnesses.begin(); } differentiability_witness_const_iterator differentiability_witness_end() const { return differentiabilityWitnesses.end(); } From 8841d6d703aee18401ec35b5443086aa3d4bcbb7 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 7 Feb 2020 12:58:08 -0800 Subject: [PATCH 209/237] [gardening] Add a comment to BUILTIN_MISC_OPERATION_WITH_SILGEN that explains how to use it/contrasts with BUILTIN_SIL_OPERATION. --- include/swift/AST/Builtins.def | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 4b5cd1888a30a..e47b8ddae1f4f 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -634,6 +634,12 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special) // BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are // specially emitted during SIL generation. +// +// The intention is that this is meant for builtins that need a named +// builtin representation so one can create a builtin instruction in +// SIL, but that also need special SILGen behavior. If an operation +// just emits custom SIL and does not need to be able to form a +// builtin instruction, please use BUILTIN_SIL_OPERATION. #ifndef BUILTIN_MISC_OPERATION_WITH_SILGEN #define BUILTIN_MISC_OPERATION_WITH_SILGEN(Id, Name, Attrs, Overload) \ BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload) From 1137c001964638075a08d1c11377c6e98468e959 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 6 Feb 2020 11:30:53 -0800 Subject: [PATCH 210/237] [builtin] Add a new SIL builtin convertStrongToUnownedUnsafe() The signature is: (T, @inout @unowned(unsafe) Optional) -> () The reason for the weird signature is that currently the Builtin infrastructure does not handle results well. The semantics of this builtin is that it enables one to store the first argument into an unowned unsafe address without any reference counting operations. It does this just by SILGening the relevant code. The optimizer chews through this code well, so we get the expected behavior. I also included a small proof of concept to validate that this builtin works as expected. --- include/swift/AST/Builtins.def | 6 ++ include/swift/SIL/SILType.h | 18 ++++++ lib/AST/Builtins.cpp | 19 ++++++ lib/SIL/ValueOwnership.cpp | 1 - lib/SILGen/LValue.h | 11 +++- lib/SILGen/SILGenBuiltin.cpp | 64 +++++++++++++++++++++ test/SILGen/unsafevalue.swift | 102 +++++++++++++++++++++++++++++++++ 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 test/SILGen/unsafevalue.swift diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index e47b8ddae1f4f..8c53dd6a31222 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -444,6 +444,12 @@ BUILTIN_SIL_OPERATION(AllocWithTailElems, "allocWithTailElems", Special) /// Projects the first tail-allocated element of type E from a class C. BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special) +/// Unsafely convert a value of type T to an unowned value. +/// +/// It has type (T, @inout @unowned(unsafe) T) -> (). The reason for the weird +/// signature is to work around issues with results in SILGen builtin emission. +BUILTIN_SIL_OPERATION(ConvertStrongToUnownedUnsafe, "convertStrongToUnownedUnsafe", Special) + #undef BUILTIN_SIL_OPERATION // BUILTIN_RUNTIME_CALL - A call into a runtime function. diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index 0159b23b41ab6..4e15caf4c932f 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -459,6 +459,24 @@ class SILType { return SILType(getASTType().getReferenceStorageReferent(), getCategory()); } + /// Return the reference ownership of this type if it is a reference storage + /// type. Otherwse, return None. + Optional getReferenceStorageOwnership() const { + auto type = getASTType()->getAs(); + if (!type) + return None; + return type->getOwnership(); + } + + /// Attempt to wrap the passed in type as a type with reference ownership \p + /// ownership. For simplicity, we always return an address since reference + /// storage types may not be loadable (e.x.: weak ownership). + SILType getReferenceStorageType(const ASTContext &ctx, + ReferenceOwnership ownership) const { + auto *type = ReferenceStorageType::get(getASTType(), ownership, ctx); + return SILType::getPrimitiveAddressType(type->getCanonicalType()); + } + /// Transform the function type SILType by replacing all of its interface /// generic args with the appropriate item from the substitution. /// diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 818d7ffb112d2..bc140b9422418 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -940,6 +940,22 @@ static ValueDecl *getGlobalStringTablePointer(ASTContext &Context, return getBuiltinFunction(Id, {stringType}, Context.TheRawPointerType); } +static ValueDecl *getConvertStrongToUnownedUnsafe(ASTContext &ctx, + Identifier id) { + // We actually want this: + // + // (T, inout unowned (unsafe) T) -> () + // + // But for simplicity, we actually accept T, U and do the checking + // in the emission method that everything works up. This is a + // builtin, so we can crash. + BuiltinFunctionBuilder builder(ctx, 2); + builder.addParameter(makeGenericParam(0)); + builder.addParameter(makeGenericParam(1), ValueOwnership::InOut); + builder.setResult(makeConcrete(TupleType::getEmpty(ctx))); + return builder.build(id); +} + static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) { auto int1Type = BuiltinIntegerType::get(1, Context); auto optionalRawPointerType = BoundGenericEnumType::get( @@ -1996,6 +2012,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::GlobalStringTablePointer: return getGlobalStringTablePointer(Context, Id); + case BuiltinValueKind::ConvertStrongToUnownedUnsafe: + return getConvertStrongToUnownedUnsafe(Context, Id); + case BuiltinValueKind::PoundAssert: return getPoundAssert(Context, Id); diff --git a/lib/SIL/ValueOwnership.cpp b/lib/SIL/ValueOwnership.cpp index 94ebb2555fdd6..0cfea82a3203a 100644 --- a/lib/SIL/ValueOwnership.cpp +++ b/lib/SIL/ValueOwnership.cpp @@ -547,7 +547,6 @@ UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ZeroInitializer) BuiltinInst *BI, StringRef Attr) { \ llvm_unreachable("builtin should have been lowered in SILGen"); \ } - #include "swift/AST/Builtins.def" ValueOwnershipKind diff --git a/lib/SILGen/LValue.h b/lib/SILGen/LValue.h index 3ba2e78bfaf49..ba9d444e35175 100644 --- a/lib/SILGen/LValue.h +++ b/lib/SILGen/LValue.h @@ -405,7 +405,16 @@ class LValue { assert(&component == Path.back().get()); Path.pop_back(); } - + + /// Pop the last component off this LValue unsafely. Validates that the + /// component is of kind \p kind as a sanity check. + /// + /// Please be careful when using this! + void unsafelyDropLastComponent(PathComponent::KindTy kind) & { + assert(kind == Path.back()->getKind()); + Path.pop_back(); + } + /// Add a new component at the end of the access path of this lvalue. template void add(As &&... args) { diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index f1772f0e511df..049647b4909ef 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -1045,6 +1045,70 @@ emitBuiltinGlobalStringTablePointer(SILGenFunction &SGF, SILLocation loc, return SGF.emitManagedRValueWithCleanup(resultVal); } +/// Emit SIL for the named builtin: +/// convertStrongToUnownedUnsafe. Unlike the default ownership +/// convention for named builtins, which is to take (non-trivial) +/// arguments as Owned, this builtin accepts owned as well as +/// guaranteed arguments, and hence doesn't require the arguments to +/// be at +1. Therefore, this builtin is emitted specially. +/// +/// We assume our convention is (T, @inout @unmanaged Optional) -> () +static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe( + SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, + PreparedArguments &&preparedArgs, SGFContext C) { + auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2); + if (!argsOrError) + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); + + auto args = *argsOrError; + + // First get our object at +0 if we can. + auto object = SGF.emitRValue(args[0], SGFContext::AllowGuaranteedPlusZero) + .getAsSingleValue(SGF, args[0]); + + // Borrow it and get the value. + SILValue objectSrcValue = object.borrow(SGF, loc).getValue(); + + // Then create our inout. + auto inout = cast(args[1]->getSemanticsProvidingExpr()); + auto lv = + SGF.emitLValue(inout->getSubExpr(), SGFAccessKind::BorrowedAddressRead); + lv.unsafelyDropLastComponent(PathComponent::OwnershipKind); + if (!lv.isPhysical() || !lv.isLoadingPure()) { + llvm::report_fatal_error("Builtin.convertStrongToUnownedUnsafe passed " + "non-physical, non-pure lvalue as 2nd arg"); + } + + SILValue inoutDest = + SGF.emitAddressOfLValue(args[1], std::move(lv)).getLValueAddress(); + SILType destType = inoutDest->getType().getObjectType(); + + // Make sure our types match up as we expect. + if (objectSrcValue->getType() != + destType.getReferenceStorageReferentType().getOptionalObjectType()) { + llvm::errs() + << "Invalid usage of Builtin.convertStrongToUnsafeUnowned. lhsType " + "must be T and rhsType must be inout unsafe(unowned) Optional" + << "lhsType: " << objectSrcValue->getType() << "\n" + << "rhsType: " << inoutDest->getType() << "\n"; + llvm::report_fatal_error("standard fatal error msg"); + } + + // Ok. We have the right types. First convert objectSrcValue to its + // unowned representation. + SILType optionalType = SILType::getOptionalType(objectSrcValue->getType()); + SILValue someVal = + SGF.B.createOptionalSome(loc, objectSrcValue, optionalType); + + SILType unmanagedOptType = someVal->getType().getReferenceStorageType( + SGF.getASTContext(), ReferenceOwnership::Unmanaged); + SILValue unownedObjectSrcValue = SGF.B.createRefToUnmanaged( + loc, someVal, unmanagedOptType.getObjectType()); + SGF.B.emitStoreValueOperation(loc, unownedObjectSrcValue, inoutDest, + StoreOwnershipQualifier::Trivial); + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); +} + Optional SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) { // Only consider standalone declarations in the Builtin module. diff --git a/test/SILGen/unsafevalue.swift b/test/SILGen/unsafevalue.swift new file mode 100644 index 0000000000000..38e6fab868310 --- /dev/null +++ b/test/SILGen/unsafevalue.swift @@ -0,0 +1,102 @@ +// RUN: %target-swift-emit-silgen -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s +// RUN: %target-swift-emit-sil -Onone -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck -check-prefix=CANONICAL %s +// RUN: %target-swift-emit-sil -O -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck -check-prefix=OPT %s + +import Swift + +// Eventually element will be unconstrained, but for testing this builtin, we +// should use it this way. +@frozen +public struct UnsafeValue { + @usableFromInline + internal unowned(unsafe) var _value: Element? + + @_transparent + @inlinable + public init() { + _value = nil + } + + // Create a new unmanaged value that owns the underlying value. This unmanaged + // value must after use be deinitialized by calling the function deinitialize() + // + // This will insert a retain that the optimizer can not remove! + @_transparent + @inlinable + public init(copying newValue: __shared Element) { + self.init() + _value = newValue + } + + // Create a new unmanaged value that unsafely produces a new + // unmanaged value without introducing any rr traffic. + // + // CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s11unsafevalue11UnsafeValueV14unsafelyAssignACyxGxh_tcfC : $@convention(method) (@guaranteed Element, @thin UnsafeValue.Type) -> UnsafeValue { + // CHECK: bb0([[INPUT_ELEMENT:%.*]] : @guaranteed $Element, + // CHECK: [[BOX:%.*]] = alloc_box + // CHECK: [[UNINIT_BOX:%.*]] = mark_uninitialized [delegatingself] [[BOX]] + // CHECK: [[PROJECT_UNINIT_BOX:%.*]] = project_box [[UNINIT_BOX]] + // CHECK: [[DELEGATING_INIT_RESULT:%.*]] = apply {{%.*}}( + // CHECK: assign [[DELEGATING_INIT_RESULT]] to [[PROJECT_UNINIT_BOX]] + // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[PROJECT_UNINIT_BOX]] + // CHECK: [[STRUCT_ACCESS:%.*]] = struct_element_addr [[ACCESS]] + // CHECK: [[OPT_INPUT_ELEMENT:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[INPUT_ELEMENT]] + // CHECK: [[UNMANAGED_OPT_INPUT_ELEMENT:%.*]] = ref_to_unmanaged [[OPT_INPUT_ELEMENT]] + // CHECK: store [[UNMANAGED_OPT_INPUT_ELEMENT]] to [trivial] [[STRUCT_ACCESS]] + // CHECK: end_access [[ACCESS]] + // CHECK: [[RESULT:%.*]] = load [trivial] [[PROJECT_UNINIT_BOX]] + // CHECK: destroy_value [[UNINIT_BOX]] + // CHECK: return [[RESULT]] + // CHECK: } // end sil function '$s11unsafevalue11UnsafeValueV14unsafelyAssignACyxGxh_tcfC' + // + // CANONICAL-LABEL: sil [transparent] [serialized] @$s11unsafevalue11UnsafeValueV14unsafelyAssignACyxGxh_tcfC : $@convention(method) (@guaranteed Element, @thin UnsafeValue.Type) -> UnsafeValue { + // CANONICAL: bb0([[INPUT_ELEMENT:%.*]] : $Element, + // CANONICAL-NEXT: debug_value + // TODO(gottesmm): release_value on a .none shouldn't exist. + // CANONICAL-NEXT: [[NONE:%.*]] = enum $Optional, #Optional.none!enumelt + // CANONICAL-NEXT: release_value [[NONE]] + // CANONICAL-NEXT: [[ENUM:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[INPUT_ELEMENT]] + // CANONICAL-NEXT: [[UNMANAGED_ENUM:%.*]] = ref_to_unmanaged [[ENUM]] + // CANONICAL-NEXT: [[RESULT:%.*]] = struct $UnsafeValue ([[UNMANAGED_ENUM]] : $@sil_unmanaged Optional) + // CANONICAL-NEXT: tuple + // CANONICAL-NEXT: return [[RESULT]] + // CANONICAL: } // end sil function '$s11unsafevalue11UnsafeValueV14unsafelyAssignACyxGxh_tcfC' + // + // OPT-LABEL: sil [transparent] @$s11unsafevalue11UnsafeValueV14unsafelyAssignACyxGxh_tcfC : $@convention(method) (@guaranteed Element, @thin UnsafeValue.Type) -> UnsafeValue { + // OPT: bb0([[INPUT_ELEMENT:%.*]] : $Element, + // OPT-NEXT: debug_value + // OPT-NEXT: [[ENUM:%.*]] = enum $Optional, #Optional.some!enumelt.1, [[INPUT_ELEMENT]] + // OPT-NEXT: [[UNMANAGED_ENUM:%.*]] = ref_to_unmanaged [[ENUM]] + // OPT-NEXT: [[RESULT:%.*]] = struct $UnsafeValue ([[UNMANAGED_ENUM]] : $@sil_unmanaged Optional) + // OPT-NEXT: return [[RESULT]] + // OPT: } // end sil function '$s11unsafevalue11UnsafeValueV14unsafelyAssignACyxGxh_tcfC' + @_transparent + @inlinable + public init(unsafelyAssign newValue: __shared Element) { + self.init() + Builtin.convertStrongToUnownedUnsafe(newValue, &_value) + } + + // Access the underlying value at +0, guaranteeing its lifetime by base. + @_transparent + @inlinable + func withGuaranteeingBase(base: Base, f: (Element) -> ()) { + // TODO: Once we have a builtin for mark_dependence, fill this in. + } + + // If the unmanaged value was initialized with a copy, release the underlying value. + // + // This will insert a release that can not be removed by the optimizer. + @_transparent + @inlinable + mutating func deinitialize() { + _value = nil + } + + // Return a new strong reference to the unmanaged value. + // + // This will insert a retain that can not be removed by the optimizer! + @_transparent + @inlinable + public var strongRef: Element { _value.unsafelyUnwrapped } +} From f7f98887d462873f8c6d2ef41011ab2f561bc657 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 6 Feb 2020 14:21:28 -0800 Subject: [PATCH 211/237] [builtin] Add a new SIL builtin convertUnownedUnsafeToGuaranteed() (BaseT, @inout @unowned(unsafe) T) -> @guaranteed T The reason for the weird signature is that currently the Builtin infrastructure does not handle results well. Also, note that we are not actually performing a call here. We are SILGening directly so we can create a guaranteed result. The intended semantics is that one passes in a base value that guarantees the lifetime of the unowned(unsafe) value. The builtin then: 1. Borrows the base. 2. Loads the trivial unowned (unsafe), converts that value to a guaranteed ref after unsafely unwrapping the optional. 3. Uses mark dependence to tie the lifetimes of the guaranteed base to the guaranteed ref. I also updated my small UnsafeValue.swift test to make sure we get the codegen we expect. --- include/swift/AST/Builtins.def | 19 ++++++++++++ lib/AST/Builtins.cpp | 19 ++++++++++++ lib/SILGen/SILGenBuilder.cpp | 9 ++++++ lib/SILGen/SILGenBuilder.h | 4 +++ lib/SILGen/SILGenBuiltin.cpp | 55 +++++++++++++++++++++++++++++++++- lib/SILGen/SILGenExpr.cpp | 24 +++++++++++++++ lib/SILGen/SILGenFunction.h | 5 ++++ test/SILGen/unsafevalue.swift | 42 ++++++++++++++++++++++++-- 8 files changed, 174 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 8c53dd6a31222..6925fd42d7703 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -450,6 +450,25 @@ BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special) /// signature is to work around issues with results in SILGen builtin emission. BUILTIN_SIL_OPERATION(ConvertStrongToUnownedUnsafe, "convertStrongToUnownedUnsafe", Special) +/// Unsafely convert a value of type @inout @unowned(unsafe) T to a loaded +/// guaranteed T value that has a lifetime guaranteed by the passed in base +/// value of type BaseTy. +/// +/// It has type (@in_guaranteed BaseTy, @in_guaranteed @unowned (unsafe) T) -> @guaranteed T. +/// +/// NOTE: Saying the result is a guaranteed T is a bit of a misnomer. We aren't +/// emitting a builtin call, but are just emitting SIL directly. +/// +/// NOTE: Even though the signature is as mentioned above, we actually tell the +/// AST we have the signature: +/// +/// (BaseT, T) -> U +/// +/// We then perform the actual type checking in SILGen and assert on +/// failure. This is an early, unsupported feature so this is sufficient for +/// now. +BUILTIN_SIL_OPERATION(ConvertUnownedUnsafeToGuaranteed, "convertUnownedUnsafeToGuaranteed", Special) + #undef BUILTIN_SIL_OPERATION // BUILTIN_RUNTIME_CALL - A call into a runtime function. diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index bc140b9422418..262f0222dcb77 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -956,6 +956,22 @@ static ValueDecl *getConvertStrongToUnownedUnsafe(ASTContext &ctx, return builder.build(id); } +static ValueDecl *getConvertUnownedUnsafeToGuaranteed(ASTContext &ctx, + Identifier id) { + // We actually want this: + // + /// (BaseT, @inout unowned(unsafe) T) -> T + // + // But for simplicity, we actually accept three generic params T, U and do the + // checking in the emission method that everything works up. This is a + // builtin, so we can crash. + BuiltinFunctionBuilder builder(ctx, 3); + builder.addParameter(makeGenericParam(0)); // Base + builder.addParameter(makeGenericParam(1), ValueOwnership::InOut); // Unmanaged + builder.setResult(makeGenericParam(2)); // Guaranteed Result + return builder.build(id); +} + static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) { auto int1Type = BuiltinIntegerType::get(1, Context); auto optionalRawPointerType = BoundGenericEnumType::get( @@ -2015,6 +2031,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::ConvertStrongToUnownedUnsafe: return getConvertStrongToUnownedUnsafe(Context, Id); + case BuiltinValueKind::ConvertUnownedUnsafeToGuaranteed: + return getConvertUnownedUnsafeToGuaranteed(Context, Id); + case BuiltinValueKind::PoundAssert: return getPoundAssert(Context, Id); diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index 4ba29af7d3abe..16738a419c7b5 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -844,3 +844,12 @@ ManagedValue SILGenBuilder::createProjectBox(SILLocation loc, ManagedValue mv, auto *pbi = createProjectBox(loc, mv.getValue(), index); return ManagedValue::forUnmanaged(pbi); } + +ManagedValue SILGenBuilder::createMarkDependence(SILLocation loc, + ManagedValue value, + ManagedValue base) { + CleanupCloner cloner(*this, value); + auto *mdi = createMarkDependence(loc, value.forward(getSILGenFunction()), + base.forward(getSILGenFunction())); + return cloner.clone(mdi); +} diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index 55d3dad837e00..585c53bed4cd6 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -368,6 +368,10 @@ class SILGenBuilder : public SILBuilder { using SILBuilder::createProjectBox; ManagedValue createProjectBox(SILLocation loc, ManagedValue mv, unsigned index); + + using SILBuilder::createMarkDependence; + ManagedValue createMarkDependence(SILLocation loc, ManagedValue value, + ManagedValue base); }; } // namespace Lowering diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 049647b4909ef..5f7a820f8017c 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -1087,7 +1087,7 @@ static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe( if (objectSrcValue->getType() != destType.getReferenceStorageReferentType().getOptionalObjectType()) { llvm::errs() - << "Invalid usage of Builtin.convertStrongToUnsafeUnowned. lhsType " + << "Invalid usage of Builtin.convertStrongToUnownedUnsafe. lhsType " "must be T and rhsType must be inout unsafe(unowned) Optional" << "lhsType: " << objectSrcValue->getType() << "\n" << "rhsType: " << inoutDest->getType() << "\n"; @@ -1109,6 +1109,59 @@ static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe( return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); } +/// Emit SIL for the named builtin: convertUnownedUnsafeToGuaranteed. +/// +/// We assume our convention is: +/// +/// (BaseT, @in_guaranteed @unmanaged Optional) -> @guaranteed T +/// +static ManagedValue emitBuiltinConvertUnownedUnsafeToGuaranteed( + SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs, + PreparedArguments &&preparedArgs, SGFContext C) { + auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2); + if (!argsOrError) + return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc)); + + auto args = *argsOrError; + + // First grab our base and borrow it. + auto baseMV = + SGF.emitRValueAsSingleValue(args[0], SGFContext::AllowGuaranteedPlusZero) + .borrow(SGF, args[0]); + + // Then grab our LValue operand, drop the last ownership component. + auto srcLV = SGF.emitLValue(args[1]->getSemanticsProvidingExpr(), + SGFAccessKind::BorrowedAddressRead); + srcLV.unsafelyDropLastComponent(PathComponent::OwnershipKind); + if (!srcLV.isPhysical() || !srcLV.isLoadingPure()) { + llvm::report_fatal_error("Builtin.convertUnownedUnsafeToGuaranteed passed " + "non-physical, non-pure lvalue as 2nd arg"); + } + + // Grab our address and load our unmanaged and convert it to a ref. + SILValue srcAddr = + SGF.emitAddressOfLValue(args[1], std::move(srcLV)).getLValueAddress(); + SILValue srcValue = SGF.B.emitLoadValueOperation( + loc, srcAddr, LoadOwnershipQualifier::Trivial); + SILValue unownedNonTrivialRef = SGF.B.createUnmanagedToRef( + loc, srcValue, srcValue->getType().getReferenceStorageReferentType()); + + // Now convert our unownedNonTrivialRef from unowned ownership to guaranteed + // ownership and create a cleanup for it. + SILValue guaranteedNonTrivialRef = SGF.B.createUncheckedOwnershipConversion( + loc, unownedNonTrivialRef, ValueOwnershipKind::Guaranteed); + auto guaranteedNonTrivialRefMV = + SGF.emitManagedBorrowedRValueWithCleanup(guaranteedNonTrivialRef); + auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); + + // Then unsafely extract from the optional. + auto extMV = + SGF.B.createUncheckedEnumData(loc, guaranteedNonTrivialRefMV, someDecl); + + // Now create a mark dependence on our base and return the result. + return SGF.B.createMarkDependence(loc, extMV, baseMV); +} + Optional SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) { // Only consider standalone declarations in the Builtin module. diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 0986ed1f33e46..897e448152545 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -297,6 +297,30 @@ SILGenFunction::emitManagedBorrowedRValueWithCleanup(SILValue original, return emitManagedBorrowedRValueWithCleanup(original, borrowed, lowering); } +ManagedValue +SILGenFunction::emitManagedBorrowedRValueWithCleanup(SILValue borrowed) { + auto &lowering = getTypeLowering(borrowed->getType()); + return emitManagedBorrowedRValueWithCleanup(borrowed, lowering); +} + +ManagedValue SILGenFunction::emitManagedBorrowedRValueWithCleanup( + SILValue borrowed, const TypeLowering &lowering) { + assert(lowering.getLoweredType().getObjectType() == + borrowed->getType().getObjectType()); + if (lowering.isTrivial()) + return ManagedValue::forUnmanaged(borrowed); + + if (borrowed->getType().isObject() && + borrowed.getOwnershipKind() == ValueOwnershipKind::None) + return ManagedValue::forUnmanaged(borrowed); + + if (borrowed->getType().isObject()) { + Cleanups.pushCleanup(borrowed); + } + + return ManagedValue(borrowed, CleanupHandle::invalid()); +} + ManagedValue SILGenFunction::emitManagedBorrowedRValueWithCleanup( SILValue original, SILValue borrowed, const TypeLowering &lowering) { assert(lowering.getLoweredType().getObjectType() == diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index f0a4c9d7646bf..caba5bbb129d3 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1338,6 +1338,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction const TypeLowering &lowering); ManagedValue emitManagedBeginBorrow(SILLocation loc, SILValue v); + ManagedValue + emitManagedBorrowedRValueWithCleanup(SILValue borrowedValue, + const TypeLowering &lowering); + ManagedValue emitManagedBorrowedRValueWithCleanup(SILValue borrowedValue); + ManagedValue emitManagedBorrowedRValueWithCleanup(SILValue original, SILValue borrowedValue); ManagedValue emitManagedBorrowedRValueWithCleanup( diff --git a/test/SILGen/unsafevalue.swift b/test/SILGen/unsafevalue.swift index 38e6fab868310..1d690a904ac65 100644 --- a/test/SILGen/unsafevalue.swift +++ b/test/SILGen/unsafevalue.swift @@ -78,10 +78,48 @@ public struct UnsafeValue { } // Access the underlying value at +0, guaranteeing its lifetime by base. + // + // CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF : $@convention(method) (@in_guaranteed Base, @noescape @callee_guaranteed (@guaranteed Element) -> @out Result, UnsafeValue) -> @out Result { + // CHECK: bb0([[RESULT:%.*]] : $*Result, [[BASE:%.*]] : $*Base, [[CLOSURE:%.*]] : $@noescape @callee_guaranteed (@guaranteed Element) -> @out Result, [[UNSAFE_VALUE:%.*]] : $UnsafeValue): + // CHECK: [[COPY_BOX:%.*]] = alloc_box + // CHECK: [[COPY_PROJ:%.*]] = project_box [[COPY_BOX]] + // CHECK: store [[UNSAFE_VALUE]] to [trivial] [[COPY_PROJ]] + // CHECK: [[VALUE_ADDR:%.*]] = begin_access [read] [unknown] [[COPY_PROJ]] + // CHECK: [[STR_VALUE_ADDR:%.*]] = struct_element_addr [[VALUE_ADDR]] + // CHECK: [[UNMANAGED_VALUE:%.*]] = load [trivial] [[STR_VALUE_ADDR]] + // CHECK: [[UNOWNED_REF_OPTIONAL:%.*]] = unmanaged_to_ref [[UNMANAGED_VALUE]] + // CHECK: [[GUARANTEED_REF_OPTIONAL:%.*]] = unchecked_ownership_conversion [[UNOWNED_REF_OPTIONAL]] + // CHECK: [[GUARANTEED_REF:%.*]] = unchecked_enum_data [[GUARANTEED_REF_OPTIONAL]] + // CHECK: [[GUARANTEED_REF_DEP_ON_BASE:%.*]] = mark_dependence [[GUARANTEED_REF]] : $Element on [[BASE]] + // CHECK: end_access [[VALUE_ADDR]] + // CHECK: apply [[CLOSURE]]([[RESULT]], [[GUARANTEED_REF_DEP_ON_BASE]]) + // CHECK: end_borrow [[GUARANTEED_REF_OPTIONAL]] + // CHECK: destroy_value [[COPY_BOX]] + // CHECK: } // end sil function '$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF' + // + // CANONICAL-LABEL: sil [transparent] [serialized] @$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF : $@convention(method) (@in_guaranteed Base, @noescape @callee_guaranteed (@guaranteed Element) -> @out Result, UnsafeValue) -> @out Result { + // CANONICAL: bb0([[RESULT:%.*]] : $*Result, [[BASE:%.*]] : $*Base, [[CLOSURE:%.*]] : $@noescape @callee_guaranteed (@guaranteed Element) -> @out Result, [[UNSAFE_VALUE:%.*]] : $UnsafeValue): + // CANONICAL: [[UNMANAGED_VALUE:%.*]] = struct_extract [[UNSAFE_VALUE]] + // CANONICAL: [[UNOWNED_REF_OPTIONAL:%.*]] = unmanaged_to_ref [[UNMANAGED_VALUE]] + // CANONICAL: [[GUARANTEED_REF:%.*]] = unchecked_enum_data [[UNOWNED_REF_OPTIONAL]] + // CANONICAL: [[GUARANTEED_REF_DEP_ON_BASE:%.*]] = mark_dependence [[GUARANTEED_REF]] : $Element on [[BASE]] + // CANONICAL: apply [[CLOSURE]]([[RESULT]], [[GUARANTEED_REF_DEP_ON_BASE]]) + // CANONICAL: } // end sil function '$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF' + // + // OPT-LABEL: sil [transparent] @$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF : $@convention(method) (@in_guaranteed Base, @noescape @callee_guaranteed (@guaranteed Element) -> @out Result, UnsafeValue) -> @out Result { + // OPT: bb0([[RESULT:%.*]] : $*Result, [[BASE:%.*]] : $*Base, [[CLOSURE:%.*]] : $@noescape @callee_guaranteed (@guaranteed Element) -> @out Result, [[UNSAFE_VALUE:%.*]] : $UnsafeValue): + // OPT: [[UNMANAGED_VALUE:%.*]] = struct_extract [[UNSAFE_VALUE]] + // OPT: [[UNOWNED_REF_OPTIONAL:%.*]] = unmanaged_to_ref [[UNMANAGED_VALUE]] + // OPT: [[GUARANTEED_REF:%.*]] = unchecked_enum_data [[UNOWNED_REF_OPTIONAL]] + // OPT: [[GUARANTEED_REF_DEP_ON_BASE:%.*]] = mark_dependence [[GUARANTEED_REF]] : $Element on [[BASE]] + // OPT: apply [[CLOSURE]]([[RESULT]], [[GUARANTEED_REF_DEP_ON_BASE]]) + // OPT: } // end sil function '$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF' @_transparent @inlinable - func withGuaranteeingBase(base: Base, f: (Element) -> ()) { - // TODO: Once we have a builtin for mark_dependence, fill this in. + func withGuaranteeingBase(base: Base, f: (Element) -> Result) -> Result { + // Just so we can not mark self as mutating. This is just a bitwise copy. + var tmp = self + return f(Builtin.convertUnownedUnsafeToGuaranteed(base, &tmp._value)) } // If the unmanaged value was initialized with a copy, release the underlying value. From e932580c8ea215c263e41084a4e12c759166a8c9 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 6 Feb 2020 17:33:47 -0800 Subject: [PATCH 212/237] Emit debug info for generic type aliases. Before comparing the potential sugared type for equality is needs to be mapped into the context to resolve generic type parameters to primary archetypes. --- lib/IRGen/DebugTypeInfo.cpp | 29 +++++++++++-------- lib/IRGen/IRGenSIL.cpp | 6 ++-- .../bound-generic-struct-extension.swift | 28 ++++++++++++++++++ test/DebugInfo/generic-typealias.swift | 19 ++++++++++++ 4 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 test/DebugInfo/bound-generic-struct-extension.swift create mode 100644 test/DebugInfo/generic-typealias.swift diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index 34fa19bd537f0..51e1868b76d66 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -60,20 +60,25 @@ DebugTypeInfo DebugTypeInfo::getFromTypeInfo(swift::Type Ty, DebugTypeInfo DebugTypeInfo::getLocalVariable(VarDecl *Decl, swift::Type Ty, const TypeInfo &Info) { - - auto DeclType = Decl->getInterfaceType(); - auto RealType = Ty; - - // DynamicSelfType is also sugar as far as debug info is concerned. - auto Sugared = DeclType; - if (auto DynSelfTy = DeclType->getAs()) - Sugared = DynSelfTy->getSelfType(); - // Prefer the original, potentially sugared version of the type if // the type hasn't been mucked with by an optimization pass. - auto *Type = Sugared->isEqual(RealType) ? DeclType.getPointer() - : RealType.getPointer(); - return getFromTypeInfo(Type, Info); + swift::Type DeclType = Decl->getInterfaceType(); + swift::Type RealType = Ty; + + swift::Type DebugType; + if (auto DynSelfTy = DeclType->getAs()) { + // DynamicSelfType is also sugar as far as debug info is concerned. + auto DesugaredSelf = DynSelfTy->getSelfType(); + DebugType = DesugaredSelf->isEqual(RealType) ? DynSelfTy : RealType; + } else { + // Map the sugared type into the context to resolve bound generics and + // generic type aliases. + DeclContext *DeclCtx = Decl->getDeclContext(); + swift::Type Sugared = + DeclCtx ? DeclCtx->mapTypeIntoContext(DeclType) : DeclType; + DebugType = Sugared->isEqual(RealType) ? Sugared : RealType; + } + return getFromTypeInfo(DebugType, Info); } DebugTypeInfo DebugTypeInfo::getMetadata(swift::Type Ty, llvm::Type *StorageTy, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 7292f9e0160ca..81d885aaeac80 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3660,7 +3660,7 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { llvm::SmallVector Copy; emitShadowCopyIfNeeded(SILVal, i->getDebugScope(), *VarInfo, IsAnonymous, Copy); - bindArchetypes(DbgTy.getType()); + bindArchetypes(RealTy); if (!IGM.DebugInfo) return; @@ -3691,7 +3691,7 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) { auto DbgTy = DebugTypeInfo::getLocalVariable( Decl, RealType, getTypeInfo(SILVal->getType())); - bindArchetypes(DbgTy.getType()); + bindArchetypes(RealType); if (!IGM.DebugInfo) return; @@ -3991,7 +3991,7 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, auto RealType = SILTy.getASTType(); auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type); - bindArchetypes(DbgTy.getType()); + bindArchetypes(RealType); if (IGM.DebugInfo) emitDebugVariableDeclaration(addr, DbgTy, SILTy, DS, Decl, *VarInfo, Indirection); diff --git a/test/DebugInfo/bound-generic-struct-extension.swift b/test/DebugInfo/bound-generic-struct-extension.swift new file mode 100644 index 0000000000000..ec6e13aa235f2 --- /dev/null +++ b/test/DebugInfo/bound-generic-struct-extension.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s + +// Test that a bound generic type is fully resolved in the debug info. + +public protocol P {} + +public struct S : P { + var x: Int +} +// This is significant, it must be bound to S: main.BoundGeneric +// CHECK-DAG: ![[S:[0-9]+]] = !DICompositeType({{.*}}identifier: "$s4main12BoundGenericVyAA1SVGD") + +public extension BoundGeneric where T == S { + func f() { +// CHECK-DAG: !DILocalVariable(name: "self", arg: 1,{{.*}} line: [[@LINE-1]],{{.*}} type: ![[C_BGS:[0-9]+]], +// CHECK-DAG: ![[C_BGS]] = !DIDerivedType(tag: DW_TAG_const_type,{{.*}} baseType: ![[BGS:[0-9]+]]) +// CHECK-DAG: ![[BGS]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}} elements: ![[ELTS:[0-9]+]], +// CHECK-DAG: ![[ELTS]] = !{![[MEMBER:[0-9]+]]} +// CHECK-DAG: ![[MEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[S]], + } +} + +public struct BoundGeneric where T : P { + let x : T +} + +public let pat = BoundGeneric(x: S(x: 23)) +pat.f() diff --git a/test/DebugInfo/generic-typealias.swift b/test/DebugInfo/generic-typealias.swift new file mode 100644 index 0000000000000..3b4c4399e4008 --- /dev/null +++ b/test/DebugInfo/generic-typealias.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s + +// Test that a generic type alias is represented in the debug info. + +public struct S { + public typealias Alias = (T, T) + // CHECK: ![[T_T:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sx_xtD" + public let member : Alias + public func f(t : Alias) -> Alias { return t } + // CHECK: !DILocalVariable(name: "t", arg: 1,{{.*}} line: [[@LINE-1]], + // CHECK-SAME: type: ![[C_ALIAS:[0-9]+]]) + // CHECK: ![[C_ALIAS]] = !DIDerivedType(tag: DW_TAG_const_type, + // CHECK-SAME: baseType: ![[ALIAS:[0-9]+]]) + // CHECK: ![[ALIAS]] = !DIDerivedType(tag: DW_TAG_typedef, name: "$ + // CHECK-SAME: baseType: ![[T_T]]) + +} + +public let s = S(member: (4, 2)) From a17424315983c7c80cfe2d6618ef0de1f40f2cc3 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Fri, 7 Feb 2020 14:10:34 -0800 Subject: [PATCH 213/237] [AutoDiff upstream] Add SIL differentiability witness IRGen. (#29704) SIL differentiability witnesses are a new top-level SIL construct mapping an "original" SIL function and derivative configuration to derivative SIL functions. This patch adds `SILDifferentiabilityWitness` IRGen. `SILDifferentiabilityWitness` has a fixed `{ i8*, i8* }` layout: JVP and VJP derivative function pointers. Resolves TF-1146. --- include/swift/AST/PrettyStackTrace.h | 18 +++++++ include/swift/IRGen/Linking.h | 24 +++++++++ lib/AST/AutoDiff.cpp | 12 +++++ lib/AST/PrettyStackTrace.cpp | 15 ++++++ lib/IRGen/CMakeLists.txt | 1 + lib/IRGen/GenDecl.cpp | 23 +++++++- lib/IRGen/GenDiffWitness.cpp | 54 +++++++++++++++++++ lib/IRGen/IRGenModule.cpp | 3 ++ lib/IRGen/IRGenModule.h | 8 +++ lib/IRGen/Linking.cpp | 14 +++++ .../sil_differentiability_witness.sil | 27 +++++++++- 11 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 lib/IRGen/GenDiffWitness.cpp diff --git a/include/swift/AST/PrettyStackTrace.h b/include/swift/AST/PrettyStackTrace.h index 0714bd6c83e96..350526d26e3e6 100644 --- a/include/swift/AST/PrettyStackTrace.h +++ b/include/swift/AST/PrettyStackTrace.h @@ -202,6 +202,24 @@ class PrettyStackTraceSelector : public llvm::PrettyStackTraceEntry { void print(llvm::raw_ostream &OS) const override; }; +/// PrettyStackTraceDifferentiabilityWitness - Observe that we are processing a +/// specific differentiability witness. +class PrettyStackTraceDifferentiabilityWitness + : public llvm::PrettyStackTraceEntry { + const SILDifferentiabilityWitnessKey Key; + const char *Action; + +public: + PrettyStackTraceDifferentiabilityWitness( + const char *action, const SILDifferentiabilityWitnessKey key) + : Key(key), Action(action) {} + virtual void print(llvm::raw_ostream &OS) const; +}; + +void printDifferentiabilityWitnessDescription( + llvm::raw_ostream &out, const SILDifferentiabilityWitnessKey key, + bool addNewline = true); + } // end namespace swift #endif diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 56b76c07be474..21acb1124cc5f 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -346,6 +346,9 @@ class LinkEntity { /// ProtocolConformance*. ProtocolWitnessTableLazyCacheVariable, + /// A SIL differentiability witness. + DifferentiabilityWitness, + // Everything following this is a type kind. /// A value witness for a type. @@ -535,6 +538,14 @@ class LinkEntity { return getAssociatedConformanceByIndex(conformance->getProtocol(), index); } + void + setForDifferentiabilityWitness(Kind kind, + const SILDifferentiabilityWitness *witness) { + Pointer = const_cast(static_cast(witness)); + SecondaryPointer = nullptr; + Data = LINKENTITY_SET_FIELD(Kind, unsigned(kind)); + } + void setForType(Kind kind, CanType type) { assert(isTypeKind(kind)); Pointer = type.getPointer(); @@ -835,6 +846,14 @@ class LinkEntity { return entity; } + static LinkEntity + forDifferentiabilityWitness(const SILDifferentiabilityWitness *witness) { + LinkEntity entity; + entity.setForDifferentiabilityWitness(Kind::DifferentiabilityWitness, + witness); + return entity; + } + static LinkEntity forProtocolWitnessTable(const RootProtocolConformance *C) { LinkEntity entity; entity.setForProtocolConformance(Kind::ProtocolWitnessTable, C); @@ -1043,6 +1062,11 @@ class LinkEntity { return reinterpret_cast(Pointer); } + SILDifferentiabilityWitness *getSILDifferentiabilityWitness() const { + assert(getKind() == Kind::DifferentiabilityWitness); + return reinterpret_cast(Pointer); + } + const RootProtocolConformance *getRootProtocolConformance() const { assert(isRootProtocolConformanceKind(getKind())); return cast(getProtocolConformance()); diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp index dbfd72bc1f626..c1d20b10c86e3 100644 --- a/lib/AST/AutoDiff.cpp +++ b/lib/AST/AutoDiff.cpp @@ -17,6 +17,18 @@ using namespace swift; +void AutoDiffConfig::print(llvm::raw_ostream &s) const { + s << "(parameters="; + parameterIndices->print(s); + s << " results="; + resultIndices->print(s); + if (derivativeGenericSignature) { + s << " where="; + derivativeGenericSignature->print(s); + } + s << ')'; +} + // TODO(TF-874): This helper is inefficient and should be removed. Unwrapping at // most once (for curried method types) is sufficient. static void unwrapCurryLevels(AnyFunctionType *fnTy, diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index 750e661c66af1..837027427fb1e 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -273,3 +273,18 @@ void PrettyStackTraceGenericSignature::print(llvm::raw_ostream &out) const { void PrettyStackTraceSelector::print(llvm::raw_ostream &out) const { out << "While " << Action << " '" << Selector << "'"; } + +void PrettyStackTraceDifferentiabilityWitness::print( + llvm::raw_ostream &out) const { + out << "While " << Action << ' '; + printDifferentiabilityWitnessDescription(out, Key); +} + +void swift::printDifferentiabilityWitnessDescription( + llvm::raw_ostream &out, const SILDifferentiabilityWitnessKey key, + bool addNewline) { + out << key.first << " "; + key.second.print(out); + if (addNewline) + out << '\n'; +} diff --git a/lib/IRGen/CMakeLists.txt b/lib/IRGen/CMakeLists.txt index f7276a871cfb7..1d3563accaa99 100644 --- a/lib/IRGen/CMakeLists.txt +++ b/lib/IRGen/CMakeLists.txt @@ -16,6 +16,7 @@ add_swift_host_library(swiftIRGen STATIC GenControl.cpp GenCoverage.cpp GenDecl.cpp + GenDiffWitness.cpp GenEnum.cpp GenExistential.cpp GenFunc.cpp diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 440963c22d129..fbefa083a58a1 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1073,7 +1073,21 @@ void IRGenerator::emitGlobalTopLevel(llvm::StringSet<> *linkerDirectives) { CurrentIGMPtr IGM = getGenModule(prop.getDecl()->getInnermostDeclContext()); IGM->emitSILProperty(&prop); } - + + // Emit differentiability witnesses. + for (auto &dw : + PrimaryIGM->getSILModule().getDifferentiabilityWitnessList()) { + // Emit into same IRGenModule as the original function. + // NOTE(TF-894): Investigate whether `getGenModule(dw.getVJP())` is + // significant/desirable; `getGenModule` seems relevant for multi-threaded + // compilation. When the differentiation transform canonicalizes all + // differentiability witnesses to have JVP/VJP functions, we can assert + // that JVP/VJP functions exist and use `getGenModule(dw.getVJP())`. + CurrentIGMPtr IGM = getGenModule(dw.getOriginalFunction()); + + IGM->emitSILDifferentiabilityWitness(&dw); + } + // Emit code coverage mapping data. PrimaryIGM->emitCoverageMapping(); @@ -4495,6 +4509,13 @@ IRGenModule::getAddrOfWitnessTablePattern(const NormalProtocolConformance *conf, return getAddrOfLLVMVariable(entity, definition, DebugTypeInfo()); } +/// Look up the address of a differentiability witness. +llvm::Constant *IRGenModule::getAddrOfDifferentiabilityWitness( + const SILDifferentiabilityWitness *witness, ConstantInit definition) { + auto entity = LinkEntity::forDifferentiabilityWitness(witness); + return getAddrOfLLVMVariable(entity, definition, DebugTypeInfo()); +} + llvm::Function * IRGenModule::getAddrOfAssociatedTypeWitnessTableAccessFunction( const NormalProtocolConformance *conformance, diff --git a/lib/IRGen/GenDiffWitness.cpp b/lib/IRGen/GenDiffWitness.cpp new file mode 100644 index 0000000000000..f27a5861a8840 --- /dev/null +++ b/lib/IRGen/GenDiffWitness.cpp @@ -0,0 +1,54 @@ +//===--- GenDiffWitness.cpp - IRGen for differentiability witnesses -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements IR generation for SIL differentiability witnesses. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/PrettyStackTrace.h" +#include "swift/SIL/SILDifferentiabilityWitness.h" + +#include "ConstantBuilder.h" +#include "IRGenModule.h" + +using namespace swift; +using namespace irgen; + +void IRGenModule::emitSILDifferentiabilityWitness( + SILDifferentiabilityWitness *dw) { + PrettyStackTraceDifferentiabilityWitness _st( + "emitting differentiability witness for", dw->getKey()); + + // Don't emit declarations. + if (dw->isDeclaration()) + return; + + // Don't emit `public_external` witnesses. + if (dw->getLinkage() == SILLinkage::PublicExternal) + return; + + ConstantInitBuilder builder(*this); + auto diffWitnessContents = builder.beginStruct(); + + assert(dw->getJVP() && + "Differentiability witness definition should have JVP"); + assert(dw->getVJP() && + "Differentiability witness definition should have VJP"); + + diffWitnessContents.addBitCast( + getAddrOfSILFunction(dw->getJVP(), NotForDefinition), Int8PtrTy); + diffWitnessContents.addBitCast( + getAddrOfSILFunction(dw->getVJP(), NotForDefinition), Int8PtrTy); + + getAddrOfDifferentiabilityWitness( + dw, diffWitnessContents.finishAndCreateFuture()); +} diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index dc7084b85a9cc..eee416bdee441 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -524,6 +524,9 @@ IRGenModule::IRGenModule(IRGenerator &irgen, DynamicReplacementKeyTy = createStructType(*this, "swift.dyn_repl_key", {RelativeAddressTy, Int32Ty}); + + DifferentiabilityWitnessTy = createStructType( + *this, "swift.differentiability_witness", {Int8PtrTy, Int8PtrTy}); } IRGenModule::~IRGenModule() { diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 87e948c7aa176..da2cfa028f4c4 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -95,6 +95,7 @@ namespace swift { class RootProtocolConformance; struct SILDeclRef; class SILDefaultWitnessTable; + class SILDifferentiabilityWitness; class SILGlobalVariable; class SILModule; class SILProperty; @@ -672,6 +673,8 @@ class IRGenModule { *DynamicReplacementLinkEntryPtrTy; // %link_entry* llvm::StructType *DynamicReplacementKeyTy; // { i32, i32} + llvm::StructType *DifferentiabilityWitnessTy; // { i8*, i8* } + llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr; /// Used to create unique names for class layout types with tail allocated @@ -1272,6 +1275,7 @@ private: \ void emitSILFunction(SILFunction *f); void emitSILWitnessTable(SILWitnessTable *wt); void emitSILProperty(SILProperty *prop); + void emitSILDifferentiabilityWitness(SILDifferentiabilityWitness *dw); void emitSILStaticInitializers(); llvm::Constant *emitFixedTypeLayout(CanType t, const FixedTypeInfo &ti); void emitProtocolConformance(const ConformanceDescription &record); @@ -1463,6 +1467,10 @@ private: \ llvm::Function *getAddrOfDefaultAssociatedConformanceAccessor( AssociatedConformance requirement); + llvm::Constant * + getAddrOfDifferentiabilityWitness(const SILDifferentiabilityWitness *witness, + ConstantInit definition = ConstantInit()); + Address getAddrOfObjCISAMask(); /// Retrieve the generic signature for the current generic context, or null if no diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 19cb5949d15aa..48fd8f1c1440b 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -414,6 +414,10 @@ std::string LinkEntity::mangleAsString() const { case Kind::ReflectionAssociatedTypeDescriptor: return mangler.mangleReflectionAssociatedTypeDescriptor( getProtocolConformance()); + case Kind::DifferentiabilityWitness: + return mangler.mangleSILDifferentiabilityWitnessKey( + {getSILDifferentiabilityWitness()->getOriginalFunction()->getName(), + getSILDifferentiabilityWitness()->getConfig()}); } llvm_unreachable("bad entity kind!"); } @@ -659,6 +663,8 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::ExtensionDescriptor: case Kind::AnonymousDescriptor: return SILLinkage::Shared; + case Kind::DifferentiabilityWitness: + return getSILDifferentiabilityWitness()->getLinkage(); } llvm_unreachable("bad link entity kind"); } @@ -783,6 +789,9 @@ bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const { ->getDeclContext() ->getInnermostTypeContext()); } + + case Kind::DifferentiabilityWitness: + return true; case Kind::ObjCMetadataUpdateFunction: case Kind::ObjCResilientClassStub: @@ -904,6 +913,8 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const { return IGM.ObjCResilientClassStubTy; } llvm_unreachable("invalid metadata address"); + case Kind::DifferentiabilityWitness: + return IGM.DifferentiabilityWitnessTy; default: llvm_unreachable("declaration LLVM type not specified"); } @@ -951,6 +962,7 @@ Alignment LinkEntity::getAlignment(IRGenModule &IGM) const { case Kind::OpaqueTypeDescriptorAccessorKey: case Kind::OpaqueTypeDescriptorAccessorVar: case Kind::ObjCResilientClassStub: + case Kind::DifferentiabilityWitness: return IGM.getPointerAlignment(); case Kind::TypeMetadataDemanglingCacheVariable: return Alignment(8); @@ -1052,6 +1064,7 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { case Kind::ReflectionBuiltinDescriptor: case Kind::ReflectionFieldDescriptor: case Kind::CoroutineContinuationPrototype: + case Kind::DifferentiabilityWitness: return false; } @@ -1181,6 +1194,7 @@ const SourceFile *LinkEntity::getSourceFileForEmission() const { case Kind::ReflectionBuiltinDescriptor: case Kind::ValueWitness: case Kind::ValueWitnessTable: + case Kind::DifferentiabilityWitness: return nullptr; } diff --git a/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil b/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil index 75cc1d803311d..acdfd9e2225f6 100644 --- a/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil +++ b/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil @@ -7,11 +7,14 @@ // RUN: %empty-directory(%t) // RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name main // RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.sil -module-name main - // NOTE(SR-12090): Workaround because import declarations are not preserved in .sib files. // RUN: sed -e 's/import Swift$/import Swift; import _Differentiation/' %t/tmp.sil > %t/tmp_fixed.sil // RUN: %target-sil-opt %t/tmp_fixed.sil -module-name main -emit-sorted-sil | %FileCheck --check-prefix=ROUNDTRIP %s +// IRGen test. + +// RUN: %target-swift-frontend -emit-ir %s | %FileCheck --check-prefix=IRGEN %s + // REQUIRES: differentiable_programming // NOTE(SR-12090): `shell` is required only to run `sed` as a SR-12090 workaround. // REQUIRES: shell @@ -49,6 +52,11 @@ sil_differentiability_witness [parameters 0] [results 0] @externalFn1 : $@conven // ROUNDTRIP: vjp: @AD__externalFn1__vjp_src_0_wrt_0 : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) // ROUNDTRIP: } +// IRGEN-LABEL: @AD__externalFn1_PSRS ={{( protected)?}} global { i8*, i8* } { +// IRGEN-SAME: @AD__externalFn1__jvp_src_0_wrt_0 +// IRGEN-SAME: @AD__externalFn1__vjp_src_0_wrt_0 +// IRGEN-SAME: } + // Test SIL differentiability witness for bodiless original function, with bodiless jvp/vjp. sil @externalFn2 : $@convention(thin) (Float) -> Float @@ -68,6 +76,11 @@ sil_differentiability_witness [parameters 0] [results 0] @externalFn2 : $@conven // ROUNDTRIP: vjp: @AD__externalFn2__vjp_src_0_wrt_0 : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) // ROUNDTRIP: } +// IRGEN-LABEL: @AD__externalFn2_PSRS ={{( protected)?}} global { i8*, i8* } { +// IRGEN-SAME: @AD__externalFn2__jvp_src_0_wrt_0 +// IRGEN-SAME: @AD__externalFn2__vjp_src_0_wrt_0 +// IRGEN-SAME: } + // Test SIL differentiability witness declaration. sil @externalFn3 : $@convention(thin) (Float) -> Float @@ -77,6 +90,8 @@ sil_differentiability_witness [parameters 0] [results 0] @externalFn3 : $@conven // ROUNDTRIP-LABEL: // differentiability witness for externalFn3 // ROUNDTRIP: sil_differentiability_witness{{( public_external)?}} [parameters 0] [results 0] @externalFn3 : $@convention(thin) (Float) -> Float{{[^{]*$}} +// IRGEN-NOT: @AD__externalFn3{{.*}}={{.*}}{ i8*, i8* } + // Test public non-generic function. // SIL differentiability witness: // - Has public linkage (implicit). @@ -108,6 +123,11 @@ sil_differentiability_witness [parameters 0] [results 0] @foo : $@convention(thi // ROUNDTRIP: vjp: @AD__foo__vjp_src_0_wrt_0 : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) // ROUNDTRIP: } +// IRGEN-LABEL: @AD__foo_PSRS ={{( protected)?}} global { i8*, i8* } { +// IRGEN-SAME: @AD__foo__jvp_src_0_wrt_0 +// IRGEN-SAME: @AD__foo__vjp_src_0_wrt_0 +// IRGEN-SAME: } + // Test internal generic function. // SIL differentiability witness: // - Has hidden linkage. @@ -140,3 +160,8 @@ sil_differentiability_witness hidden [parameters 0 1] [results 0] <τ_0_0 where // ROUNDTRIP: jvp: @AD__generic__jvp_src_0_wrt_0_1 : $@convention(thin) <τ_0_0 where τ_0_0 : Differentiable> (@in_guaranteed τ_0_0, Float) -> (@out τ_0_0, @owned @callee_guaranteed (@in_guaranteed τ_0_0.TangentVector, Float) -> @out τ_0_0.TangentVector) // ROUNDTRIP: vjp: @AD__generic__vjp_src_0_wrt_0_1 : $@convention(thin) <τ_0_0 where τ_0_0 : Differentiable> (@in_guaranteed τ_0_0, Float) -> (@out τ_0_0, @owned @callee_guaranteed (@in_guaranteed τ_0_0.TangentVector) -> (@out τ_0_0.TangentVector, Float)) // ROUNDTRIP: } + +// IRGEN-LABEL: @AD__generic_PSSRS16_Differentiation14DifferentiableRzl = hidden global { i8*, i8* } { +// IRGEN-SAME: @AD__generic__jvp_src_0_wrt_0_1 +// IRGEN-SAME: @AD__generic__vjp_src_0_wrt_0_1 +// IRGEN-SAME: } From f56fee839b047022d4073cf21db86c69494a5c08 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 7 Feb 2020 12:04:46 -0800 Subject: [PATCH 214/237] [ModuleInterface] Pass Through Flags for Nested Types Tables When a swift module is generated from a swift interface file, we must remember to setup the nested types tables. Plumb the flag down from the frontend options. In the future, we must remove the ability to turn this off. There's literally zero reason to have this be a configuration option anymore. Resolves rdar://59202687 and its many, many dupes. --- lib/Frontend/ModuleInterfaceBuilder.cpp | 2 ++ lib/Serialization/ModuleFormat.h | 2 +- .../interface-module-nested-types.swift | 35 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/Serialization/interface-module-nested-types.swift diff --git a/lib/Frontend/ModuleInterfaceBuilder.cpp b/lib/Frontend/ModuleInterfaceBuilder.cpp index 2f7a40f255c21..fbbd92d99a095 100644 --- a/lib/Frontend/ModuleInterfaceBuilder.cpp +++ b/lib/Frontend/ModuleInterfaceBuilder.cpp @@ -347,6 +347,8 @@ bool ModuleInterfaceBuilder::buildSwiftModuleInternal( // optimization pipeline. SerializationOptions SerializationOpts; std::string OutPathStr = OutPath; + SerializationOpts.EnableNestedTypeLookupTable + = FEOpts.EnableSerializationNestedTypeLookupTable; SerializationOpts.OutputPath = OutPathStr.c_str(); SerializationOpts.ModuleLinkName = FEOpts.ModuleLinkName; SerializationOpts.AutolinkForceLoad = diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 6c223c620fa77..ffd1186ca73e2 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 = 537; // SIL differentiability witnesses +const uint16_t SWIFTMODULE_VERSION_MINOR = 538; // swiftmodules from swiftinterfaces have nested types tables /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/test/Serialization/interface-module-nested-types.swift b/test/Serialization/interface-module-nested-types.swift new file mode 100644 index 0000000000000..1342498dc0702 --- /dev/null +++ b/test/Serialization/interface-module-nested-types.swift @@ -0,0 +1,35 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/ModuleCache) +// RUN: %empty-directory(%t/Build) +// RUN: %empty-directory(%t/PrebuiltCache) + +// 1. Create a module for our nested type +// RUN: echo 'public protocol Nest { associatedtype Egg }' > %t/TestModule.swift + +// 2. Create an interface for it +// RUN: %target-swift-frontend -typecheck %t/TestModule.swift -emit-module-interface-path %t/Build/TestModule.swiftinterface -swift-version 5 + +// 3. Build a .swiftmodule from the .swiftinterface and put it in the module cache +// RUN: %target-swift-frontend -compile-module-from-interface %t/Build/TestModule.swiftinterface -o %t/PrebuiltCache/TestModule.swiftmodule + +// 4. Import the module in a different module that extends the nested type. +// RUN: echo 'import TestModule; extension Nest where Egg == Int { public func robin() -> Egg { return 3 } }' > %t/NestModule.swift + +// 5. Create an interface for it +// RUN: %target-swift-frontend -typecheck %t/NestModule.swift -I %t/PrebuiltCache -sdk %t -prebuilt-module-cache-path %t/PrebuiltCache -module-cache-path %t/ModuleCache -emit-module-interface-path %t/Build/NestModule.swiftinterface -swift-version 5 + +// 6. Build a .swiftmodule from the .swiftinterface and put it in the module cache +// RUN: %target-swift-frontend -compile-module-from-interface -I %t/PrebuiltCache -sdk %t %t/Build/NestModule.swiftinterface -o %t/PrebuiltCache/NestModule.swiftmodule + +// 7. Ensure we resolve the cross-ref to the nested type properly. +// RUN: %target-swift-frontend -typecheck %s -I %t/PrebuiltCache -sdk %t -prebuilt-module-cache-path %t/PrebuiltCache -module-cache-path %t/ModuleCache -print-stats 2>&1 | %FileCheck %s + +import TestModule +import NestModule + +// CHECK: Statistics +// CHECK: 1 Serialization - # of nested types resolved without full lookup + +func tweet(from place: Location) where Location.Egg == Int { + _ = place.robin() +} From 6093b79efd329110de3d2499550f0ab22f5e1767 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 7 Feb 2020 14:44:25 -0800 Subject: [PATCH 215/237] test: loosen the differentiability witness test There is currently a difference between the tensorflow branch and the master branch. On tensorflow, the differentiability support is merged into the standard library. This changes the decoration of the witness. Loosen the test to accept either. We should change the tensorflow branch to generate the `_Differentiation` module with the support and then auto-import the module in the longer term. This can be gated by the `-enable-experimental-autodifferentiation` flag to the driver to gain the same behaviour on both the branches. --- .../SIL/Serialization/sil_differentiability_witness.sil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil b/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil index acdfd9e2225f6..4db87196b6573 100644 --- a/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil +++ b/test/AutoDiff/SIL/Serialization/sil_differentiability_witness.sil @@ -161,7 +161,7 @@ sil_differentiability_witness hidden [parameters 0 1] [results 0] <τ_0_0 where // ROUNDTRIP: vjp: @AD__generic__vjp_src_0_wrt_0_1 : $@convention(thin) <τ_0_0 where τ_0_0 : Differentiable> (@in_guaranteed τ_0_0, Float) -> (@out τ_0_0, @owned @callee_guaranteed (@in_guaranteed τ_0_0.TangentVector) -> (@out τ_0_0.TangentVector, Float)) // ROUNDTRIP: } -// IRGEN-LABEL: @AD__generic_PSSRS16_Differentiation14DifferentiableRzl = hidden global { i8*, i8* } { +// IRGEN: @AD__generic_PSSRS{{16_Differentiation|s}}14DifferentiableRzl = hidden global { i8*, i8* } { // IRGEN-SAME: @AD__generic__jvp_src_0_wrt_0_1 // IRGEN-SAME: @AD__generic__vjp_src_0_wrt_0_1 // IRGEN-SAME: } From 9a8f55ae61793e3edc6e4d3c0b1924edce10ee14 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 7 Feb 2020 15:26:22 -0800 Subject: [PATCH 216/237] CI: use verbose mode during the CI runs Enable verbose mode execution for the build script in the hopes that we can catch the issue that is preventing the builds on new hosts. --- utils/build-windows.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 4fd36ccb2f264..b9363684ab85d 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -21,7 +21,7 @@ :: The user will need permission to write files into the Windows SDK and the :: VisualC++ folder. -@echo off +:: @echo off setlocal enableextensions enabledelayedexpansion From 134cab61566017bff1e4fe985086cb5ce60a77cf Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 7 Feb 2020 00:42:53 -0500 Subject: [PATCH 217/237] Add regression test for https://bugs.swift.org/browse/SR-9225 --- .../Inputs/sr9225-other.swift | 10 ++++++++++ .../compiler_crashers_2_fixed/sr9225.swift | 15 +++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 validation-test/compiler_crashers_2_fixed/Inputs/sr9225-other.swift create mode 100644 validation-test/compiler_crashers_2_fixed/sr9225.swift diff --git a/validation-test/compiler_crashers_2_fixed/Inputs/sr9225-other.swift b/validation-test/compiler_crashers_2_fixed/Inputs/sr9225-other.swift new file mode 100644 index 0000000000000..add945424f4ac --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/Inputs/sr9225-other.swift @@ -0,0 +1,10 @@ +struct S1: P1 { + typealias P1_T1 = Int + typealias P1_T2 = Range +} + +struct S2: P1, P3 { + typealias P1_T2 = [P1_T1] + typealias P2_T = Int + typealias P3_T = S1 +} diff --git a/validation-test/compiler_crashers_2_fixed/sr9225.swift b/validation-test/compiler_crashers_2_fixed/sr9225.swift new file mode 100644 index 0000000000000..41a459d2595af --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr9225.swift @@ -0,0 +1,15 @@ +// RUN: %target-swift-frontend -emit-ir -primary-file %s %S/Inputs/sr9225-other.swift -module-name sr9225 +// RUN: %target-swift-frontend -emit-ir %s -primary-file %S/Inputs/sr9225-other.swift -module-name sr9225 + +protocol P1 { + associatedtype P1_T1 + associatedtype P1_T2: Collection where P1_T2.Element == P1_T1 +} + +protocol P2 { + associatedtype P2_T +} + +protocol P3: P1, P2 { + associatedtype P3_T: P1 where P3_T.P1_T2 == Range +} From 4f2a55bdd41220f15e912f11532a493d8cbed9ad Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Tue, 28 Jan 2020 12:38:41 -0800 Subject: [PATCH 218/237] [stdlib/private][OSLog] Support expressive, fprintf-style formatting options for integers passed to os log string interpolation. --- lib/SILOptimizer/Utils/ConstExpr.cpp | 3 + stdlib/private/OSLog/CMakeLists.txt | 2 + .../OSLog/OSLogIntegerFormatting.swift | 317 ++++++++++++++++++ stdlib/private/OSLog/OSLogIntegerTypes.swift | 76 ++--- stdlib/private/OSLog/OSLogMessage.swift | 17 +- stdlib/private/OSLog/OSLogNSObjectType.swift | 2 +- .../private/OSLog/OSLogStringAlignment.swift | 69 ++++ stdlib/private/OSLog/OSLogStringTypes.swift | 2 +- .../OSLogConstantEvaluableTest.swift | 4 +- .../OSLogPrototypeCompileDiagnostics.swift | 4 +- .../OSLogPrototypeCompileTest.swift | 21 +- test/stdlib/OSLogPrototypeExecTest.swift | 91 +++-- 12 files changed, 505 insertions(+), 103 deletions(-) create mode 100644 stdlib/private/OSLog/OSLogIntegerFormatting.swift create mode 100644 stdlib/private/OSLog/OSLogStringAlignment.swift diff --git a/lib/SILOptimizer/Utils/ConstExpr.cpp b/lib/SILOptimizer/Utils/ConstExpr.cpp index 402c2aadf27c0..23d6af179fa77 100644 --- a/lib/SILOptimizer/Utils/ConstExpr.cpp +++ b/lib/SILOptimizer/Utils/ConstExpr.cpp @@ -481,6 +481,9 @@ SymbolicValue ConstExprFunctionState::computeConstantValue(SILValue value) { evaluator.getAllocator()); } + if (auto *convertEscapeInst = dyn_cast(value)) + return getConstantValue(convertEscapeInst->getOperand()); + LLVM_DEBUG(llvm::dbgs() << "ConstExpr Unknown simple: " << *value << "\n"); // Otherwise, we don't know how to handle this. diff --git a/stdlib/private/OSLog/CMakeLists.txt b/stdlib/private/OSLog/CMakeLists.txt index 4dcf40af1cc98..71a0c72ebef11 100644 --- a/stdlib/private/OSLog/CMakeLists.txt +++ b/stdlib/private/OSLog/CMakeLists.txt @@ -4,6 +4,8 @@ add_swift_target_library(swiftOSLogPrototype OSLog.swift OSLogMessage.swift + OSLogIntegerFormatting.swift + OSLogStringAlignment.swift OSLogIntegerTypes.swift OSLogStringTypes.swift OSLogNSObjectType.swift diff --git a/stdlib/private/OSLog/OSLogIntegerFormatting.swift b/stdlib/private/OSLog/OSLogIntegerFormatting.swift new file mode 100644 index 0000000000000..c9b579243c69a --- /dev/null +++ b/stdlib/private/OSLog/OSLogIntegerFormatting.swift @@ -0,0 +1,317 @@ +@frozen +public struct OSLogIntegerFormatting: Equatable { + /// The base to use for the string representation. `radix` must be at least 2 + /// and at most 36. The default is 10. + public var radix: Int + + /// When set, a `+` will be printed for all non-negative integers. + public var explicitPositiveSign: Bool + + /// When set, a prefix: 0b or 0o or 0x will be added when the radix is 2, 8 or + /// 16 respectively. + public var includePrefix: Bool + + /// Whether to use uppercase letters to represent numerals + /// greater than 9 (default is to use lowercase). + public var uppercase: Bool + + /// Minimum number of digits to display. Numbers having fewer digits than + /// minDigits will be displayed with leading zeros. + public var minDigits: Int + + /// - Parameters: + /// - radix: The base to use for the string representation. `radix` must be + /// at least 2 and at most 36. The default is 10. + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. Default is `false`. + /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding + /// radices. Default is `false`. + /// - uppercase: Pass `true` to use uppercase letters to represent numerals + /// greater than 9, or `false` to use lowercase letters. The default is + /// `false`. + /// - minDigits: minimum number of digits to display. Numbers will be + /// prefixed with zeros if necessary to meet the minimum. The default is 1. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public init( + radix: Int = 10, + explicitPositiveSign: Bool = false, + includePrefix: Bool = false, + uppercase: Bool = false, + minDigits: Int = 1 + ) { + precondition(radix >= 2 && radix <= 36) + + self.radix = radix + self.explicitPositiveSign = explicitPositiveSign + self.includePrefix = includePrefix + self.uppercase = uppercase + self.minDigits = minDigits + } + + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. Default is `false`. + /// - minDigits: minimum number of digits to display. Numbers will be + /// prefixed with zeros if necessary to meet the minimum. The default is 1. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func decimal( + explicitPositiveSign: Bool = false, + minDigits: Int = 1 + ) -> OSLogIntegerFormatting { + return OSLogIntegerFormatting( + radix: 10, + explicitPositiveSign: explicitPositiveSign, + minDigits: minDigits) + } + + /// Default decimal format. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var decimal: OSLogIntegerFormatting { .decimal() } + + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. Default is `false`. + /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding + /// radices. Default is `false`. + /// - uppercase: Pass `true` to use uppercase letters to represent numerals + /// greater than 9, or `false` to use lowercase letters. The default is + /// `false`. + /// - minDigits: minimum number of digits to display. Numbers will be + /// prefixed with zeros if necessary to meet the minimum. The default is 1. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func hex( + explicitPositiveSign: Bool = false, + includePrefix: Bool = false, + uppercase: Bool = false, + minDigits: Int = 1 + ) -> OSLogIntegerFormatting { + return OSLogIntegerFormatting( + radix: 16, + explicitPositiveSign: explicitPositiveSign, + includePrefix: includePrefix, + uppercase: uppercase, + minDigits: minDigits) + } + + /// Default hexadecimal format. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var hex: OSLogIntegerFormatting { .hex() } + + /// - Parameters: + /// - explicitPositiveSign: Pass `true` to add a + sign to non-negative + /// numbers. Default is `false`. + /// - includePrefix: Pass `true` to add a prefix: 0b, 0o, 0x to corresponding + /// radices. Default is `false`. + /// - uppercase: Pass `true` to use uppercase letters to represent numerals + /// greater than 9, or `false` to use lowercase letters. The default is + /// `false`. + /// - minDigits: minimum number of digits to display. Numbers will be + /// prefixed with zeros if necessary to meet the minimum. The default is 1. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func octal( + explicitPositiveSign: Bool = false, + includePrefix: Bool = false, + uppercase: Bool = false, + minDigits: Int = 1 + ) -> OSLogIntegerFormatting { + OSLogIntegerFormatting( + radix: 8, + explicitPositiveSign: explicitPositiveSign, + includePrefix: includePrefix, + uppercase: uppercase, + minDigits: minDigits) + } + + /// Default octal format. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var octal: OSLogIntegerFormatting { .octal() } +} + +extension OSLogIntegerFormatting { + /// The prefix for the radix in the Swift literal syntax. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal var _prefix: String { + guard includePrefix else { return "" } + switch radix { + case 2: return "0b" + case 8: return "0o" + case 16: return "0x" + default: return "" + } + } +} + +extension OSLogIntegerFormatting { + + /// Returns a fprintf-compatible length modifier for a given argument type. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + internal static func formatSpecifierLengthModifier( + _ type: I.Type + ) -> String? { + // IEEE Std 1003.1-2017, length modifiers: + switch type { + // hh - d, i, o, u, x, or X conversion specifier applies to + // (signed|unsigned) char + case is CChar.Type: return "hh" + case is CUnsignedChar.Type: return "hh" + + // h - d, i, o, u, x, or X conversion specifier applies to + // (signed|unsigned) short + case is CShort.Type: return "h" + case is CUnsignedShort.Type: return "h" + + case is CInt.Type: return "" + case is CUnsignedInt.Type: return "" + + // l - d, i, o, u, x, or X conversion specifier applies to + // (signed|unsigned) long + case is CLong.Type: return "l" + case is CUnsignedLong.Type: return "l" + + // ll - d, i, o, u, x, or X conversion specifier applies to + // (signed|unsigned) long long + case is CLongLong.Type: return "ll" + case is CUnsignedLongLong.Type: return "ll" + + default: return nil + } + } + + /// Constructs an os_log format specifier for the given type `type` + /// using the specified alignment `align` and privacy qualifier `privacy`. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + @_effects(readonly) + internal func formatSpecifier( + for type: I.Type, + align: OSLogStringAlignment, + privacy: OSLogPrivacy + ) -> String { + // Based on IEEE Std 1003.1-2017 + // `d`/`i` is the only signed integral conversions allowed + if (type.isSigned && radix != 10) { + fatalError("Signed integers must be formatted using .decimal") + } + + // IEEE: Each conversion specification is introduced by the '%' character + // after which the following appear in sequence: + // 1. Zero or more flags (in any order), which modify the meaning of the + // conversion specification. + // 2. An optional minimum field width (for alignment). If the converted + // value has fewer bytes than the field width, it shall be padded with + // characters by default on the left; it shall be padded on the + // right if the left-adjustment flag ( '-' ), is given to the + // field width. + // 3. An optional precision that gives the minimum number of digits to + // display for the d, i, o, u, x, and X conversion specifiers. + // 4. An optional length modifier that specifies the size of the argument. + // 5. A conversion specifier character that indicates the type of + // conversion to be applied. + + // Use Swift style prefixes rather than fprintf style prefixes. + var specification = _prefix + specification += "%" + + // Add privacy qualifier after % sign within curly braces. This is an + // os log specific flag. + switch privacy { + case .private: + specification += "{private}" + case .public: + specification += "{public}" + default: + break + } + + // + // 1. Flags + // + // Use `+` flag if signed, otherwise prefix a literal `+` for unsigned. + if explicitPositiveSign { + // IEEE: `+` The result of a signed conversion shall always begin with a + // sign ( '+' or '-' ) + if type.isSigned { + specification += "+" + } else { + var newSpecification = "+" + newSpecification += specification + specification = newSpecification + } + } + + // IEEE: `-` The result of the conversion shall be left-justified within + // the field. The conversion is right-justified if this flag is not + // specified. + switch align.anchor { + case OSLogCollectionBound.start: + specification += "-" + default: + break + } + + // 2. Minimumn field width + // IEEE: When field width is prefixed by `0`, leading zeros (following any + // indication of sign or base) are used to pad to the field width rather + // than performing space padding. If the '0' and '-' flags both appear, + // the '0' flag is ignored. If a precision is specified, the '0' flag shall + // be ignored. + // + // Commentary: `0` is prefixed to field width when the user doesn't want to + // use precision (minDigits). This allows sign and prefix characters to be + // counted towards field width (they wouldn't be counted towards precision). + // This is more useful for floats, where precision is digits after the radix. + // In our case, we're already handling prefix ourselves; we choose not to + // support this functionality. In our case, alignment always pads spaces ( + // to the left or right) until the minimum field width is met. + if align.minimumColumnWidth > 0 { + specification += align.minimumColumnWidth.description + } + + // 3. Precision + + // Default precision for integers is 1, otherwise use the requested precision. + if minDigits != 1 { + specification += "." + specification += minDigits.description + } + + // 4. Length modifier + guard let lengthModifier = + OSLogIntegerFormatting.formatSpecifierLengthModifier(type) else { + fatalError("Integer type has unknown byte length") + } + specification += lengthModifier + + // 5. The conversion specifier + switch radix { + case 10: + specification += type.isSigned ? "d" : "u" + case 8: + specification += "o" + case 16: + specification += uppercase ? "X" : "x" + default: + fatalError("radix must be 10, 8 or 16") + } + return specification + } +} diff --git a/stdlib/private/OSLog/OSLogIntegerTypes.swift b/stdlib/private/OSLog/OSLogIntegerTypes.swift index 0ba0e31fc6cdf..34fd5cdd31039 100644 --- a/stdlib/private/OSLog/OSLogIntegerTypes.swift +++ b/stdlib/private/OSLog/OSLogIntegerTypes.swift @@ -26,7 +26,7 @@ extension OSLogInterpolation { /// - Parameters: /// - number: the interpolated expression of type Int, which is autoclosured. /// - format: a formatting option available for integer types, defined by the - /// enum `IntFormat`. + /// enum `OSLogIntegerFormatting`. /// - privacy: a privacy qualifier which is either private or public. /// The default is public. @_semantics("constant_evaluable") @@ -34,17 +34,18 @@ extension OSLogInterpolation { @_optimize(none) public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int, - format: IntFormat = .decimal, - privacy: Privacy = .public + format: OSLogIntegerFormatting = .decimal, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .public ) { - appendInteger(number, format: format, privacy: privacy) + appendInteger(number, format: format, align: align, privacy: privacy) } /// Define interpolation for expressions of type Int32. /// - Parameters: /// - number: the interpolated expression of type Int32, which is autoclosured. /// - format: a formatting option available for integer types, defined by the - /// enum `IntFormat`. + /// enum `OSLogIntegerFormatting`. /// - privacy: a privacy qualifier which is either private or public. /// The default is public. @_semantics("constant_evaluable") @@ -52,21 +53,30 @@ extension OSLogInterpolation { @_optimize(none) public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> Int32, - format: IntFormat = .decimal, - privacy: Privacy = .public + format: OSLogIntegerFormatting = .decimal, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .public ) { - appendInteger(number, format: format, privacy: privacy) + appendInteger(number, format: format, align: align, privacy: privacy) } + /// Define interpolation for expressions of type UInt. + /// - Parameters: + /// - number: the interpolated expression of type UInt, which is autoclosured. + /// - format: a formatting option available for integer types, defined by the + /// enum `OSLogIntegerFormatting`. + /// - privacy: a privacy qualifier which is either private or public. + /// The default is public. @_semantics("constant_evaluable") @inlinable @_optimize(none) public mutating func appendInterpolation( _ number: @autoclosure @escaping () -> UInt, - format: IntFormat = .decimal, - privacy: Privacy = .public + format: OSLogIntegerFormatting = .decimal, + align: OSLogStringAlignment = .none, + privacy: OSLogPrivacy = .public ) { - appendInteger(number, format: format, privacy: privacy) + appendInteger(number, format: format, align: align, privacy: privacy) } /// Given an integer, create and append a format specifier for the integer to the @@ -77,17 +87,15 @@ extension OSLogInterpolation { @_optimize(none) internal mutating func appendInteger( _ number: @escaping () -> T, - format: IntFormat, - privacy: Privacy + format: OSLogIntegerFormatting, + align: OSLogStringAlignment, + privacy: OSLogPrivacy ) where T: FixedWidthInteger { guard argumentCount < maxOSLogArgumentCount else { return } + formatString += + format.formatSpecifier(for: T.self, align: align, privacy: privacy) let isPrivateArgument = isPrivate(privacy) - formatString += - getIntegerFormatSpecifier( - T.self, - format, - isPrivateArgument) addIntHeaders(isPrivateArgument, sizeForEncoding(T.self)) arguments.append(number) @@ -114,38 +122,6 @@ extension OSLogInterpolation { preamble = getUpdatedPreamble(isPrivate: isPrivate, isScalar: true) } - - /// Construct an os_log format specifier from the given parameters. - /// This function must be constant evaluable and all its arguments - /// must be known at compile time. - @inlinable - @_semantics("constant_evaluable") - @_effects(readonly) - @_optimize(none) - internal func getIntegerFormatSpecifier( - _ integerType: T.Type, - _ format: IntFormat, - _ isPrivate: Bool - ) -> String where T : FixedWidthInteger { - var formatSpecifier: String = isPrivate ? "%{private}" : "%{public}" - - // Add a length modifier to the specifier. - // TODO: more length modifiers will be added. - if (integerType.bitWidth == CLongLong.bitWidth) { - formatSpecifier += "ll" - } - - // TODO: more format specifiers will be added. - switch (format) { - case .hex: - formatSpecifier += "x" - case .octal: - formatSpecifier += "o" - default: - formatSpecifier += integerType.isSigned ? "d" : "u" - } - return formatSpecifier - } } extension OSLogArguments { diff --git a/stdlib/private/OSLog/OSLogMessage.swift b/stdlib/private/OSLog/OSLogMessage.swift index 1fa5352808a10..2c23d5d8b3f91 100644 --- a/stdlib/private/OSLog/OSLogMessage.swift +++ b/stdlib/private/OSLog/OSLogMessage.swift @@ -14,19 +14,6 @@ // the new OS log APIs. These are prototype implementations and should not be // used outside of tests. -/// Formatting options supported by the logging APIs for logging integers. -/// These can be specified in the string interpolation passed to the log APIs. -/// For Example, -/// log.info("Writing to file with permissions: \(perm, format: .octal)") -/// -/// See `OSLogInterpolation.appendInterpolation` definitions for default options -/// for integer types. -public enum IntFormat { - case decimal - case hex - case octal -} - /// Privacy qualifiers for indicating the privacy level of the logged data /// to the logging system. These can be specified in the string interpolation /// passed to the log APIs. @@ -35,7 +22,7 @@ public enum IntFormat { /// /// See `OSLogInterpolation.appendInterpolation` definitions for default options /// for each supported type. -public enum Privacy { +public enum OSLogPrivacy { case `private` case `public` } @@ -213,7 +200,7 @@ public struct OSLogInterpolation : StringInterpolationProtocol { @_semantics("constant_evaluable") @_effects(readonly) @_optimize(none) - internal func isPrivate(_ privacy: Privacy) -> Bool { + internal func isPrivate(_ privacy: OSLogPrivacy) -> Bool { // Do not use equality comparisons on enums as it is not supported by // the constant evaluator. if case .private = privacy { diff --git a/stdlib/private/OSLog/OSLogNSObjectType.swift b/stdlib/private/OSLog/OSLogNSObjectType.swift index 72d64e65081d8..13d86576bf71a 100644 --- a/stdlib/private/OSLog/OSLogNSObjectType.swift +++ b/stdlib/private/OSLog/OSLogNSObjectType.swift @@ -34,7 +34,7 @@ extension OSLogInterpolation { @_optimize(none) public mutating func appendInterpolation( _ argumentObject: @autoclosure @escaping () -> NSObject, - privacy: Privacy = .private + privacy: OSLogPrivacy = .private ) { guard argumentCount < maxOSLogArgumentCount else { return } diff --git a/stdlib/private/OSLog/OSLogStringAlignment.swift b/stdlib/private/OSLog/OSLogStringAlignment.swift new file mode 100644 index 0000000000000..4e85d64ea0fac --- /dev/null +++ b/stdlib/private/OSLog/OSLogStringAlignment.swift @@ -0,0 +1,69 @@ +@frozen +public enum OSLogCollectionBound { + case start + case end +} + +@frozen +public struct OSLogStringAlignment { + /// Minimum number of characters to be displayed. If the value to be printed + /// is shorter than this number, the result is padded with spaces. The value + /// is not truncated even if the result is larger. + public var minimumColumnWidth: Int + /// This captures right/left alignment. + public var anchor: OSLogCollectionBound + + /// - Parameters: + /// - minimumColumnWidth: Minimum number of characters to be displayed. If the value to be + /// printed is shorter than this number, the result is padded with spaces. The value is not truncated + /// even if the result is larger. + /// - anchor: Use `.end` for right alignment and `.start` for left. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public init( + minimumColumnWidth: Int = 0, + anchor: OSLogCollectionBound = .end + ) { + self.minimumColumnWidth = minimumColumnWidth + self.anchor = anchor + } + + /// Right alignment formatter. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var right: OSLogStringAlignment { + OSLogStringAlignment(anchor: .end) + } + + /// Left alignment formatter. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var left: OSLogStringAlignment { + OSLogStringAlignment(anchor: .start) + } + + /// Use default alignment, which is right alignment. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static var none: OSLogStringAlignment { .right } + + /// Right align and display at least`columns` characters. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func right(columns: Int = 0) -> OSLogStringAlignment { + OSLogStringAlignment(minimumColumnWidth: columns, anchor: .end) + } + + /// Left align and display at least`columns` characters. + @_semantics("constant_evaluable") + @inlinable + @_optimize(none) + public static func left(columns: Int = 0) -> OSLogStringAlignment { + OSLogStringAlignment(minimumColumnWidth: columns, anchor: .start) + } +} diff --git a/stdlib/private/OSLog/OSLogStringTypes.swift b/stdlib/private/OSLog/OSLogStringTypes.swift index 6a616ea221204..ad04b196111e4 100644 --- a/stdlib/private/OSLog/OSLogStringTypes.swift +++ b/stdlib/private/OSLog/OSLogStringTypes.swift @@ -37,7 +37,7 @@ extension OSLogInterpolation { @_optimize(none) public mutating func appendInterpolation( _ argumentString: @autoclosure @escaping () -> String, - privacy: Privacy = .private + privacy: OSLogPrivacy = .private ) { guard argumentCount < maxOSLogArgumentCount else { return } diff --git a/test/SILOptimizer/OSLogConstantEvaluableTest.swift b/test/SILOptimizer/OSLogConstantEvaluableTest.swift index 6a139e9a5c5e1..0292f0d1b38ed 100644 --- a/test/SILOptimizer/OSLogConstantEvaluableTest.swift +++ b/test/SILOptimizer/OSLogConstantEvaluableTest.swift @@ -28,7 +28,7 @@ func osLogMessageStringLiteralInitTest() -> OSLogMessage { // CHECK-NOT: error: // CHECK-LABEL: @appendLiteral(String) -> () // CHECK-NOT: error: -// CHECK-LABEL: @appendInterpolation(_: @autoclosure () -> Int, format: IntFormat, privacy: Privacy) -> () +// CHECK-LABEL: @appendInterpolation(_: @autoclosure () -> Int, format: OSLogIntegerFormatting, align: OSLogStringAlignment, privacy: OSLogPrivacy) -> () // CHECK-NOT: error: // CHECK-LABEL: @appendLiteral(String) -> () // CHECK-NOT: error: @@ -41,7 +41,7 @@ func intValueInterpolationTest() -> OSLogMessage { // CHECK-LABEL: @init(literalCapacity: Int, interpolationCount: Int) -> OSLogInterpolation // CHECK-NOT: error: -// CHECK-LABEL: @appendInterpolation(_: @autoclosure () -> String, privacy: Privacy) -> () +// CHECK-LABEL: @appendInterpolation(_: @autoclosure () -> String, privacy: OSLogPrivacy) -> () // CHECK-NOT: error: @_semantics("test_driver") func stringValueInterpolationTest() -> OSLogMessage { diff --git a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift index 36c524e27055e..eaf03970b138a 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileDiagnostics.swift @@ -18,13 +18,13 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // expected-error @-1 {{globalStringTablePointer builtin must used only on string literals}} } - func testNonconstantFormatOption(h: Logger, formatOpt: IntFormat) { + func testNonconstantFormatOption(h: Logger, formatOpt: OSLogIntegerFormatting) { h.log(level: .debug, "Minimum integer value: \(Int.min, format: formatOpt)") // expected-error @-1 {{interpolation arguments like format and privacy options must be constants}} // expected-error @-2 {{globalStringTablePointer builtin must used only on string literals}} } - func testNonconstantPrivacyOption(h: Logger, privacyOpt: Privacy) { + func testNonconstantPrivacyOption(h: Logger, privacyOpt: OSLogPrivacy) { h.log(level: .debug, "Minimum integer value: \(Int.min, privacy: privacyOpt)") // expected-error @-1 {{interpolation arguments like format and privacy options must be constants}} // expected-error @-2 {{globalStringTablePointer builtin must used only on string literals}} diff --git a/test/SILOptimizer/OSLogPrototypeCompileTest.swift b/test/SILOptimizer/OSLogPrototypeCompileTest.swift index 3901cb43b589a..424ddf9e08ebb 100644 --- a/test/SILOptimizer/OSLogPrototypeCompileTest.swift +++ b/test/SILOptimizer/OSLogPrototypeCompileTest.swift @@ -27,8 +27,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Minimum integer value: %{public}lld" - // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Minimum integer value: %{public}d" + // CHECK-DAG: [[LIT]] = string_literal utf8 "Minimum integer value: %{public}ld" // Check if the size of the argument buffer is a constant. @@ -68,7 +67,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-LABEL: @${{.*}}testInterpolationWithFormatOptionsL_ func testInterpolationWithFormatOptions(h: Logger) { - h.log(level: .info, "Maximum integer value: \(Int.max, format: .hex)") + h.log(level: .info, "Maximum unsigned integer value: \(UInt.max, format: .hex)") // Check if there is a call to _os_log_impl with a literal format string. @@ -78,8 +77,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Maximum integer value: %{public}llx" - // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Maximum integer value: %{public}x" + // CHECK-DAG: [[LIT]] = string_literal utf8 "Maximum unsigned integer value: %{public}lx" // Check if the size of the argument buffer is a constant. @@ -118,7 +116,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-LABEL: @${{.*}}testInterpolationWithFormatOptionsAndPrivacyL_ func testInterpolationWithFormatOptionsAndPrivacy(h: Logger) { - let privateID = 0x79abcdef + let privateID: UInt = 0x79abcdef h.log( level: .error, "Private Identifier: \(privateID, format: .hex, privacy: .private)") @@ -131,8 +129,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Private Identifier: %{private}llx" - // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Private Identifier: %{private}x" + // CHECK-DAG: [[LIT]] = string_literal utf8 "Private Identifier: %{private}lx" // Check if the size of the argument buffer is a constant. @@ -172,7 +169,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-LABEL: @${{.*}}testInterpolationWithMultipleArgumentsL_ func testInterpolationWithMultipleArguments(h: Logger) { let privateID = 0x79abcdef - let filePermissions = 0o777 + let filePermissions: UInt = 0o777 let pid = 122225 h.log( level: .error, @@ -190,8 +187,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-64-DAG: [[LIT]] = string_literal utf8 "Access prevented: process %{public}lld initiated by user: %{private}lld attempted resetting permissions to %{public}llo" - // CHECK-32-DAG: [[LIT]] = string_literal utf8 "Access prevented: process %{public}d initiated by user: %{private}d attempted resetting permissions to %{public}o" + // CHECK-DAG: [[LIT]] = string_literal utf8 "Access prevented: process %{public}ld initiated by user: %{private}ld attempted resetting permissions to %{public}lo" // Check if the size of the argument buffer is a constant. @@ -339,8 +335,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // CHECK-DAG: [[STRING3]] = begin_borrow [[STRING4:%[0-9]+]] // CHECK-DAG: [[STRING4]] = apply [[STRING_INIT:%[0-9]+]]([[LIT:%[0-9]+]], // CHECK-DAG: [[STRING_INIT]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC - // CHECK-64-DAG: [[LIT]] = string_literal utf8 "%{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld %{public}lld " - // CHECK-32-DAG: [[LIT]] = string_literal utf8 "%{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d %{public}d " + // CHECK-DAG: [[LIT]] = string_literal utf8 "%{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld %{public}ld " // Check if the size of the argument buffer is a constant. diff --git a/test/stdlib/OSLogPrototypeExecTest.swift b/test/stdlib/OSLogPrototypeExecTest.swift index 0e3e0c04fa5e5..5739ab0f2435f 100644 --- a/test/stdlib/OSLogPrototypeExecTest.swift +++ b/test/stdlib/OSLogPrototypeExecTest.swift @@ -26,16 +26,16 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { h.log("A message with no data") // Test logging at specific levels. - h.debug("Minimum integer value: \(Int.min, format: .hex)") - h.info("Maximum integer value: \(Int.max, format: .hex)") + h.debug("Minimum integer value: \(Int.min)") + h.info("Maximum unsigned integer value: \(UInt.max, format: .hex)") - let privateID = 0x79abcdef + let privateID: UInt = 0x79abcdef h.error("Private Identifier: \(privateID, format: .hex, privacy: .private)") - let addr = 0x7afebabe + let addr: UInt = 0x7afebabe h.fault("Invalid address: 0x\(addr, format: .hex, privacy: .public)") // Test logging with multiple arguments. - let filePermissions = 0o777 + let filePermissions: UInt = 0o777 let pid = 122225 h.error( """ @@ -107,7 +107,7 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { OSLogTestSuite.test("integer types") { let h = Logger() - h.log("Smallest 32-bit integer value: \(Int32.min, format: .hex)") + h.log("Smallest 32-bit integer value: \(Int32.min)") } OSLogTestSuite.test("dynamic strings") { @@ -149,7 +149,6 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) { // be available. internal var InterpolationTestSuite = TestSuite("OSLogInterpolationTest") -internal let intPrefix = Int.bitWidth == CLongLong.bitWidth ? "ll" : "" internal let bitsPerByte = 8 /// A struct that exposes methods for checking whether a given byte buffer @@ -343,7 +342,7 @@ InterpolationTestSuite.test("integer literal") { "An integer literal \(10)", with: { (formatString, buffer) in expectEqual( - "An integer literal %{public}\(intPrefix)d", + "An integer literal %{public}ld", formatString) let bufferChecker = OSLogBufferChecker(buffer) @@ -360,10 +359,10 @@ InterpolationTestSuite.test("integer literal") { InterpolationTestSuite.test("integer with formatting") { _checkFormatStringAndBuffer( - "Minimum integer value: \(Int.min, format: .hex)", + "Minimum integer value: \(UInt.max, format: .hex)", with: { (formatString, buffer) in expectEqual( - "Minimum integer value: %{public}\(intPrefix)x", + "Minimum integer value: %{public}lx", formatString) let bufferChecker = OSLogBufferChecker(buffer) @@ -376,18 +375,18 @@ InterpolationTestSuite.test("integer with formatting") { bufferChecker.checkInt( startIndex: $0, flag: .publicFlag, - expectedInt: Int.min) + expectedInt: UInt.max) }) }) } InterpolationTestSuite.test("integer with privacy and formatting") { - let addr = 0x7afebabe + let addr: UInt = 0x7afebabe _checkFormatStringAndBuffer( "Access to invalid address: \(addr, format: .hex, privacy: .private)", with: { (formatString, buffer) in expectEqual( - "Access to invalid address: %{private}\(intPrefix)x", + "Access to invalid address: %{private}lx", formatString) let bufferChecker = OSLogBufferChecker(buffer) @@ -406,7 +405,7 @@ InterpolationTestSuite.test("integer with privacy and formatting") { } InterpolationTestSuite.test("test multiple arguments") { - let filePerms = 0o777 + let filePerms: UInt = 0o777 let pid = 122225 let privateID = 0x79abcdef @@ -419,9 +418,9 @@ InterpolationTestSuite.test("test multiple arguments") { with: { (formatString, buffer) in expectEqual( """ - Access prevented: process %{public}\(intPrefix)d initiated by \ - user: %{private}\(intPrefix)d attempted resetting permissions \ - to %{public}\(intPrefix)o + Access prevented: process %{public}ld initiated by \ + user: %{private}ld attempted resetting permissions \ + to %{public}lo """, formatString) @@ -466,7 +465,7 @@ InterpolationTestSuite.test("interpolation of too many arguments") { with: { (formatString, buffer) in expectEqual( String( - repeating: "%{public}\(intPrefix)d ", + repeating: "%{public}ld ", count: Int(maxOSLogArgumentCount)), formatString) @@ -630,7 +629,7 @@ protocol TestProto { InterpolationTestSuite.test("Interpolation of complex expressions") { class TestClass: NSObject { func testFunction() { - // The following call should no crash. + // The following call should not crash. _checkFormatStringAndBuffer("A complex expression \(toString(self))") { (formatString, _) in expectEqual("A complex expression %s", formatString) @@ -638,3 +637,57 @@ InterpolationTestSuite.test("Interpolation of complex expressions") { } } } + +InterpolationTestSuite.test("Include prefix formatting option") { + let unsignedValue: UInt = 0o171 + _checkFormatStringAndBuffer( + "Octal with prefix: \(unsignedValue, format: .octal(includePrefix: true))") { + (formatString, buffer) in + expectEqual("Octal with prefix: 0o%{public}lo", formatString) + } +} + +InterpolationTestSuite.test("Hex with uppercase formatting option") { + let unsignedValue: UInt = 0xcafebabe + _checkFormatStringAndBuffer( + "Hex with uppercase: \(unsignedValue, format: .hex(uppercase: true))") { + (formatString, buffer) in + expectEqual("Hex with uppercase: %{public}lX", formatString) + } +} + +InterpolationTestSuite.test("Integer with explicit positive sign") { + let posValue = Int.max + _checkFormatStringAndBuffer( + "\(posValue, format: .decimal(explicitPositiveSign: true))") { + (formatString, buffer) in + expectEqual("%{public}+ld", formatString) + } +} + +InterpolationTestSuite.test("Unsigned integer with explicit positive sign") { + let posValue = UInt.max + _checkFormatStringAndBuffer( + "\(posValue, format: .decimal(explicitPositiveSign: true))") { + (formatString, buffer) in + expectEqual("+%{public}lu", formatString) + } +} + +InterpolationTestSuite.test("Integer formatting with precision") { + let intValue = 10 + _checkFormatStringAndBuffer( + "\(intValue, format: .decimal(minDigits: 10))") { + (formatString, buffer) in + expectEqual("%{public}.10ld", formatString) + } +} + +InterpolationTestSuite.test("Integer formatting with alignment") { + let intValue = 10 + _checkFormatStringAndBuffer( + "\(intValue, align: .right(columns: 10)) \(intValue, align: .left(columns: 5))") { + (formatString, buffer) in + expectEqual("%{public}10ld %{public}-5ld", formatString) + } +} From 6ff129c71bfc0522970516685929429c535bb234 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Fri, 7 Feb 2020 19:30:18 -0800 Subject: [PATCH 219/237] Move file only --- lib/AST/CMakeLists.txt | 2 +- ...epGraphConstructor.cpp => SourceFileDepGraphConstructor.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/AST/{FineGrainedDependenciesSourceFileDepGraphConstructor.cpp => SourceFileDepGraphConstructor.cpp} (100%) diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 45a6911b110b7..b5fa70c93317a 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -44,7 +44,6 @@ add_swift_host_library(swiftAST STATIC Evaluator.cpp Expr.cpp FineGrainedDependencies.cpp - FineGrainedDependenciesSourceFileDepGraphConstructor.cpp GenericEnvironment.cpp GenericSignature.cpp GenericSignatureBuilder.cpp @@ -68,6 +67,7 @@ add_swift_host_library(swiftAST STATIC RequirementEnvironment.cpp SyntaxASTMap.cpp SILLayout.cpp + SourceFileDepGraphConstructor.cpp Stmt.cpp SubstitutionMap.cpp SwiftNameTranslation.cpp diff --git a/lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp b/lib/AST/SourceFileDepGraphConstructor.cpp similarity index 100% rename from lib/AST/FineGrainedDependenciesSourceFileDepGraphConstructor.cpp rename to lib/AST/SourceFileDepGraphConstructor.cpp From ec9844b2d9c13f9f0ec0b1e705c33a2a4a2bb58a Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Mon, 3 Feb 2020 19:25:00 -0800 Subject: [PATCH 220/237] [SIL Optimization] Add a new mandatory pass for unrolling forEach calls over arrays created from array literals. This enables optimizing further the output of the OSLogOptimization pass, and results in highly-compact and optimized IR for calls to the new os log API. --- include/swift/AST/SemanticAttrs.def | 2 + .../swift/SILOptimizer/PassManager/Passes.def | 2 + .../swift/SILOptimizer/Utils/InstOptUtils.h | 9 + .../LoopTransforms/CMakeLists.txt | 1 + .../LoopTransforms/ForEachLoopUnroll.cpp | 630 ++++++++++++++++++ lib/SILOptimizer/PassManager/PassPipeline.cpp | 2 + lib/SILOptimizer/Utils/InstOptUtils.cpp | 15 + stdlib/public/core/Sequence.swift | 1 + .../OSLogPrototypeFullOptTest.swift | 174 +++++ .../for_each_loop_unroll_test.sil | 225 +++++++ .../for_each_loop_unroll_test.swift | 111 +++ test/stdlib/OSLogPrototypeExecTest.swift | 3 + 12 files changed, 1175 insertions(+) create mode 100644 lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp create mode 100644 test/SILOptimizer/OSLogPrototypeFullOptTest.swift create mode 100644 test/SILOptimizer/for_each_loop_unroll_test.sil create mode 100644 test/SILOptimizer/for_each_loop_unroll_test.swift diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def index e810eb99c94e4..28be95549153b 100644 --- a/include/swift/AST/SemanticAttrs.def +++ b/include/swift/AST/SemanticAttrs.def @@ -60,6 +60,8 @@ SEMANTICS_ATTR(ARRAY_COUNT, "array.count") SEMANTICS_ATTR(ARRAY_DEALLOC_UNINITIALIZED, "array.dealloc_uninitialized") SEMANTICS_ATTR(ARRAY_UNINITIALIZED_INTRINSIC, "array.uninitialized_intrinsic") +SEMANTICS_ATTR(SEQUENCE_FOR_EACH, "sequence.forEach") + SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_NEVER, "optimize.sil.specialize.generic.never") SEMANTICS_ATTR(OPTIMIZE_SIL_SPECIALIZE_GENERIC_PARTIAL_NEVER, "optimize.sil.specialize.generic.partial.never") diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index b879b3a666795..5d048881ccd6f 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -319,6 +319,8 @@ PASS(NonInlinableFunctionSkippingChecker, "check-non-inlinable-function-skipping PASS(YieldOnceCheck, "yield-once-check", "Check correct usage of yields in yield-once coroutines") PASS(OSLogOptimization, "os-log-optimization", "Optimize os log calls") +PASS(ForEachLoopUnroll, "for-each-loop-unroll", + "Unroll forEach loops over array literals") PASS(MandatoryCombine, "mandatory-combine", "Perform mandatory peephole combines") PASS(BugReducerTester, "bug-reducer-tester", diff --git a/include/swift/SILOptimizer/Utils/InstOptUtils.h b/include/swift/SILOptimizer/Utils/InstOptUtils.h index d67600dbd17ef..e1665d5f466c0 100644 --- a/include/swift/SILOptimizer/Utils/InstOptUtils.h +++ b/include/swift/SILOptimizer/Utils/InstOptUtils.h @@ -145,6 +145,15 @@ class InstructionDeleter { SILInstruction *inst, llvm::function_ref callback = [](SILInstruction *) {}); + + /// Recursively visit users of \c inst (including \c inst)and force delete + /// them. Also, destroy the consumed operands of the deleted instructions + /// whenever necessary. Invoke the \c callback on instructions that are + /// deleted. + void recursivelyForceDeleteUsersAndFixLifetimes( + SILInstruction *inst, + llvm::function_ref callback = + [](SILInstruction *) {}); }; /// If \c inst is dead, delete it and recursively eliminate all code that diff --git a/lib/SILOptimizer/LoopTransforms/CMakeLists.txt b/lib/SILOptimizer/LoopTransforms/CMakeLists.txt index 25ebb3572ef91..32a9dc6d7d88b 100644 --- a/lib/SILOptimizer/LoopTransforms/CMakeLists.txt +++ b/lib/SILOptimizer/LoopTransforms/CMakeLists.txt @@ -5,4 +5,5 @@ silopt_register_sources( LoopRotate.cpp LoopUnroll.cpp LICM.cpp + ForEachLoopUnroll.cpp ) diff --git a/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp new file mode 100644 index 0000000000000..56715e2bbc743 --- /dev/null +++ b/lib/SILOptimizer/LoopTransforms/ForEachLoopUnroll.cpp @@ -0,0 +1,630 @@ +//===--- ForEachLoopUnrolling.cpp - Unroll loops over array literals ----- ===// +// +// 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 pass unrolls Sequence.forEach calls invoked on an array created +// from an array literal. See below for an overview of the algorithm. This is +// a function-level transformation pass that operates on ownership SIL and +// so must be applied before ownership is stripped. +// +// Algorithm overview: +// +// 1. Iterate over the body of the function and analyze calls to the array +// initializer annotated "array.uninitialized_intrinsic". This is done in +// `run` method. +// +// 2. For every "array.uninintialized_intrinsic" initializer call, try +// extracting the elements with which the array is initialized, and classify +// the uses of the array into incidental uses, forEach calls and uses that +// can write into the array. If any of the following conditions hold, give +// up and look for the next initializer call: +// - The elements stored into the array cannot be extracted. +// - The array can be modified after initialization. +// - There are no forEach calls on the array. +// - The array is too large to unroll forEach calls. This check uses +// SILModule::UnrollThreshold parameter. +// If none of the above conditions hold, procede to unrolling every forEach +// call on the array (Step 3). Step 2 is implemented in the function: +// `tryUnrollForEachCallsOverArrayLiteral`. +// +// 3. Given a forEach call, unroll it by applying the "body closure" passed to +// the call to every element of the array. There are three important +// details to this unrolling. Firstly, the SILValue of the element stored +// into the array through the store instruction (identified in step 2) may +// not be valid at the point where the unrolling must happen (i.e., where the +// forEach is called). Therefore, we need to copy_value the elements at the +// point where they are stored and ensure that the copies are alive until +// they are used by the unrolled code. The implementation actually +// makes the copies valid for the entire lifetime of the array. +// +// Secondly, the body closure uses @in_guaranteed convention for the +// parameter. Therefore, an alloc_stack is created before the unrolled code +// begins to hold the elements, and is destoryed once the unrolled code ends. +// +// Thirdly, the body closure throws. Hence, it has to be try_applied. This +// means that we need to chain the try_applies in such a way that when the +// try_apply on the element e_i completes normally it jumps to the try_apply +// of e_i+1. When the try_apply (of any element) throws, it must go to the +// error block of the original forEach call. +// +// All of this is implemented by the function `unrollForEach`. +// +// 4. Delete the forEach calls that were unrolled and clean up dead code +// resulting due to that. +// +// This transformation is illustrated below on a small example: +// +// Input: +// %initResult = apply %arrayUninitialized(...) +// (%array, %storage_ptr) = destructure_tuple %initResult +// store %elem1 at array index 1 +// store %elem2 at array index 2 +// .. +// try_apply %forEach(%body, %array) normal bb1, error bb2 +// bb1(%res : $()): +// .. +// bb2(%error : @owned $Error): +// ... +// bb3: +// destroy_value %array +// +// Output: +// +// %initResult = apply %arrayUninitialized(...) +// (%array, %storage_ptr) = destructure_tuple %initResult +// %elem1copy = copy_value %elem1 <-- +// store %elem1 at array index 1 +// %elem2copy = copy_value %elem2 <-- +// store %elem2 at array index 2 +// .. +// alloc_stack %stack +// %elem1borrow = begin_borrow %elem1copy +// store_borrow %elem1borrow to %stack +// end_borrow %elem1borrow +// try_apply %body(%stack) normal normalbb1, error errbb1 +// +// errbb1(%error : @owned $Error): +// br bb2(%error) +// +// normalbb1(%res : $()): +// %elem2borrow = begin_borrow %elem2copy +// store_borrow %elem2borrow to %stack +// end_borrow %elem2borrow +// try_apply %body(%stack) normal bb1, error errbb2 +// +// errbb2(%error : @owned $Error): +// br bb2(%error) +// +// bb1(%res : $()): +// dealloc_stack %stack +// ... +// bb2(%error : @owned $Error): +// ... +// dealloc_stack %stack +// bb3: +// destroy_value %elem1copy +// destroy_value %elem2copy +// destroy_value %array + +#include "swift/AST/ASTContext.h" +#include "swift/AST/Expr.h" +#include "swift/AST/Module.h" +#include "swift/AST/SemanticAttrs.h" +#include "swift/AST/SubstitutionMap.h" +#include "swift/Basic/OptimizationMode.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/CFG.h" +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILBuilder.h" +#include "swift/SIL/SILConstants.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILModule.h" +#include "swift/SIL/TypeLowering.h" +#include "swift/SILOptimizer/Analysis/ArraySemantic.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/CFGOptUtils.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" + +using namespace swift; + +namespace { + +/// A class for processing and storing information about an array +/// such as the values with which it is initialized and the kind of +/// users it has. +class ArrayInfo { + /// The array value returned by the _allocateUninitialized call. + SILValue arrayValue; + + /// A map from an array index to the store instruction that initializes that + /// index. + llvm::DenseMap elementStoreMap; + + /// List of Sequence.forEach calls invoked on the array. + SmallSetVector forEachCalls; + + /// Indicates whether the array could be modified after initialization. Note + /// that this not include modifications to the elements of the array. When + /// set, this will prevent the forEach loops from getting unrolled. + bool mayBeWritten = false; + + /// Instructions that destroy the array. These must be destroy_value + /// instructions either of the \c arrayValue or the copy_values of that. + SmallVector destroys; + + /// Classify uses of the array into forEach uses, read-only uses etc. and set + /// the fields of this instance appropriately. This function will recursively + /// classify the uses of borrows and copy-values of the array as well. + void classifyUsesOfArray(SILValue arrayValue); + +public: + ArrayInfo() {} + + /// Given an apply instruction \c apply, try to initialize this ArrayInfo + /// with it. This would succeed iff the apply instruction starts an + /// initialization pattern that is auto-generated by the compiler for array + /// literals. Return true on success and false on failure. + bool tryInitialize(ApplyInst *apply); + + /// Return the SILValue of the array that is initialized. + SILValue getArrayValue() { + assert(arrayValue); + return arrayValue; + } + + /// Return true iff the array could be modified after initialization. + bool mayBeModified() { + assert(arrayValue); + return mayBeWritten; + } + + /// Return the forEach uses identified for the array. + ArrayRef getForEachUses() { + assert(arrayValue); + return ArrayRef(forEachCalls.begin(), forEachCalls.end()); + } + + /// Return the number of elements in the array. + uint64_t getNumElements() { + assert(arrayValue); + return elementStoreMap.size(); + } + + /// Return the store instruction that initializes the given \c index + /// of the array. + StoreInst *getElementStore(uint64_t index) { + assert(arrayValue); + return elementStoreMap[index]; + } + + /// Return the SIL type of the elements stored in the array. + /// \pre the array must be non-empty. + SILType getElementSILType() { + assert(getNumElements() > 0 && "cannot call this on empty arrays"); + return elementStoreMap[0]->getSrc()->getType(); + } + + /// Add the destroy_value instructions that represents the last use of the + /// array to the parameter \c lastDestroys. The \c lastDestroys + /// added by this function are guaranteed to come after all uses of the + /// \c arrayValue and copy_values of the \c arrayValue (along all + /// "non-dead-end" blocks). + void getLastDestroys(SmallVectorImpl &lastDestroys); +}; + +/// Given a Array-typed SIL value \c array and an instruction \c user that uses +/// the array, check whether this use actually represents a call to _fixLifetime +/// function. This would be the case if \c user is a store_borrow instruction +/// that stores into an alloc_stack which is passed to a fixLifetime +/// instruction. That is, if the following pattern holds: +/// +/// %stack = alloc_stack +/// user: store_borrow %array to %stack +/// fixLifetime %stack +/// \returns the fixLifetime instruction if this is a fixLifetime use of the +/// array, nullptr otherwise. +static FixLifetimeInst *isFixLifetimeUseOfArray(SILInstruction *user, + SILValue array) { + StoreBorrowInst *storeUser = dyn_cast(user); + if (!storeUser || storeUser->getSrc() != array) + return nullptr; + AllocStackInst *alloc = dyn_cast(storeUser->getDest()); + if (!alloc) + return nullptr; + auto fixLifetimeUsers = alloc->getUsersOfType(); + if (fixLifetimeUsers.empty()) + return nullptr; + auto firstUser = fixLifetimeUsers.begin(); + FixLifetimeInst *result = *firstUser; + // We need to have a unique result. + if (++firstUser != fixLifetimeUsers.end()) + return nullptr; + return result; +} + +/// Given an array-typed SIL value \c array and an instruction \c user that uses +/// the array, check whether this use actually represents a call to the +/// Sequence.forEach function. This would be case if \c user is a store_borrow +/// instruction that stores into an alloc_stack which is passed to a try-apply +/// of the Sequence.forEach function. That is, if the following pattern holds: +/// +/// %stack = alloc_stack +/// user: store_borrow %array to %stack +/// try_apply %forEachCall(%closure, %array) +/// \returns the try-apply instruction invoking the forEach function if this is +/// a forEach use of the array, nullptr otherwise. +static TryApplyInst *isForEachUseOfArray(SILInstruction *user, SILValue array) { + StoreBorrowInst *storeUser = dyn_cast(user); + if (!storeUser || storeUser->getSrc() != array) + return nullptr; + AllocStackInst *alloc = dyn_cast(storeUser->getDest()); + if (!alloc) + return nullptr; + auto applyUsers = alloc->getUsersOfType(); + if (applyUsers.empty()) + return nullptr; + auto firstUser = applyUsers.begin(); + TryApplyInst *apply = *firstUser; + // We need to have a unique result. + if (++firstUser != applyUsers.end()) + return nullptr; + SILFunction *callee = apply->getCalleeFunction(); + if (!callee || !callee->hasSemanticsAttr(semantics::SEQUENCE_FOR_EACH)) + return nullptr; + return apply; +} + +void ArrayInfo::classifyUsesOfArray(SILValue arrayValue) { + for (Operand *operand : arrayValue->getUses()) { + auto *user = operand->getUser(); + if (isIncidentalUse(user)) + continue; + // Ignore this user if it is a call to _fixLifetime. Note that this use + // will not be subsumed by InstructionUtils::isIncidentalUse check made + // above as the array would be passed indirectly. + if (isFixLifetimeUseOfArray(user, arrayValue)) + continue; + // Check if this is a forEach call on the array. + if (TryApplyInst *forEachCall = isForEachUseOfArray(user, arrayValue)) { + forEachCalls.insert(forEachCall); + continue; + } + // Recursively classify begin_borrow and copy_value uses. + if (BeginBorrowInst *beginBorrow = dyn_cast(user)) { + classifyUsesOfArray(beginBorrow); + continue; + } + if (CopyValueInst *copyValue = dyn_cast(user)) { + classifyUsesOfArray(copyValue); + continue; + } + if (DestroyValueInst *destroyValue = dyn_cast(user)) { + destroys.push_back(destroyValue); + continue; + } + // Set mayBeWritten to true if the user could potentially modify the array. + // Note that the array elements are allowed to be modified as long + // as the array itself is not modified (which is possible with reference + // types). + ArraySemanticsCall arrayOp(user); + if (!arrayOp.doesNotChangeArray()) + mayBeWritten = true; + } +} + +bool ArrayInfo::tryInitialize(ApplyInst *apply) { + ArraySemanticsCall arrayAllocateUninitCall( + apply, semantics::ARRAY_UNINITIALIZED_INTRINSIC); + if (!arrayAllocateUninitCall) + return false; + arrayValue = arrayAllocateUninitCall.getArrayValue(); + if (!arrayValue) + return false; + if (!arrayAllocateUninitCall.mapInitializationStores(elementStoreMap)) + return false; + // Collect information about uses of the array value. + classifyUsesOfArray(arrayValue); + return true; +} + +void ArrayInfo::getLastDestroys( + SmallVectorImpl &lastDestroys) { + assert(arrayValue); + // Collect the frontier instructions of the field \c destroys, which stores + // destroy_value instructions of the \c arrayValue as well as of its + // copy_values, using ValueLifetimeAnalysis. The frontier is a list of + // instructions that mark the exits of the flow of control from the + // \c destroys. + ValueLifetimeAnalysis lifetimeAnalysis = + ValueLifetimeAnalysis(arrayValue->getDefiningInstruction(), destroys); + ValueLifetimeAnalysis::Frontier frontier; + lifetimeAnalysis.computeFrontier(frontier, + ValueLifetimeAnalysis::DontModifyCFG); + for (SILInstruction *frontierInst : frontier) { + // Skip frontier instructions at the start of a basic block as they do not + // follow a destroy_value of the array. Note that the goal is to collect + // the last destroys, which must always immediately preceed a frontier + // instruction as it marks the end of the use of the array. + if (frontierInst == &frontierInst->getParent()->front()) + continue; + SILInstruction *inst = &*(--frontierInst->getIterator()); + // This must be a destroy instruction. Moreover it must also belong to \c + // destroys. + DestroyValueInst *lastDestroy = dyn_cast(inst); + assert(lastDestroy); + lastDestroys.push_back(cast(lastDestroy)); + } +} + +/// Delete the forEach calls from the SIL that contains it. This function will +/// not clean up any resulting dead instructions. +static void removeForEachCall(TryApplyInst *forEachCall, + InstructionDeleter &deleter) { + AllocStackInst *allocStack = + dyn_cast(forEachCall->getArgument(1)); + assert(allocStack); + // The allocStack will be used in the forEach call and also in a store + // instruction and a dealloc_stack instruction. Force delete all of them. + deleter.recursivelyForceDeleteUsersAndFixLifetimes(allocStack); +} + +/// Unroll the \c forEachCall on an array, using the information in +/// \c ArrayInfo. Once unrolled, \c forEachCall will be deleted +/// from the containing function. This function implements the following +/// transformation. +/// - If the array stores non-trivial elements, for every element stored +/// into the array, insert a copy_value of the element just before it +/// is stored. This is necessary so that an @owned SILValue for the +/// element is available at the point where the forEach is used. +/// +/// - Create an alloc_stack A at the point of the forEach call. This is +/// necessary to indirectly pass the elements of the array. +/// +/// For every element e_i at the index i of the array, do the following: +/// - create a new basic block b_i if i > 0. Let b_0 denote the basic block +/// that contains the forEach call. +/// - store_borrow the element e_i into the alloc_stack A. Note that we +/// can use the owned copy of e_i created in the previous step. +/// - try_apply the forEach's body closure on the the alloc_stack A. +/// If i is not the last index, jump to b_i+1 in the normal case of the +/// try_apply. If i is the last index of the array jump to the normal +/// target of the forEach call. Jump to a new error block: err_i in the +/// error case. Make err_i jump to the error target of the original +/// forEach call. +/// +/// - Dealloc the alloc_stack along the normal and error targets of the +/// forEach calls. +/// +/// - Destroy all the copies of the elements (if it is non-trivial) just +/// before the array's lifetime ends. +static void unrollForEach(ArrayInfo &arrayInfo, TryApplyInst *forEachCall, + InstructionDeleter &deleter) { + if (arrayInfo.getNumElements() == 0) { + // If this is an empty array, delete the forEach entirely. + removeForEachCall(forEachCall, deleter); + return; + } + + SILFunction *fun = forEachCall->getFunction(); + SILLocation forEachLoc = forEachCall->getLoc(); + SILValue forEachBodyClosure = forEachCall->getArgument(0); + SILType arrayElementType = arrayInfo.getElementSILType(); + + SILFunctionType *bodyClosureType = + forEachBodyClosure->getType().getAs(); + SILParameterInfo bodyParameterInfo = bodyClosureType->getParameters()[0]; + // The forEachBodyClosure must use @in_guaranteed convention for passing + // arguments. + assert(bodyParameterInfo.getConvention() == + ParameterConvention::Indirect_In_Guaranteed && + "forEach body closure is expected to take @in_guaranteed argument"); + + // Copy the elements stored into the array. This is necessary as we need to + // extend the lifetime of the stored elements at least until the forEach call, + // which will now be unrolled. The following code inserts a copy_value of the + // elements just before the point where they are stored into the array, and + // a corresponding destroy_value at the end of the lifetime of the array. In + // other words, the lifetime of the element copies are made to match the + // lifetime of the array. Even though copies can be destroyed sooner after + // they are used by the unrolled code, doing so may introduce more destroys + // than needed as the unrolled code have many branches (due to try applies) + // all of which joins later into a single path eventually. + SmallVector elementCopies; + for (uint64_t i = 0; i < arrayInfo.getNumElements(); i++) { + StoreInst *elementStore = arrayInfo.getElementStore(i); + // Insert the copy just before the store of the element into the array. + SILValue copy = SILBuilderWithScope(elementStore) + .emitCopyValueOperation(elementStore->getLoc(), + elementStore->getSrc()); + elementCopies.push_back(copy); + } + + // Destroy the copy_value of the elements before the last destroys of the + // array. It is important that the copied elements are destroyed before the + // array is destroyed. This enables other optimizations. + SmallVector lastDestroys; + arrayInfo.getLastDestroys(lastDestroys); + for (DestroyValueInst *destroy : lastDestroys) { + SILBuilderWithScope destroyBuilder(destroy); + for (SILValue element : elementCopies) { + destroyBuilder.emitDestroyValueOperation(destroy->getLoc(), element); + } + } + + // Create alloc_stack for passing the array elements indirectly. + SILValue allocStack = SILBuilderWithScope(forEachCall) + .createAllocStack(forEachLoc, arrayElementType); + + // Extract the Error and normal targets of the forEach call. Both these + // targets must be taking a phi argument. + SILBasicBlock *normalBB = forEachCall->getNormalBB(); + SILBasicBlock *errorBB = forEachCall->getErrorBB(); + assert(errorBB->getSILPhiArguments().size() == 1 && + normalBB->getSILPhiArguments().size() == 1); + SILPhiArgument *normalArgument = normalBB->getSILPhiArguments()[0]; + SILPhiArgument *errorArgument = errorBB->getSILPhiArguments()[0]; + + // A generator for creating a basic block for use as the target of the + // "normal" branch of a try_apply. + auto normalTargetGenerator = [&](SILBasicBlock *insertionBlock) { + SILBasicBlock *newBB = fun->createBasicBlockBefore(insertionBlock); + newBB->createPhiArgument(normalArgument->getType(), + normalArgument->getOwnershipKind()); + return newBB; + }; + + // A generator for creating a basic block for use as the target of the + // "error" branch of a try_apply. The error block created here will always + // jump to the error target of the original forEach. + auto errorTargetGenerator = [&](SILBasicBlock *insertionBlock) { + SILBasicBlock *newErrorBB = fun->createBasicBlockBefore(insertionBlock); + SILValue argument = newErrorBB->createPhiArgument( + errorArgument->getType(), errorArgument->getOwnershipKind()); + // Make the errorBB jump to the error target of the original forEach. + SILBuilderWithScope builder(newErrorBB, forEachCall); + builder.createBranch(forEachLoc, errorBB, argument); + return newErrorBB; + }; + + // The basic block to jump to in the normal case of the try_apply in each + // unrolling. + SILBasicBlock *nextNormalBB = normalBB; + + // Iterate through the array indices in the reverse order and do the + // following: + // - create a new basic block b_i if i > 0. Let b_0 denote the basic block + // that contains the forEach call. + // - store_borrow the owned copy of the element e_i into the `allocStack`. + // - try_apply the forEach's body closure on the `allocStack`. The normal + // target of the try_apply is b_i+1 if i is not the last index, otherwise + // it is `normalBB`. (The normal target is captured by `nextNormalBB`.) + // Jump to a new error block: err_i in the error case. Note that all + // error blocks jump to the error target of the original forEach call. + for (uint64_t num = arrayInfo.getNumElements(); num > 0; num--) { + SILValue elementCopy = elementCopies[num - 1]; + SILBasicBlock *currentBB = num > 1 ? normalTargetGenerator(nextNormalBB) + : forEachCall->getParentBlock(); + SILBuilderWithScope unrollBuilder(currentBB, forEachCall); + if (arrayElementType.isTrivial(*fun)) { + unrollBuilder.createStore(forEachLoc, elementCopy, allocStack, + StoreOwnershipQualifier::Trivial); + } else { + // Borrow the elementCopy and store it in the allocStack. Note that the + // element's copy is guaranteed to be alive until the array is alive. + // Therefore it is okay to use a borrow here. + SILValue borrowedElem = + unrollBuilder.createBeginBorrow(forEachLoc, elementCopy); + unrollBuilder.createStoreBorrow(forEachLoc, borrowedElem, allocStack); + unrollBuilder.createEndBorrow(forEachLoc, borrowedElem); + } + SILBasicBlock *errorTarget = errorTargetGenerator(nextNormalBB); + // Note that the substitution map must be empty as we are not supporting + // elements of address-only type. All elements in the array are guaranteed + // to be loadable. TODO: generalize this to address-only types. + unrollBuilder.createTryApply(forEachLoc, forEachBodyClosure, + SubstitutionMap(), allocStack, nextNormalBB, + errorTarget); + nextNormalBB = currentBB; + } + + // Dealloc the stack in the normalBB and also in errorBB. Note that every + // try_apply created during the unrolling must pass through these blocks. + SILBuilderWithScope(&normalBB->front()) + .createDeallocStack(forEachLoc, allocStack); + SILBuilderWithScope(&errorBB->front()) + .createDeallocStack(forEachLoc, allocStack); + + // Remove the forEach call as it has now been unrolled. + removeForEachCall(forEachCall, deleter); +} + +/// Determine whether the cost of unrolling the forEach is within the +/// \c unrollThreshold. +static bool canUnrollForEachOfArray(ArrayInfo arrayInfo, SILModule &module) { + const uint64_t unrollThreshold = module.getOptions().UnrollThreshold; + // The cost of unrolling a forEach loop is mostly just two instructions per + // array element: one to store the element into an alloc_stack and another to + // invoke the forEach body closure with the element. Note that the copy_value + // of the element and the basic blocks created for the try-applies of the + // body closure are not counted, as these should likely get optimized away. + const uint64_t cost = 2; + return arrayInfo.getNumElements() * cost <= unrollThreshold; +} + +static bool tryUnrollForEachCallsOverArrayLiteral(ApplyInst *apply, + InstructionDeleter &deleter) { + ArrayInfo arrayInfo; + if (!arrayInfo.tryInitialize(apply)) + return false; + // Bail out, if the array could be modified after initialization. + if (arrayInfo.mayBeModified()) + return false; + // If there are no forEach loops to unroll, return. + ArrayRef forEachCalls = arrayInfo.getForEachUses(); + if (forEachCalls.empty()) + return false; + // If the array is too large to unroll, bail out. + if (!canUnrollForEachOfArray(arrayInfo, apply->getParent()->getModule())) + return false; + for (TryApplyInst *forEachCall : forEachCalls) + unrollForEach(arrayInfo, forEachCall, deleter); + return true; +} + +class ForEachLoopUnroller : public SILFunctionTransform { + + ~ForEachLoopUnroller() override {} + + void run() override { + SILFunction &fun = *getFunction(); + bool changed = false; + + if (!fun.hasOwnership()) + return; + + InstructionDeleter deleter; + for (SILBasicBlock &bb : fun) { + for (auto instIter = bb.begin(); instIter != bb.end();) { + SILInstruction *inst = &*instIter; + ApplyInst *apply = dyn_cast(inst); + if (!apply) { + instIter++; + continue; + } + // Note that the following operation may delete a forEach call but + // would not delete this apply instruction, which is an array + // initializer. Therefore, the iterator should be valid here. + changed |= tryUnrollForEachCallsOverArrayLiteral(apply, deleter); + instIter++; + } + } + + if (changed) { + deleter.cleanUpDeadInstructions(); + PM->invalidateAnalysis(&fun, + SILAnalysis::InvalidationKind::FunctionBody); + } + } +}; + +} // end anonymous namespace + +SILTransform *swift::createForEachLoopUnroll() { + return new ForEachLoopUnroller(); +} diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index f0e90f0d1fc22..67357b3759a1c 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -614,6 +614,7 @@ SILPassPipelinePlan::getSILOptPreparePassPipeline(const SILOptions &Options) { } P.startPipeline("SILOpt Prepare Passes"); + P.addForEachLoopUnroll(); P.addMandatoryCombine(); P.addAccessMarkerElimination(); @@ -673,6 +674,7 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { SILPassPipelinePlan P(Options); P.startPipeline("Mandatory Combines"); + P.addForEachLoopUnroll(); P.addMandatoryCombine(); // First serialize the SIL if we are asked to. diff --git a/lib/SILOptimizer/Utils/InstOptUtils.cpp b/lib/SILOptimizer/Utils/InstOptUtils.cpp index 715ba5de296da..774ede5d58567 100644 --- a/lib/SILOptimizer/Utils/InstOptUtils.cpp +++ b/lib/SILOptimizer/Utils/InstOptUtils.cpp @@ -509,6 +509,21 @@ void InstructionDeleter::recursivelyDeleteUsersIfDead(SILInstruction *inst, deleteIfDead(inst, callback); } +void InstructionDeleter::recursivelyForceDeleteUsersAndFixLifetimes( + SILInstruction *inst, CallbackTy callback) { + for (SILValue result : inst->getResults()) { + while (!result->use_empty()) { + SILInstruction *user = result->use_begin()->getUser(); + recursivelyForceDeleteUsersAndFixLifetimes(user); + } + } + if (isIncidentalUse(inst) || isa(inst)) { + forceDelete(inst); + return; + } + forceDeleteAndFixLifetimes(inst); +} + void swift::eliminateDeadInstruction(SILInstruction *inst, CallbackTy callback) { InstructionDeleter deleter; diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index e2329fd0ca499..e05cc19eb0892 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -710,6 +710,7 @@ extension Sequence { /// /// - Parameter body: A closure that takes an element of the sequence as a /// parameter. + @_semantics("sequence.forEach") @inlinable public func forEach( _ body: (Element) throws -> Void diff --git a/test/SILOptimizer/OSLogPrototypeFullOptTest.swift b/test/SILOptimizer/OSLogPrototypeFullOptTest.swift new file mode 100644 index 0000000000000..8af1817fcf291 --- /dev/null +++ b/test/SILOptimizer/OSLogPrototypeFullOptTest.swift @@ -0,0 +1,174 @@ +// RUN: %target-swift-frontend -emit-ir -swift-version 5 -O -primary-file %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize +// REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos + +// This tests the optimality of the IR generated for the new os log APIs. This +// is not testing the output of a specific optimization pass (which has separate +// tests) but that all optimizations together result in optimal IR. If this test +// fails, it implies that some expected optimizations fail to get triggered on +// os log APIs. TODO: eventually these optimization should also happen in Onone +// mode. + +import OSLogPrototype +import Foundation + +// CHECK-LABEL: define hidden swiftcc void @"${{.*}}testSimpleInterpolation +@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) +func testSimpleInterpolation(h: Logger) { + h.log(level: .debug, "Minimum integer value: \(Int.min)") + // CHECK: entry: + // CHECK-NEXT: [[LOGLEVEL:%.+]] = tail call swiftcc i8 @"$sSo13os_log_type_ta0A0E5debugABvgZ"() + // CHECK-NEXT: tail call swiftcc %TSo9OS_os_logC* @"$s14OSLogPrototype6LoggerV9logObjectSo06OS_os_D0Cvg" + // CHECK-NEXT: [[LOGOBJ:%.+]] = bitcast %TSo9OS_os_logC* + // CHECK-NEXT: tail call zeroext i1 @os_log_type_enabled + // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] + + // CHECK: [[ENABLED]]: + // + // Header bytes. + // + // CHECK-64-NEXT: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i64 12 + // CHECK-32-NEXT: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i32 8 + // CHECK-NEXT: store i8 0, i8* [[BUFFER]], align 1 + // CHECK-NEXT: [[OFFSET1:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 1 + // CHECK-NEXT: store i8 1, i8* [[OFFSET1]], align 1 + // + // Argument bytes. + // + // CHECK-NEXT: [[OFFSET2:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 2 + // CHECK-NEXT: store i8 2, i8* [[OFFSET2]], align 1 + // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 + // CHECK-64-NEXT: store i8 8, i8* [[OFFSET3]], align 1 + // CHECK-32-NEXT: store i8 4, i8* [[OFFSET3]], align 1 + // CHECK-NEXT: [[OFFSET4:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 4 + // CHECK-NEXT: [[BITCASTED:%.+]] = bitcast i8* [[OFFSET4]] to i{{.*}}* + // CHECK-64-NEXT: store i64 -9223372036854775808, i64* [[BITCASTED]], align 1 + // CHECK-32-NEXT: store i32 -2147483648, i32* [[BITCASTED]], align 1 + // CHECK-64-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([35 x i8], [35 x i8]* @{{.*}}, i64 0, i64 0), i8* [[BUFFER]], i32 12) + // CHECK-32-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([35 x i8], [35 x i8]* @{{.*}}, i32 0, i32 0), i8* [[BUFFER]], i32 8) + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* [[BUFFER]] + // CHECK-NEXT: br label %[[NOT_ENABLED]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-NEXT: bitcast + // CHECK-NEXT: tail call void @llvm.objc.release + // CHECK-NEXT: ret void +} + +// CHECK-LABEL: define hidden swiftcc void @"${{.*}}testInterpolationWithMultipleArguments +@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) +func testInterpolationWithMultipleArguments(h: Logger) { + let privateID: Int32 = 0x79abcdef + let filePermissions: Int32 = 0o777 + let pid: Int32 = 122225 + h.log( + level: .error, + """ + Access prevented: process \(pid) initiated by \ + user: \(privateID, privacy: .private) attempted resetting \ + permissions to \(filePermissions) + """) + // CHECK: entry: + // CHECK-NEXT: [[LOGLEVEL:%.+]] = tail call swiftcc i8 @"$sSo13os_log_type_ta0A0E5errorABvgZ"() + // CHECK-NEXT: tail call swiftcc %TSo9OS_os_logC* @"$s14OSLogPrototype6LoggerV9logObjectSo06OS_os_D0Cvg" + // CHECK-NEXT: [[LOGOBJ:%.+]] = bitcast %TSo9OS_os_logC* + // CHECK-NEXT: tail call zeroext i1 @os_log_type_enabled + // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] + + // CHECK: [[ENABLED]]: + // + // Header bytes. + // + // CHECK-NEXT: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i{{.*}} 20 + // CHECK-NEXT: store i8 1, i8* [[BUFFER]], align 1 + // CHECK-NEXT: [[OFFSET1:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 1 + // CHECK-NEXT: store i8 3, i8* [[OFFSET1]], align 1 + // + // First argument bytes. + // + // CHECK-NEXT: [[OFFSET2:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 2 + // CHECK-NEXT: store i8 2, i8* [[OFFSET2]], align 1 + // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 + // CHECK-NEXT: store i8 4, i8* [[OFFSET3]], align 1 + // CHECK-NEXT: [[OFFSET4:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 4 + // CHECK-NEXT: [[BITCASTED:%.+]] = bitcast i8* [[OFFSET4]] to i32* + // CHECK-NEXT: store i32 122225, i32* [[BITCASTED]], align 1 + // + // Second argument + // + // CHECK-NEXT: [[OFFSET12:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 8 + // CHECK-NEXT: store i8 1, i8* [[OFFSET12]], align 1 + // CHECK-NEXT: [[OFFSET13:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 9 + // CHECK-NEXT: store i8 4, i8* [[OFFSET13]], align 1 + // CHECK-NEXT: [[OFFSET14:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 10 + // CHECK-NEXT: [[BITCASTED2:%.+]] = bitcast i8* [[OFFSET14]] to i32* + // CHECK-NEXT: store i32 2041302511, i32* [[BITCASTED2]], align 1 + // + // Third argument + // + // CHECK-NEXT: [[OFFSET22:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 14 + // CHECK-NEXT: store i8 2, i8* [[OFFSET22]], align 1 + // CHECK-NEXT: [[OFFSET23:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 15 + // CHECK-NEXT: store i8 4, i8* [[OFFSET23]], align 1 + // CHECK-NEXT: [[OFFSET24:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 16 + // CHECK-NEXT: [[BITCASTED3:%.+]] = bitcast i8* [[OFFSET24]] to i32* + // CHECK-NEXT: store i32 511, i32* [[BITCASTED3]], align 1 + // + // os_log_impl call. + // CHECK-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([114 x i8], [114 x i8]* @{{.*}}, i{{.*}} 0, i{{.*}} 0), i8* [[BUFFER]], i32 20) + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* [[BUFFER]] + // CHECK-NEXT: br label %[[NOT_ENABLED]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-NEXT: bitcast + // CHECK-NEXT: tail call void @llvm.objc.release + // CHECK-NEXT: ret void +} + +// CHECK-LABEL: define hidden swiftcc void @"${{.*}}testNSObjectInterpolation +@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) +func testNSObjectInterpolation(h: Logger, nsArray: NSArray) { + h.log("NSArray: \(nsArray, privacy: .public)") + // CHECK: entry: + // CHECK-NEXT: bitcast %TSo7NSArrayC* %1 to i8* + // CHECK-NEXT: [[NSARRAY_ARG:%.+]] = tail call i8* @llvm.objc.retain + // CHECK-NEXT: [[LOGLEVEL:%.+]] = tail call swiftcc i8 @"$sSo13os_log_type_ta0A0E7defaultABvgZ"() + // CHECK-NEXT: tail call swiftcc %TSo9OS_os_logC* @"$s14OSLogPrototype6LoggerV9logObjectSo06OS_os_D0Cvg" + // CHECK-NEXT: [[LOGOBJ:%.+]] = bitcast %TSo9OS_os_logC* + // CHECK-NEXT: tail call zeroext i1 @os_log_type_enabled + // CHECK-NEXT: br i1 {{%.*}}, label %[[ENABLED:[0-9]+]], label %[[NOT_ENABLED:[0-9]+]] + + // CHECK: [[ENABLED]]: + // + // Header bytes. + // + // CHECK-64-NEXT: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i64 12 + // CHECK-32-NEXT: [[BUFFER:%.+]] = tail call noalias i8* @swift_slowAlloc(i32 8 + // CHECK-NEXT: store i8 2, i8* [[BUFFER]], align 1 + // CHECK-NEXT: [[OFFSET1:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 1 + // CHECK-NEXT: store i8 1, i8* [[OFFSET1]], align 1 + // + // Argument bytes. + // + // CHECK-NEXT: [[OFFSET2:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 2 + // CHECK-NEXT: store i8 66, i8* [[OFFSET2]], align 1 + // CHECK-NEXT: [[OFFSET3:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 3 + // CHECK-64-NEXT: store i8 8, i8* [[OFFSET3]], align 1 + // CHECK-32-NEXT: store i8 4, i8* [[OFFSET3]], align 1 + // CHECK-NEXT: [[OFFSET4:%.+]] = getelementptr inbounds i8, i8* [[BUFFER]], i{{.*}} 4 + // CHECK-NEXT: [[BITCASTED_DEST:%.+]] = bitcast i8* [[OFFSET4]] to %TSo7NSArrayC** + // CHECK-NEXT: [[BITCASTED_SRC:%.+]] = bitcast i8* [[NSARRAY_ARG]] to %TSo7NSArrayC* + // CHECK-NEXT: store %TSo7NSArrayC* [[BITCASTED_SRC]], %TSo7NSArrayC** [[BITCASTED_DEST]], align 1 + // CHECK-64-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i64 0, i64 0), i8* [[BUFFER]], i32 12) + // CHECK-32-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i32 0, i32 0), i8* [[BUFFER]], i32 8) + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* [[BUFFER]] + // CHECK-NEXT: br label %[[NOT_ENABLED]] + + // CHECK: [[NOT_ENABLED]]: + // CHECK-NEXT: tail call void @llvm.objc.release(i8* [[NSARRAY_ARG]]) + // CHECK-NEXT: bitcast + // CHECK-NEXT: tail call void @llvm.objc.release + // CHECK-NEXT: ret void +} + +// TODO: add test for String. It is more complicated due to more complex logic +// in string serialization. diff --git a/test/SILOptimizer/for_each_loop_unroll_test.sil b/test/SILOptimizer/for_each_loop_unroll_test.sil new file mode 100644 index 0000000000000..0414fa420bd36 --- /dev/null +++ b/test/SILOptimizer/for_each_loop_unroll_test.sil @@ -0,0 +1,225 @@ +// RUN: %target-sil-opt -for-each-loop-unroll -enable-sil-verify-all %s 2>&1 | %FileCheck %s + +// SIL tests for the ForEachLoopUnroll mandatory optimization pass that unrolls +// Sequence.forEach calls over array literals. + +import Swift +import Builtin + +sil [_semantics "array.uninitialized_intrinsic"] @_allocateUninitializedArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + +sil [_semantics "sequence.forEach"] @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error + +sil @forEachBody : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error + +sil hidden [ossa] @forEachLoopUnrollTest : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Word, 2 + %1 = function_ref @_allocateUninitializedArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %2 = apply %1(%0) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + (%3, %4) = destructure_tuple %2 : $(Array, Builtin.RawPointer) + %5 = pointer_to_address %4 : $Builtin.RawPointer to [strict] $*Builtin.Int64 + %6 = integer_literal $Builtin.Int64, 15 + store %6 to [trivial] %5 : $*Builtin.Int64 + %12 = integer_literal $Builtin.Word, 1 + %13 = index_addr %5 : $*Builtin.Int64, %12 : $Builtin.Word + %14 = integer_literal $Builtin.Int64, 27 + store %14 to [trivial] %13 : $*Builtin.Int64 + %21 = begin_borrow %3 : $Array + %22 = alloc_stack $Array + %23 = store_borrow %21 to %22 : $*Array + %24 = function_ref @forEachBody : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error + %25 = convert_function %24 : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error to $@convention(thin) @noescape (@in_guaranteed Builtin.Int64) -> @error Error + %26 = thin_to_thick_function %25 : $@convention(thin) @noescape (@in_guaranteed Builtin.Int64) -> @error Error to $@noescape @callee_guaranteed (@in_guaranteed Builtin.Int64) -> @error Error + // A stub for Sequence.forEach(_:) + %30 = function_ref @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error + try_apply %30<[Builtin.Int64]>(%26, %22) : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error, normal bb1, error bb2 + +bb1(%32 : $()): + dealloc_stack %22 : $*Array + end_borrow %21 : $Array + destroy_value %3 : $Array + %37 = tuple () + return %37 : $() + +bb2(%39 : @owned $Error): + unreachable +} +// CHECK-LABEL: forEachLoopUnrollTest +// CHECK: [[LIT1:%[0-9]+]] = integer_literal $Builtin.Int64, 15 +// CHECK: [[LIT2:%[0-9]+]] = integer_literal $Builtin.Int64, 27 +// CHECK: [[BODYCLOSURE:%[0-9]+]] = thin_to_thick_function +// CHECK-NOT: forEach +// CHECK: [[STACK:%[0-9]+]] = alloc_stack $Builtin.Int64 +// CHECK: store [[LIT1]] to [trivial] [[STACK]] +// CHECK: try_apply [[BODYCLOSURE]]([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed Builtin.Int64) -> @error Error, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + +// CHECK: [[ERROR]]([[ERRPARAM:%[0-9]+]] : @owned $Error): +// CHECK: br [[ERROR3:bb[0-9]+]]([[ERRPARAM]] : $Error) + +// CHECK: [[NORMAL]](%{{.*}} : $()): +// CHECK: store [[LIT2]] to [trivial] [[STACK]] : $*Builtin.Int64 +// CHECK: try_apply [[BODYCLOSURE]]([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed Builtin.Int64) -> @error Error, normal [[NORMAL2:bb[0-9]+]], error [[ERROR2:bb[0-9]+]] + +// CHECK: [[ERROR2]]([[ERRPARAM2:%[0-9]+]] : @owned $Error): +// CHECK: br [[ERROR3:bb[0-9]+]]([[ERRPARAM2]] : $Error) + +// CHECK: [[NORMAL2]](%{{.*}} : $()): +// CHECK: dealloc_stack [[STACK]] +// Note that the temporary alloc_stack of the array created for the forEach call +// will be cleaned up when the forEach call is removed. +// CHECK: destroy_value + +// CHECK: [[ERROR3]]([[ERRPARAM3:%[0-9]+]] : @owned $Error): +// CHECK: dealloc_stack [[STACK]] +// CHECK: unreachable + +sil @forEachBody2 : $@convention(thin) (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error + +sil hidden [ossa] @nonTrivialForEachLoopUnrollTest : $@convention(thin) (@owned @callee_guaranteed () -> @out Int, @owned @callee_guaranteed () -> @out Int) -> () { +bb0(%0: @owned $@callee_guaranteed () -> @out Int, %1: @owned $@callee_guaranteed () -> @out Int): + %2 = integer_literal $Builtin.Word, 2 + %3 = function_ref @_allocateUninitializedArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %4 = apply %3<() -> Int>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + (%5, %6) = destructure_tuple %4 : $(Array<()->Int>, Builtin.RawPointer) + %7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*@callee_guaranteed () -> @out Int + store %0 to [init] %7 : $*@callee_guaranteed () -> @out Int + %12 = integer_literal $Builtin.Word, 1 + %13 = index_addr %7 : $*@callee_guaranteed () -> @out Int, %12 : $Builtin.Word + store %1 to [init] %13 : $*@callee_guaranteed () -> @out Int + %21 = begin_borrow %5 : $Array<()->Int> + %22 = alloc_stack $Array<()->Int> + %23 = store_borrow %21 to %22 : $*Array<()->Int> + %24 = function_ref @forEachBody2 : $@convention(thin) (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error + %25 = convert_function %24 : $@convention(thin) (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error to $@convention(thin) @noescape (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error + %26 = thin_to_thick_function %25 : $@convention(thin) @noescape (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error to $@noescape @callee_guaranteed (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error + // A stub for Sequence.forEach(_:) + %30 = function_ref @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error + try_apply %30<[() -> Int]>(%26, %22) : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error, normal bb1, error bb2 + +bb1(%32 : $()): + dealloc_stack %22 : $*Array<() -> Int> + end_borrow %21 : $Array<() -> Int> + destroy_value %5 : $Array<() -> Int> + %37 = tuple () + return %37 : $() + +bb2(%39 : @owned $Error): + unreachable +} +// CHECK-LABEL: nonTrivialForEachLoopUnrollTest +// CHECK: [[ELEM1:%[0-9]+]] = copy_value %0 +// CHECK-NEXT: store %0 to [init] %{{.*}} : $*@callee_guaranteed () -> @out Int +// CHECK: [[ELEM2:%[0-9]+]] = copy_value %1 +// CHECK-NEXT: store %1 to [init] %{{.*}} : $*@callee_guaranteed () -> @out Int +// CHECK: [[BODYCLOSURE:%[0-9]+]] = thin_to_thick_function +// CHECK-NOT: forEach +// CHECK: [[STACK:%[0-9]+]] = alloc_stack $@callee_guaranteed () -> @out Int +// CHECK: [[ELEM1BORROW:%[0-9]+]] = begin_borrow [[ELEM1]] +// CHECK: store_borrow [[ELEM1BORROW]] to [[STACK]] +// CHECK: end_borrow [[ELEM1BORROW]] +// CHECK: try_apply [[BODYCLOSURE]]([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + +// CHECK: [[ERROR]]([[ERRPARAM:%[0-9]+]] : @owned $Error): +// CHECK: br [[ERROR3:bb[0-9]+]]([[ERRPARAM]] : $Error) + +// CHECK: [[NORMAL]](%{{.*}} : $()): +// CHECK: [[ELEM2BORROW:%[0-9]+]] = begin_borrow [[ELEM2]] +// CHECK: store_borrow [[ELEM2BORROW]] to [[STACK]] +// CHECK: end_borrow [[ELEM2BORROW]] +// CHECK: try_apply [[BODYCLOSURE]]([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed @callee_guaranteed () -> @out Int) -> @error Error, normal [[NORMAL2:bb[0-9]+]], error [[ERROR2:bb[0-9]+]] + +// CHECK: [[ERROR2]]([[ERRPARAM2:%[0-9]+]] : @owned $Error): +// CHECK: br [[ERROR3:bb[0-9]+]]([[ERRPARAM2]] : $Error) + +// CHECK: [[NORMAL2]](%{{.*}} : $()): +// CHECK: dealloc_stack [[STACK]] +// Note that the temporary alloc_stack of the array created for the forEach call +// will be cleaned up when the forEach call is removed. +// CHECK: destroy_value + +// CHECK: [[ERROR3]]([[ERRPARAM3:%[0-9]+]] : @owned $Error): +// CHECK: dealloc_stack [[STACK]] +// CHECK: unreachable + +sil hidden [ossa] @checkIndirectFixLifetimeUsesAreIgnored : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Word, 2 + %1 = function_ref @_allocateUninitializedArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %2 = apply %1(%0) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + (%3, %4) = destructure_tuple %2 : $(Array, Builtin.RawPointer) + %5 = pointer_to_address %4 : $Builtin.RawPointer to [strict] $*Builtin.Int64 + %6 = integer_literal $Builtin.Int64, 15 + store %6 to [trivial] %5 : $*Builtin.Int64 + %12 = integer_literal $Builtin.Word, 1 + %13 = index_addr %5 : $*Builtin.Int64, %12 : $Builtin.Word + %14 = integer_literal $Builtin.Int64, 27 + store %14 to [trivial] %13 : $*Builtin.Int64 + %21 = begin_borrow %3 : $Array + %22 = alloc_stack $Array + %23 = store_borrow %21 to %22 : $*Array + %24 = function_ref @forEachBody : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error + %25 = convert_function %24 : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error to $@convention(thin) @noescape (@in_guaranteed Builtin.Int64) -> @error Error + %26 = thin_to_thick_function %25 : $@convention(thin) @noescape (@in_guaranteed Builtin.Int64) -> @error Error to $@noescape @callee_guaranteed (@in_guaranteed Builtin.Int64) -> @error Error + // A stub for Sequence.forEach(_:) + %30 = function_ref @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error + try_apply %30<[Builtin.Int64]>(%26, %22) : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error, normal bb1, error bb2 + +bb1(%32 : $()): + // An indirect fixLifetime use + dealloc_stack %22 : $*Array + %33 = alloc_stack $Array + %34 = store_borrow %21 to %33 : $*Array + fix_lifetime %33 : $*Array + dealloc_stack %33 : $*Array + end_borrow %21 : $Array + destroy_value %3 : $Array + %37 = tuple () + return %37 : $() + +bb2(%39 : @owned $Error): + unreachable +} +// CHECK-LABEL: @checkIndirectFixLifetimeUsesAreIgnored +// CHECK-NOT: function_ref @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error +// CHECK: end sil function 'checkIndirectFixLifetimeUsesAreIgnored' + +sil hidden [ossa] @testUnrollOfArrayWithPhiArguments : $@convention(thin) () -> () { +bb0: + %0 = integer_literal $Builtin.Int64, 57 + br bb1(%0 : $Builtin.Int64) + +bb1(%arg : $Builtin.Int64): + %10 = integer_literal $Builtin.Word, 1 + %11 = function_ref @_allocateUninitializedArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + %12 = apply %11(%10) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) + (%13, %14) = destructure_tuple %12 : $(Array, Builtin.RawPointer) + %15 = pointer_to_address %14 : $Builtin.RawPointer to [strict] $*Builtin.Int64 + store %arg to [trivial] %15 : $*Builtin.Int64 + br bb2(%arg : $Builtin.Int64) + +bb2(%arg2 : $Builtin.Int64): + %21 = begin_borrow %13 : $Array + %22 = alloc_stack $Array + %23 = store_borrow %21 to %22 : $*Array + %24 = function_ref @forEachBody : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error + %25 = convert_function %24 : $@convention(thin) (@in_guaranteed Builtin.Int64) -> @error Error to $@convention(thin) @noescape (@in_guaranteed Builtin.Int64) -> @error Error + %26 = thin_to_thick_function %25 : $@convention(thin) @noescape (@in_guaranteed Builtin.Int64) -> @error Error to $@noescape @callee_guaranteed (@in_guaranteed Builtin.Int64) -> @error Error + // A stub for Sequence.forEach(_:) + %30 = function_ref @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error + try_apply %30<[Builtin.Int64]>(%26, %22) : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error, normal bb3, error bb4 + +bb3(%32 : $()): + dealloc_stack %22 : $*Array + end_borrow %21 : $Array + destroy_value %13 : $Array + %37 = tuple () + return %37 : $() + +bb4(%39 : @owned $Error): + unreachable +} +// CHECK-LABEL: @testUnrollOfArrayWithPhiArguments +// CHECK-NOT: function_ref @forEach : $@convention(method) <τ_0_0 where τ_0_0 : Sequence> (@noescape @callee_guaranteed (@in_guaranteed τ_0_0.Element) -> @error Error, @in_guaranteed τ_0_0) -> @error Error +// CHECK: end sil function 'testUnrollOfArrayWithPhiArguments' + diff --git a/test/SILOptimizer/for_each_loop_unroll_test.swift b/test/SILOptimizer/for_each_loop_unroll_test.swift new file mode 100644 index 0000000000000..195694aceb490 --- /dev/null +++ b/test/SILOptimizer/for_each_loop_unroll_test.swift @@ -0,0 +1,111 @@ +// RUN: %target-swift-frontend -emit-sil -primary-file %s | %FileCheck %s + +// Tests for the ForEachLoopUnroll mandatory optimization pass that unrolls +// Sequence.forEach calls over array literals. + +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0D19LetArrayLiteralTestyyF : $@convention(thin) () -> () +func unrollLetArrayLiteralTest() { + let a = [Int64(15), Int64(27)] + a.forEach { print($0) } + // CHECK: [[LIT1:%[0-9]+]] = integer_literal $Builtin.Int64, 15 + // CHECK: [[INT1:%[0-9]+]] = struct $Int64 ([[LIT1]] : $Builtin.Int64) + // CHECK: [[LIT2:%[0-9]+]] = integer_literal $Builtin.Int64, 27 + // CHECK: [[INT2:%[0-9]+]] = struct $Int64 ([[LIT2]] : $Builtin.Int64) + // CHECK-NOT: forEach + // CHECK: [[STACK:%[0-9]+]] = alloc_stack $Int64 + // CHECK: store [[INT1]] to [[STACK]] + // CHECK: try_apply %{{.*}}([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed Int64) -> @error Error, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + + // CHECK: [[NORMAL]](%{{.*}} : $()): + // CHECK: store [[INT2]] to [[STACK]] : $*Int64 + // CHECK: try_apply {{.*}}([[STACK]]) +} + +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0D35LetArrayLiteralWithVariableElements1x1yys5Int64V_AFtF +func unrollLetArrayLiteralWithVariableElements(x: Int64, y: Int64) { + let a = [x, y] + a.forEach { print($0) } + // CHECK-NOT: forEach + // CHECK: [[STACK:%[0-9]+]] = alloc_stack $Int64 + // CHECK: store %0 to [[STACK]] + // CHECK: try_apply %{{.*}}([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed Int64) -> @error Error, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + + // CHECK: [[NORMAL]](%{{.*}} : $()): + // CHECK: store %1 to [[STACK]] : $*Int64 + // CHECK: try_apply {{.*}}([[STACK]]) +} + +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0D37LetArrayLiteralWithNonTrivialElementsyyF +func unrollLetArrayLiteralWithNonTrivialElements() { + let a = ["a", "aa"] + a.forEach { print($0) } + // CHECK: [[LIT1:%[0-9]+]] = string_literal utf8 "a" + // CHECK: [[STRING_INIT:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRING1:%[0-9]+]] = apply [[STRING_INIT]]([[LIT1]], + + // CHECK: [[LIT2:%[0-9]+]] = string_literal utf8 "aa" + // CHECK: [[STRING_INIT2:%[0-9]+]] = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC + // CHECK: [[STRING2:%[0-9]+]] = apply [[STRING_INIT2]]([[LIT2]], + + // CHECK-NOT: forEach + // CHECK: [[STACK:%[0-9]+]] = alloc_stack $String + // CHECK: store [[STRING1]] to [[STACK]] : $*String + // CHECK: try_apply %{{.*}}([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed String) -> @error Error, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + + // CHECK: [[NORMAL]](%{{.*}} : $()): + // CHECK: store [[STRING2]] to [[STACK]] : $*String + // CHECK: try_apply {{.*}}([[STACK]]) +} + +// This test mimics the array liteal and forEach created by the OSLogOptimization pass. +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0D27LetArrayLiteralWithClosures1i1jys5Int32V_AFtF +func unrollLetArrayLiteralWithClosures(i: Int32, j: Int32) { + let a = [{ i } , { j }] + a.forEach { print($0()) } + // CHECK: [[ALLOCATE:%[0-9]+]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF + // CHECK: [[ARRAYTUP:%[0-9]+]] = apply [[ALLOCATE]]<() -> Int32> + // CHECK: [[STORAGEPTR:%[0-9]+]] = tuple_extract [[ARRAYTUP]] : $(Array<() -> Int32>, Builtin.RawPointer), 1 + // CHECK: [[STORAGEADDR:%[0-9]+]] = pointer_to_address [[STORAGEPTR]] + // CHECK: store [[CLOSURE1:%[0-9]+]] to [[STORAGEADDR]] + // CHECK: [[INDEX1:%[0-9]+]] = index_addr [[STORAGEADDR]] + // CHECK: store [[CLOSURE2:%[0-9]+]] to [[INDEX1]] + + // CHECK-NOT: forEach + // CHECK: [[STACK:%[0-9]+]] = alloc_stack + // CHECK: store [[CLOSURE1]] to [[STACK]] + // CHECK: try_apply %{{.*}}([[STACK]]) : $@noescape @callee_guaranteed (@in_guaranteed {{.*}}) -> @error Error, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]] + + // CHECK: [[NORMAL]](%{{.*}} : $()): + // CHECK: store [[CLOSURE2]] to [[STACK]] + // CHECK: try_apply {{.*}}([[STACK]]) +} + +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0E16NoUnrollScenarioyyF +func testNoUnrollScenario() { + var a = [Int64(15), Int64(27)] + a.append(Int64(7)) + a.forEach { print($0) } + // CHECK: forEach +} + +// FIXME: Currently, array literals with address-only types cannot be unrolled +// as they are initialied using copy_addr instead of store. +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0E27UnrollingOfAddressOnlyTypes1x1yyx_xtlF +func testUnrollingOfAddressOnlyTypes(x: T, y: T) { + let a = [x, y] + a.forEach { print($0) } + // CHECK: forEach +} + +// Check that when there are multiple forEach loops they are unrolled. +// CHECK-LABEL: sil hidden @$s25for_each_loop_unroll_test0D13MultipleLoops1x1y1zys5Int64V_AGSbtF +func unrollMultipleLoops(x: Int64, y: Int64, z: Bool) { + let a = [x, y] + if z { + a.forEach { print($0) } + } else { + a.forEach{ print($0 + 1) } + } + // CHECK-NOT: forEach + // CHECK-LABEL: end sil function '$s25for_each_loop_unroll_test0D13MultipleLoops1x1y1zys5Int64V_AGSbtF' +} diff --git a/test/stdlib/OSLogPrototypeExecTest.swift b/test/stdlib/OSLogPrototypeExecTest.swift index 5739ab0f2435f..59cab77b59248 100644 --- a/test/stdlib/OSLogPrototypeExecTest.swift +++ b/test/stdlib/OSLogPrototypeExecTest.swift @@ -1,6 +1,9 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -swift-version 5 -DPTR_SIZE_%target-ptrsize -o %t/OSLogPrototypeExecTest // RUN: %target-run %t/OSLogPrototypeExecTest +// +// RUN: %target-build-swift %s -O -swift-version 5 -DPTR_SIZE_%target-ptrsize -o %t/OSLogPrototypeExecTest +// RUN: %target-run %t/OSLogPrototypeExecTest // REQUIRES: executable_test // REQUIRES: OS=macosx || OS=ios || OS=tvos || OS=watchos From efd4152061086f597a5f8b748285241b161aea31 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 7 Feb 2020 20:56:32 -0800 Subject: [PATCH 221/237] [Constraint solver] Factor constraint generation logic for solution targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move more constraint generation logic for an expression target into a new generateConstraints() on a solution target, so we can further generalize the main “solve” logic. --- lib/Sema/CSGen.cpp | 48 +++++++++++++++++++++++++++++++++++++ lib/Sema/CSSolver.cpp | 42 ++++---------------------------- lib/Sema/ConstraintSystem.h | 6 +++++ 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 071767fe08891..d9b5bda4f7dec 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -3782,6 +3782,54 @@ static Expr *generateConstraintsFor(ConstraintSystem &cs, Expr *expr, return result; } +bool ConstraintSystem::generateConstraints( + SolutionApplicationTarget &target, + FreeTypeVariableBinding allowFreeTypeVariables) { + if (Expr *expr = target.getAsExpr()) { + // Try to shrink the system by reducing disjunction domains. This + // goes through every sub-expression and generate its own sub-system, to + // try to reduce the domains of those subexpressions. + shrink(expr); + target.setExpr(expr); + + // Generate constraints for the main system. + expr = generateConstraints(expr, target.getDeclContext()); + if (!expr) + return true; + target.setExpr(expr); + + // If there is a type that we're expected to convert to, add the conversion + // constraint. + if (Type convertType = target.getExprConversionType()) { + // Determine whether we know more about the contextual type. + ContextualTypePurpose ctp = target.getExprContextualTypePurpose(); + bool isOpaqueReturnType = target.infersOpaqueReturnType(); + + // Substitute type variables in for unresolved types. + if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { + bool isForSingleExprFunction = (ctp == CTP_ReturnSingleExpr); + auto *convertTypeLocator = getConstraintLocator( + expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); + + convertType = convertType.transform([&](Type type) -> Type { + if (type->is()) { + return createTypeVariable( + convertTypeLocator, TVO_CanBindToNoEscape); + } + return type; + }); + } + + addContextualConversionConstraint(expr, convertType, ctp, + isOpaqueReturnType); + } + + return false; + } + + llvm_unreachable("BOOM"); +} + Expr *ConstraintSystem::generateConstraints(ClosureExpr *closure) { assert(closure->hasSingleExpressionBody()); return generateConstraintsFor(*this, closure->getSingleExpressionBody(), diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index 2e509e4f6ba15..6e0fad21db1de 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -1239,46 +1239,14 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target, assert(!solverState && "cannot be used directly"); // Set up the expression type checker timer. - Expr *expr = target.getAsExpr(); - Timer.emplace(expr, *this); - - // Try to shrink the system by reducing disjunction domains. This - // goes through every sub-expression and generate its own sub-system, to - // try to reduce the domains of those subexpressions. - shrink(expr); - - // Generate constraints for the main system. - if (auto generatedExpr = generateConstraints(expr, DC)) - expr = generatedExpr; - else { - return SolutionResult::forError(); - } + if (Expr *expr = target.getAsExpr()) + Timer.emplace(expr, *this); - // If there is a type that we're expected to convert to, add the conversion - // constraint. - if (Type convertType = target.getExprConversionType()) { - // Determine whether we know more about the contextual type. - ContextualTypePurpose ctp = target.getExprContextualTypePurpose(); - bool isOpaqueReturnType = target.infersOpaqueReturnType(); - - // Substitute type variables in for unresolved types. - if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) { - bool isForSingleExprFunction = (ctp == CTP_ReturnSingleExpr); - auto *convertTypeLocator = getConstraintLocator( - expr, LocatorPathElt::ContextualType(isForSingleExprFunction)); - - convertType = convertType.transform([&](Type type) -> Type { - if (type->is()) - return createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape); - return type; - }); - } - - addContextualConversionConstraint(expr, convertType, ctp, - isOpaqueReturnType); - } + if (generateConstraints(target, allowFreeTypeVariables)) + return SolutionResult::forError();; // Notify the listener that we've built the constraint system. + Expr *expr = target.getAsExpr(); if (listener && listener->builtConstraints(*this, expr)) { return SolutionResult::forError(); } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index 13af1e1bd582d..cc001d362e621 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -3231,6 +3231,12 @@ class ConstraintSystem { return allocateCopy(vec.begin(), vec.end()); } + /// Generate constraints for the given solution target. + /// + /// \returns true if an error occurred, false otherwise. + bool generateConstraints(SolutionApplicationTarget &target, + FreeTypeVariableBinding allowFreeTypeVariables); + /// Generate constraints for the body of the given single-statement closure. /// /// \returns a possibly-sanitized expression, or null if an error occurred. From 7c3cf1d17bdd4211d0108586a24ddb1f3d55edbe Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 7 Feb 2020 20:31:44 -0500 Subject: [PATCH 222/237] ClangImporter: Fix mirroring of instance properties as static methods on NSObject Because all metaclasses ultimately inherit from NSObject, instance members of NSObject are also visible as static members of NSObject. If the instance member is a property, we import the getter as an ordinary static method, and not a static property. The lazy loading path normally checks for the presence of alternate decls with the same name, but it was failing to do this check if the imported decl was a property and the alternate decl was attached to the accessor and not the property itself. This wasn't a problem until recently, because we weren't lazy loading members of NSObject itself, since it had protocol conformances; now that we are, this problem was exposed. Fixes . --- lib/ClangImporter/ClangImporter.cpp | 11 +++++++++++ .../mirror_instance_property_static_method.swift | 9 +++++++++ .../clang-importer-sdk/usr/include/objc/NSObject.h | 4 ++++ 3 files changed, 24 insertions(+) create mode 100644 test/ClangImporter/mirror_instance_property_static_method.swift diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 09575f46b201e..6cc87200364bd 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3787,6 +3787,17 @@ ClangImporter::Implementation::loadNamedMembers( Members.push_back(V); } } + + // If the property's accessors have alternate decls, we might have + // to import those too. + if (auto *ASD = dyn_cast(TD)) { + for (auto *AD : ASD->getAllAccessors()) { + for (auto *D : getAlternateDecls(AD)) { + if (D->getBaseName() == N) + Members.push_back(D); + } + } + } } } diff --git a/test/ClangImporter/mirror_instance_property_static_method.swift b/test/ClangImporter/mirror_instance_property_static_method.swift new file mode 100644 index 0000000000000..7a73bcb7065f3 --- /dev/null +++ b/test/ClangImporter/mirror_instance_property_static_method.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil %s -verify +// REQUIRES: objc_interop + +import Foundation + +func mirrorInstancePropertyAsStaticMethod() { + // Instance properties are mirrored as static _methods_. Make sure this works. + let _: AnyClass = NSObject.classForCoder() +} diff --git a/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h b/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h index a403cccf01ea5..a86f7b8851669 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h +++ b/test/Inputs/clang-importer-sdk/usr/include/objc/NSObject.h @@ -27,6 +27,10 @@ @property (readonly) NSInteger hash; @end +@interface NSObject (Coding) +- (Class)classForCoder; +@end + @interface A : NSObject - (int)method:(int)arg withDouble:(double)d; + (int)classMethod; From 614f618e5d1934a12bca525dff5734727674fa01 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 7 Feb 2020 21:32:13 -0800 Subject: [PATCH 223/237] [Constraint system] Allow recording pattern types in the system. --- lib/Sema/ConstraintSystem.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index cc001d362e621..b47deccdf3e69 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -774,8 +774,8 @@ struct Score { /// An AST node that can gain type information while solving. using TypedNode = - llvm::PointerUnion3; + llvm::PointerUnion4; /// Display a score. llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score); @@ -1164,7 +1164,7 @@ class SolutionApplicationTarget { Pattern *pattern = nullptr; /// Whether the expression result will be discarded at the end. - bool isDiscarded; + bool isDiscarded = false; } expression; struct { From aea86ec2c181e42eca926c9f6802e8e3237d06d8 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 7 Feb 2020 21:35:33 -0800 Subject: [PATCH 224/237] CI: expand `%ProgramFiles%` manually This expands `%ProgramFiles%` manually since it is getting expanded to `C:\Program Files (x86)` rather than `C:\Program Files` as expected. Although this is less portable, this will at least enable the CI to run properly. --- utils/build-windows.bat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 4ff92d1cda1b6..1fcb8a3d4a186 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -67,7 +67,7 @@ call :build_lldb %exitOnError% call :build_libdispatch %exitOnError% -path %source_root%\icu-%icu_version%\bin64;%install_directory%\bin;%build_root%\swift\bin;%build_root%\swift\libdispatch-prefix\bin;%PATH%;%ProgramFiles%\Git\usr\bin +path %source_root%\icu-%icu_version%\bin64;%install_directory%\bin;%build_root%\swift\bin;%build_root%\swift\libdispatch-prefix\bin;%PATH%;C:\Program Files\Git\usr\bin call :test_swift %exitOnError% call :test_libdispatch %exitOnError% @@ -107,7 +107,7 @@ set file_name=icu4c-%icu_version%-Win64-MSVC2017.zip curl -L -O "https://github.com/unicode-org/icu/releases/download/release-%icu_version_dashed%/%file_name%" %exitOnError% :: unzip warns about the paths in the zip using slashes, which raises the :: errorLevel to 1. We cannot use exitOnError, and have to ignore errors. -"%ProgramFiles%\Git\usr\bin\unzip.exe" -o %file_name% -d "%source_root%\icu-%icu_version%" +"C:\Program Files\Git\usr\bin\unzip.exe" -o %file_name% -d "%source_root%\icu-%icu_version%" exit /b 0 goto :eof @@ -121,7 +121,7 @@ setlocal enableextensions enabledelayedexpansion set file_name=sqlite-amalgamation-3270200.zip curl -L -O "https://www.sqlite.org/2019/%file_name%" %exitOnError% -"%ProgramFiles%\Git\usr\bin\unzip.exe" -o %file_name% %exitOnError% +"C:\Program Files\Git\usr\bin\unzip.exe" -o %file_name% %exitOnError% goto :eof endlocal From d70c055888326617fe744234bbbe6d5df14a3fac Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 7 Feb 2020 22:43:28 -0800 Subject: [PATCH 225/237] [Constraint system] Sink more init logic into SolutionApplicationTarget More steps toward eliminating ExprTypeCheckListener. --- lib/Sema/ConstraintSystem.cpp | 54 +++++++++++++++++++++ lib/Sema/ConstraintSystem.h | 21 ++++++++- lib/Sema/TypeCheckConstraints.cpp | 78 ++++++------------------------- 3 files changed, 86 insertions(+), 67 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index b81236a8e163e..11358c3bd4e46 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3994,9 +3994,62 @@ SolutionApplicationTarget::SolutionApplicationTarget( expression.dc = dc; expression.contextualPurpose = contextualPurpose; expression.convertType = convertType; + expression.pattern = nullptr; + expression.wrappedVar = nullptr; expression.isDiscarded = isDiscarded; } +void SolutionApplicationTarget::maybeApplyPropertyWrapper() { + assert(kind == Kind::expression); + assert(expression.contextualPurpose == CTP_Initialization); + auto singleVar = expression.pattern->getSingleVar(); + if (!singleVar) + return; + + auto wrapperAttrs = singleVar->getAttachedPropertyWrappers(); + if (wrapperAttrs.empty()) + return; + + // If the outermost property wrapper is directly initialized, form the + // call. + auto &ctx = singleVar->getASTContext(); + auto outermostWrapperAttr = wrapperAttrs.front(); + Expr *backingInitializer; + if (Expr *initializer = expression.expression) { + // Form init(wrappedValue:) call(s). + Expr *wrappedInitializer = + buildPropertyWrapperInitialValueCall( + singleVar, Type(), initializer, /*ignoreAttributeArgs=*/false); + if (!wrappedInitializer) + return; + + backingInitializer = wrappedInitializer; + } else if (auto outermostArg = outermostWrapperAttr->getArg()) { + Type outermostWrapperType = + singleVar->getAttachedPropertyWrapperType(0); + if (!outermostWrapperType) + return; + + auto typeExpr = TypeExpr::createImplicitHack( + outermostWrapperAttr->getTypeLoc().getLoc(), + outermostWrapperType, ctx); + backingInitializer = CallExpr::create( + ctx, typeExpr, outermostArg, + outermostWrapperAttr->getArgumentLabels(), + outermostWrapperAttr->getArgumentLabelLocs(), + /*hasTrailingClosure=*/false, + /*implicit=*/false); + } else { + llvm_unreachable("No initializer anywhere?"); + } + wrapperAttrs[0]->setSemanticInit(backingInitializer); + + // Note that we have applied to property wrapper, so we can adjust + // the initializer type later. + expression.wrappedVar = singleVar; + expression.expression = backingInitializer; +} + SolutionApplicationTarget SolutionApplicationTarget::forInitialization( Expr *initializer, DeclContext *dc, Type patternType, Pattern *pattern) { // Determine the contextual type for the initialization. @@ -4020,6 +4073,7 @@ SolutionApplicationTarget SolutionApplicationTarget::forInitialization( initializer, dc, CTP_Initialization, contextualType, /*isDiscarded=*/false); target.expression.pattern = pattern; + target.maybeApplyPropertyWrapper(); return target; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index b47deccdf3e69..50ee0f24f53f7 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -1161,10 +1161,14 @@ class SolutionApplicationTarget { /// When initializing a pattern from the expression, this is the /// pattern. - Pattern *pattern = nullptr; + Pattern *pattern; + + /// The variable to which property wrappers have been applied, if + /// this is an initialization involving a property wrapper. + VarDecl *wrappedVar; /// Whether the expression result will be discarded at the end. - bool isDiscarded = false; + bool isDiscarded; } expression; struct { @@ -1173,6 +1177,11 @@ class SolutionApplicationTarget { } function; }; + // If the pattern contains a single variable that has an attached + // property wrapper, set up the initializer expression to initialize + // the backing storage. + void maybeApplyPropertyWrapper(); + public: SolutionApplicationTarget(Expr *expr, DeclContext *dc, ContextualTypePurpose contextualPurpose, @@ -1281,6 +1290,14 @@ class SolutionApplicationTarget { isa(expression.pattern); } + /// Retrieve the wrapped variable when initializing a pattern with a + /// property wrapper. + VarDecl *getInitializationWrappedVar() const { + assert(kind == Kind::expression); + assert(expression.contextualPurpose == CTP_Initialization); + return expression.wrappedVar; + } + /// Whether this context infers an opaque return type. bool infersOpaqueReturnType() const; diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index ba95206b01675..6be9ba7194bfd 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2474,8 +2474,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, /// Type checking listener for pattern binding initializers. class BindingListener : public ExprTypeCheckListener { ASTContext &context; - Pattern *&pattern; - Expr *&initializer; + + SolutionApplicationTarget target; /// The locator we're using. ConstraintLocator *Locator; @@ -2487,11 +2487,9 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, VarDecl *wrappedVar = nullptr; public: - explicit BindingListener(ASTContext &ctx, Pattern *&pattern, - Expr *&initializer) - : context(ctx), pattern(pattern), initializer(initializer), - Locator(nullptr) { - maybeApplyPropertyWrapper(); + explicit BindingListener(ASTContext &ctx, SolutionApplicationTarget target) + : context(ctx), target(target), Locator(nullptr) { + wrappedVar = target.getInitializationWrappedVar(); } /// Retrieve the type to which the pattern should be coerced. @@ -2505,7 +2503,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, if (cs) { Type valueType = LValueType::get(initType); auto dc = wrappedVar->getInnermostDeclContext(); - auto *loc = cs->getConstraintLocator(initializer); + auto *loc = cs->getConstraintLocator(target.getAsExpr()); for (unsigned i : indices(wrappedVar->getAttachedPropertyWrappers())) { auto wrapperInfo = wrappedVar->getAttachedPropertyWrapperTypeInfo(i); @@ -2538,7 +2536,8 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, Locator = cs.getConstraintLocator(expr, LocatorPathElt::ContextualType()); // Collect constraints from the pattern. - Type patternType = cs.generateConstraints(pattern, Locator); + Type patternType = + cs.generateConstraints(target.getInitializationPattern(), Locator); if (!patternType) return true; @@ -2563,7 +2562,6 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, } // The expression has been pre-checked; save it in case we fail later. - initializer = expr; return false; } @@ -2593,70 +2591,20 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer, ->setSemanticInit(expr); } - initializer = expr; return expr; } - - private: - // If the pattern contains a single variable that has an attached - // property wrapper, set up the initializer expression to initialize - // the backing storage. - void maybeApplyPropertyWrapper() { - auto singleVar = pattern->getSingleVar(); - if (!singleVar) - return; - - auto wrapperAttrs = singleVar->getAttachedPropertyWrappers(); - if (wrapperAttrs.empty()) - return; - - // If the outermost property wrapper is directly initialized, form the - // call. - auto &ctx = singleVar->getASTContext(); - auto outermostWrapperAttr = wrapperAttrs.front(); - if (initializer) { - // Form init(wrappedValue:) call(s). - Expr *wrappedInitializer = - buildPropertyWrapperInitialValueCall( - singleVar, Type(), initializer, /*ignoreAttributeArgs=*/false); - if (!wrappedInitializer) - return; - - initializer = wrappedInitializer; - } else if (auto outermostArg = outermostWrapperAttr->getArg()) { - Type outermostWrapperType = - singleVar->getAttachedPropertyWrapperType(0); - if (!outermostWrapperType) - return; - - auto typeExpr = TypeExpr::createImplicitHack( - outermostWrapperAttr->getTypeLoc().getLoc(), - outermostWrapperType, ctx); - initializer = CallExpr::create( - ctx, typeExpr, outermostArg, - outermostWrapperAttr->getArgumentLabels(), - outermostWrapperAttr->getArgumentLabelLocs(), - /*hasTrailingClosure=*/false, - /*implicit=*/false); - } else { - llvm_unreachable("No initializer anywhere?"); - } - wrapperAttrs[0]->setSemanticInit(initializer); - - // Note that we have applied to property wrapper, so we can adjust - // the initializer type later. - wrappedVar = singleVar; - } }; auto &Context = DC->getASTContext(); - BindingListener listener(Context, pattern, initializer); + auto target = SolutionApplicationTarget::forInitialization( + initializer, DC, patternType, pattern); + initializer = target.getAsExpr(); + + BindingListener listener(Context, target); if (!initializer) return true; // Type-check the initializer. - auto target = SolutionApplicationTarget::forInitialization( - initializer, DC, patternType, pattern); bool unresolvedTypeExprs = false; auto resultTarget = typeCheckExpression(target, unresolvedTypeExprs, None, &listener); From de5bc086908960f6bd48a07433fa926f1d72cb6f Mon Sep 17 00:00:00 2001 From: Ross Bayer Date: Fri, 7 Feb 2020 23:04:21 -0800 Subject: [PATCH 226/237] [Python: black] Update the utils/python_format.py script to accept a list of input paths which narrows down the complete list of known Python sources in the project. --- utils/python_format.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/utils/python_format.py b/utils/python_format.py index 3b46d245d54d2..da966bf2bc803 100644 --- a/utils/python_format.py +++ b/utils/python_format.py @@ -28,7 +28,9 @@ _SWIFT_PATH / "benchmark/scripts/Benchmark_Driver", _SWIFT_PATH / "benchmark/scripts/Benchmark_DTrace.in", _SWIFT_PATH / "benchmark/scripts/Benchmark_GuardMalloc.in", + _SWIFT_PATH / "benchmark/scripts/Benchmark_QuickCheck.in", _SWIFT_PATH / "benchmark/scripts/Benchmark_RuntimeLeaksRunner.in", + _SWIFT_PATH / "benchmark/scripts/run_smoke_bench", _SWIFT_PATH / "docs/scripts/ns-html2rst", _SWIFT_PATH / "test/Driver/Inputs/fake-toolchain/ld", _SWIFT_PATH / "utils/80+-check", @@ -100,6 +102,14 @@ def _is_package_installed(name): def parse_args(): parser = argparse.ArgumentParser() + parser.add_argument( + "paths", + type=Path, + metavar="PATH", + nargs="*", + help="Source path to format.", + ) + parser.add_argument( "--check", action="store_true", @@ -110,7 +120,7 @@ def parse_args(): "-v", "--verbose", action="store_true", - help="Also emit messages to stderr about files that were not changed", + help="Emit messages to stderr about files that were not changed.", ) return parser.parse_args() @@ -136,7 +146,23 @@ def main(): if args.verbose: command.append("--verbose") - command += [str(path) for path in _get_python_sources()] + requested_paths = [path.resolve() for path in args.paths] + + # Narrow down the set of paths to format to only those paths which are either + # included in the set of requested paths or are subpaths of the requested paths. + format_paths = { + known_path + for path in requested_paths + for known_path in _get_python_sources() + if path == known_path or path in known_path.parents + } + + # Add requested paths that exists, but aren't included in the format set. + for path in requested_paths: + if path not in format_paths and path.exists(): + format_paths.add(path) + + command += sorted([str(path) for path in format_paths]) return subprocess.call(command) From a4486567d8ea3feaf01479b7e4ee0c57593eb4da Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 8 Feb 2020 09:14:23 -0800 Subject: [PATCH 227/237] CI: attempt to fix the index for swift The file endings matter for some of the tests. Try a different approach to refreshing the index. --- utils/build-windows.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-windows.bat b/utils/build-windows.bat index 1fcb8a3d4a186..7bfc589f89c86 100644 --- a/utils/build-windows.bat +++ b/utils/build-windows.bat @@ -82,7 +82,7 @@ setlocal enableextensions enabledelayedexpansion git -C "%source_root%\swift" config --local core.autocrlf input git -C "%source_root%\swift" config --local core.symlink true -git -C "%source_root%\swift" checkout HEAD +git -C "%source_root%\swift" checkout-index --force --all git clone --depth 1 --single-branch https://github.com/apple/swift-cmark cmark %exitOnError% git clone --depth 1 --single-branch --branch swift/master https://github.com/apple/llvm-project llvm-project %exitOnError% From db47e450b7494e2e167bccafb3493efb99503332 Mon Sep 17 00:00:00 2001 From: Ravi Kandhadai Date: Sat, 8 Feb 2020 13:55:27 -0800 Subject: [PATCH 228/237] [Tests][SILOptimizer] Make the new OSLogPrototypeFullOptTest.swift test suite skip the optional "nonnull" attribute added to the buffer pointer in the LLVM IR. --- .../SILOptimizer/OSLogPrototypeFullOptTest.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/SILOptimizer/OSLogPrototypeFullOptTest.swift b/test/SILOptimizer/OSLogPrototypeFullOptTest.swift index 8af1817fcf291..afb24d0afe8b8 100644 --- a/test/SILOptimizer/OSLogPrototypeFullOptTest.swift +++ b/test/SILOptimizer/OSLogPrototypeFullOptTest.swift @@ -43,9 +43,9 @@ func testSimpleInterpolation(h: Logger) { // CHECK-NEXT: [[BITCASTED:%.+]] = bitcast i8* [[OFFSET4]] to i{{.*}}* // CHECK-64-NEXT: store i64 -9223372036854775808, i64* [[BITCASTED]], align 1 // CHECK-32-NEXT: store i32 -2147483648, i32* [[BITCASTED]], align 1 - // CHECK-64-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([35 x i8], [35 x i8]* @{{.*}}, i64 0, i64 0), i8* [[BUFFER]], i32 12) - // CHECK-32-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([35 x i8], [35 x i8]* @{{.*}}, i32 0, i32 0), i8* [[BUFFER]], i32 8) - // CHECK-NEXT: tail call void @swift_slowDealloc(i8* [[BUFFER]] + // CHECK-64-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([35 x i8], [35 x i8]* @{{.*}}, i64 0, i64 0), i8* {{(nonnull )?}}[[BUFFER]], i32 12) + // CHECK-32-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([35 x i8], [35 x i8]* @{{.*}}, i32 0, i32 0), i8* {{(nonnull )?}}[[BUFFER]], i32 8) + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] // CHECK-NEXT: br label %[[NOT_ENABLED]] // CHECK: [[NOT_ENABLED]]: @@ -114,8 +114,8 @@ func testInterpolationWithMultipleArguments(h: Logger) { // CHECK-NEXT: store i32 511, i32* [[BITCASTED3]], align 1 // // os_log_impl call. - // CHECK-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([114 x i8], [114 x i8]* @{{.*}}, i{{.*}} 0, i{{.*}} 0), i8* [[BUFFER]], i32 20) - // CHECK-NEXT: tail call void @swift_slowDealloc(i8* [[BUFFER]] + // CHECK-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([114 x i8], [114 x i8]* @{{.*}}, i{{.*}} 0, i{{.*}} 0), i8* {{(nonnull )?}}[[BUFFER]], i32 20) + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] // CHECK-NEXT: br label %[[NOT_ENABLED]] // CHECK: [[NOT_ENABLED]]: @@ -158,9 +158,9 @@ func testNSObjectInterpolation(h: Logger, nsArray: NSArray) { // CHECK-NEXT: [[BITCASTED_DEST:%.+]] = bitcast i8* [[OFFSET4]] to %TSo7NSArrayC** // CHECK-NEXT: [[BITCASTED_SRC:%.+]] = bitcast i8* [[NSARRAY_ARG]] to %TSo7NSArrayC* // CHECK-NEXT: store %TSo7NSArrayC* [[BITCASTED_SRC]], %TSo7NSArrayC** [[BITCASTED_DEST]], align 1 - // CHECK-64-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i64 0, i64 0), i8* [[BUFFER]], i32 12) - // CHECK-32-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i32 0, i32 0), i8* [[BUFFER]], i32 8) - // CHECK-NEXT: tail call void @swift_slowDealloc(i8* [[BUFFER]] + // CHECK-64-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i64 0, i64 0), i8* {{(nonnull )?}}[[BUFFER]], i32 12) + // CHECK-32-NEXT: tail call void @_os_log_impl({{.*}}, {{.*}} [[LOGOBJ]], i8 zeroext [[LOGLEVEL]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @{{.*}}, i32 0, i32 0), i8* {{(nonnull )?}}[[BUFFER]], i32 8) + // CHECK-NEXT: tail call void @swift_slowDealloc(i8* {{(nonnull )?}}[[BUFFER]] // CHECK-NEXT: br label %[[NOT_ENABLED]] // CHECK: [[NOT_ENABLED]]: From 3ac5cebd8f8c505f916f355485c59432cbc55d44 Mon Sep 17 00:00:00 2001 From: David Ungar Date: Fri, 7 Feb 2020 19:43:54 -0800 Subject: [PATCH 229/237] Small fixes and debugging improvements for SwiftDriverTests --- include/swift/AST/FineGrainedDependencies.h | 5 +- .../Driver/FineGrainedDependencyDriverGraph.h | 57 ++++++------- lib/AST/FineGrainedDependencies.cpp | 53 +++++++++++++ lib/AST/SourceFileDepGraphConstructor.cpp | 79 +++++++------------ .../FineGrainedDependencyDriverGraph.cpp | 74 ++++++++++++----- 5 files changed, 171 insertions(+), 97 deletions(-) diff --git a/include/swift/AST/FineGrainedDependencies.h b/include/swift/AST/FineGrainedDependencies.h index 49f0b755f9e39..5f3424a189e84 100644 --- a/include/swift/AST/FineGrainedDependencies.h +++ b/include/swift/AST/FineGrainedDependencies.h @@ -721,7 +721,8 @@ class SourceFileDepGraphNode : public DepGraphNode { } std::string humanReadableName() const { - return DepGraphNode::humanReadableName("here"); + return DepGraphNode::humanReadableName(getIsProvides() ? "here" + : "somewhere else"); } bool verify() const { @@ -883,6 +884,8 @@ class SourceFileDepGraph { bool verifySequenceNumber() const; + void emitDotFile(StringRef outputPath, DiagnosticEngine &diags); + private: void addNode(SourceFileDepGraphNode *n) { n->setSequenceNumber(allNodes.size()); diff --git a/include/swift/Driver/FineGrainedDependencyDriverGraph.h b/include/swift/Driver/FineGrainedDependencyDriverGraph.h index 9d1e9954d2c15..36ab089d6c029 100644 --- a/include/swift/Driver/FineGrainedDependencyDriverGraph.h +++ b/include/swift/Driver/FineGrainedDependencyDriverGraph.h @@ -123,6 +123,8 @@ class ModuleDepGraphNode : public DepGraphNode { return DepGraphNode::humanReadableName(where); } + void dump(raw_ostream &) const; + SWIFT_DEBUG_DUMP; }; @@ -312,9 +314,11 @@ class ModuleDepGraph { } /// For unit tests. - ModuleDepGraph(const bool EnableTypeFingerprints) - : ModuleDepGraph(true, false, EnableTypeFingerprints, false, nullptr) {} - + ModuleDepGraph(const bool EnableTypeFingerprints, + const bool EmitDotFilesForDebugging = false) + : ModuleDepGraph( + true, /*emitFineGrainedDependencyDotFileAfterEveryImport=*/ + EmitDotFilesForDebugging, EnableTypeFingerprints, false, nullptr) {} //============================================================================ // MARK: ModuleDepGraph - updating from a switdeps file @@ -328,18 +332,18 @@ class ModuleDepGraph { /// compensates. Changes loadFromPath(const driver::Job *, StringRef, DiagnosticEngine &); - Changes loadFromSourceFileDepGraph(const driver::Job *cmd, - const SourceFileDepGraph &); + const SourceFileDepGraph &, + DiagnosticEngine &); - /// Also for unit tests - Changes - simulateLoad(const driver::Job *cmd, - llvm::StringMap> simpleNames, - llvm::StringMap>> - compoundNames = {}, - const bool includePrivateDeps = false, - const bool hadCompilationError = false); + /// Also for unit tests + Changes + simulateLoad(const driver::Job *cmd, + llvm::StringMap> simpleNames, + llvm::StringMap>> + compoundNames = {}, + const bool includePrivateDeps = false, + const bool hadCompilationError = false); private: @@ -347,7 +351,8 @@ class ModuleDepGraph { /// and integrate it into the ModuleDepGraph. /// Used both the first time, and to reload the SourceFileDepGraph. /// If any changes were observed, indicate same in the return vale. - Changes loadFromBuffer(const driver::Job *, llvm::MemoryBuffer &); + Changes loadFromBuffer(const driver::Job *, llvm::MemoryBuffer &, + DiagnosticEngine &); /// Integrate a SourceFileDepGraph into the receiver. /// Integration happens when the driver needs to read SourceFileDepGraph. @@ -365,8 +370,9 @@ class ModuleDepGraph { const SourceFileDepGraphNode *integrand) const; /// Integrate the \p integrand into the receiver. - /// Return the changed node if any.. - NullablePtr + /// If an illegal value was found, return \c None, otherwise + /// return the changed node if any.. + Optional> integrateSourceFileDepGraphNode(const SourceFileDepGraph &g, const SourceFileDepGraphNode *integrand, const PreexistingNodeIfAny preexistingMatch, @@ -392,6 +398,12 @@ class ModuleDepGraph { /// After importing a provides node from the frontend, record its /// dependencies. /// Return true if moduleUseNode picks up a new external-dependency + /// + /// \param g The source file graph being integrated into the module graph + /// \param sourceFileUseNode The source file node just integrated, which may + /// also be a use (i.e. a "depends", a declaration used by something else) + /// \param moduleUseNode The module file node corresponding to the \c + /// sourceFileUseNode bool recordWhatUseDependsUpon(const SourceFileDepGraph &g, const SourceFileDepGraphNode *sourceFileUseNode, ModuleDepGraphNode *moduleUseNode); @@ -486,17 +498,8 @@ class ModuleDepGraph { bool haveAnyNodesBeenTraversedIn(const driver::Job *) const; - /// Given a "cascading" job, that is a job whose dependents must be recompiled - /// when this job is recompiled, Compute two sets of jobs: - /// 1. Return value (via visited) is the set of jobs needing recompilation - /// after this one, and - /// 2. Jobs not previously known to need dependencies reexamined after they - /// are recompiled. - /// - /// Returns jobs to be run because of changes to any/ever node in the - /// argument. Only return jobs marked that were previously unmarked, assuming - /// previously marked jobs are already scheduled. - /// TODO: rewrite above comment + /// Find all jobs (possibly including the argument) requiring recompilation + /// assuming that every entity in \p jobToBeRecompiled has changed. std::vector findJobsToRecompileWhenWholeJobChanges(const driver::Job *jobToBeRecompiled); diff --git a/lib/AST/FineGrainedDependencies.cpp b/lib/AST/FineGrainedDependencies.cpp index 69256f1cffc93..278fa6538a75e 100644 --- a/lib/AST/FineGrainedDependencies.cpp +++ b/lib/AST/FineGrainedDependencies.cpp @@ -15,10 +15,15 @@ #include "swift/AST/FineGrainedDependencies.h" // may not all be needed +#include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/DiagnosticsCommon.h" +#include "swift/AST/DiagnosticsFrontend.h" +#include "swift/AST/FileSystem.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/LLVM.h" #include "swift/Demangling/Demangle.h" #include "swift/Frontend/FrontendOptions.h" + #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" @@ -34,6 +39,28 @@ using namespace swift; using namespace fine_grained_dependencies; +//============================================================================== +// MARK: Emitting and reading SourceFileDepGraph +//============================================================================== + +Optional SourceFileDepGraph::loadFromPath(StringRef path) { + auto bufferOrError = llvm::MemoryBuffer::getFile(path); + if (!bufferOrError) + return None; + return loadFromBuffer(*bufferOrError.get()); +} + +Optional +SourceFileDepGraph::loadFromBuffer(llvm::MemoryBuffer &buffer) { + SourceFileDepGraph fg; + llvm::yaml::Input yamlReader(llvm::MemoryBufferRef(buffer), nullptr); + yamlReader >> fg; + if (yamlReader.error()) + return None; + // return fg; compiles for Mac but not Linux, because it cannot be copied. + return Optional(std::move(fg)); +} + //============================================================================== // MARK: SourceFileDepGraph access //============================================================================== @@ -229,6 +256,23 @@ raw_ostream &fine_grained_dependencies::operator<<(raw_ostream &out, bool DependencyKey::verify() const { assert((getKind() != NodeKind::externalDepend || isInterface()) && "All external dependencies must be interfaces."); + switch (getKind()) { + case NodeKind::topLevel: + case NodeKind::dynamicLookup: + case NodeKind::externalDepend: + case NodeKind::sourceFileProvide: + assert(context.empty() && !name.empty() && "Must only have a name"); + break; + case NodeKind::nominal: + case NodeKind::potentialMember: + assert(!context.empty() && name.empty() && "Must only have a context"); + break; + case NodeKind::member: + assert(!context.empty() && !name.empty() && "Must have both"); + break; + case NodeKind::kindCount: + llvm_unreachable("impossible"); + } return true; } @@ -300,6 +344,15 @@ void SourceFileDepGraph::verifySame(const SourceFileDepGraph &other) const { #endif } +void SourceFileDepGraph::emitDotFile(StringRef outputPath, + DiagnosticEngine &diags) { + std::string dotFileName = outputPath.str() + ".dot"; + withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { + DotFileEmitter(out, *this, false, false).emit(); + return false; + }); +} + //============================================================================== // MARK: SourceFileDepGraph YAML reading & writing //============================================================================== diff --git a/lib/AST/SourceFileDepGraphConstructor.cpp b/lib/AST/SourceFileDepGraphConstructor.cpp index eba5fe33192bb..fc87e2cb59b21 100644 --- a/lib/AST/SourceFileDepGraphConstructor.cpp +++ b/lib/AST/SourceFileDepGraphConstructor.cpp @@ -46,32 +46,6 @@ using namespace swift; using namespace fine_grained_dependencies; -//============================================================================== -// MARK: Emitting and reading SourceFileDepGraph -//============================================================================== - -Optional SourceFileDepGraph::loadFromPath(StringRef path) { - auto bufferOrError = llvm::MemoryBuffer::getFile(path); - if (!bufferOrError) - return None; - return loadFromBuffer(*bufferOrError.get()); -} - -Optional -SourceFileDepGraph::loadFromBuffer(llvm::MemoryBuffer &buffer) { - SourceFileDepGraph fg; - llvm::yaml::Input yamlReader(llvm::MemoryBufferRef(buffer), nullptr); - yamlReader >> fg; - if (yamlReader.error()) - return None; - // return fg; compiles for Mac but not Linux, because it cannot be copied. - return Optional(std::move(fg)); -} - -//============================================================================== -// MARK: Start of SourceFileDepGraph building, specific to status quo -//============================================================================== - //============================================================================== // MARK: Helpers for key construction that must be in frontend //============================================================================== @@ -501,7 +475,7 @@ class SourceFileDepGraphConstructor { /// a flag indicating if the member is private to its enclosing file, and /// a flag indicating if the dependency cascades. const std::vector, bool>> - memberDepends; + dependsWithContexts; /// The base name of a class member depended-upon for dynamic lookup, and a /// cascades flag. @@ -534,7 +508,8 @@ class SourceFileDepGraphConstructor { bool hadCompilationError, const std::string &interfaceHash, ArrayRef> topLevelDepends, - ArrayRef, bool>> memberDepends, + ArrayRef, bool>> + dependsWithContexts, ArrayRef> dynamicLookupDepends, ArrayRef externalDependencies, @@ -554,7 +529,7 @@ class SourceFileDepGraphConstructor { interfaceHash(interfaceHash), topLevelDepends(topLevelDepends), - memberDepends(memberDepends), + dependsWithContexts(dependsWithContexts), dynamicLookupDepends(dynamicLookupDepends), externalDependencies(externalDependencies), @@ -569,11 +544,15 @@ class SourceFileDepGraphConstructor { classMembers(classMembers) {} - SourceFileDepGraphConstructor static forSourceFile(SourceFile *SF, - const DependencyTracker &depTracker, - StringRef swiftDeps, - const bool includePrivateDeps, - const bool hadCompilationError) { +// clang-format off +static SourceFileDepGraphConstructor +forSourceFile( + SourceFile *SF, + const DependencyTracker &depTracker, + StringRef swiftDeps, + const bool includePrivateDeps, + const bool hadCompilationError) { +// clang-format on SourceFileDeclFinder declFinder(SF, includePrivateDeps); std::vector> topLevelDepends; @@ -584,11 +563,11 @@ class SourceFileDepGraphConstructor { for (const auto &p: SF->getReferencedNameTracker()->getDynamicLookupNames()) dynamicLookupDepends.push_back(std::make_pair(p.getFirst().userFacingName(), p.getSecond())); - std::vector, bool>> memberDepends; + std::vector, bool>> dependsWithContexts; for (const auto &p: SF->getReferencedNameTracker()->getUsedMembers()) { const auto &member = p.getFirst().second; StringRef emptyOrUserFacingName = member.empty() ? "" : member.userFacingName(); - memberDepends.push_back( + dependsWithContexts.push_back( std::make_pair( std::make_tuple( mangleTypeAsContext(p.getFirst().first), @@ -604,7 +583,7 @@ class SourceFileDepGraphConstructor { getInterfaceHash(SF), topLevelDepends, - memberDepends, + dependsWithContexts, dynamicLookupDepends, depTracker.getDependencies(), @@ -782,7 +761,7 @@ void SourceFileDepGraphConstructor::addDependencyArcsToGraph() { // TODO: express the multiple provides and depends streams with variadic // templates addAllDependenciesFrom(topLevelDepends); - addAllDependenciesFrom(memberDepends); + addAllDependenciesFrom(dependsWithContexts); addAllDependenciesFrom(dynamicLookupDepends); addAllDependenciesFrom(externalDependencies); } @@ -798,7 +777,7 @@ void SourceFileDepGraphConstructor::recordThatThisWholeFileDependsOn( // Entry point from the Frontend to this whole system //============================================================================== -bool swift::fine_grained_dependencies::emitReferenceDependencies( +bool fine_grained_dependencies::emitReferenceDependencies( DiagnosticEngine &diags, SourceFile *const SF, const DependencyTracker &depTracker, StringRef outputPath, const bool alsoEmitDotFile) { @@ -814,8 +793,9 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( // we force the inclusion of private declarations when fingerprints // are enabled. const bool includeIntrafileDeps = - SF->getASTContext().LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || - SF->getASTContext().LangOpts.EnableTypeFingerprints; + SF->getASTContext() + .LangOpts.FineGrainedDependenciesIncludeIntrafileOnes || + SF->getASTContext().LangOpts.EnableTypeFingerprints; const bool hadCompilationError = SF->getASTContext().hadError(); auto gc = SourceFileDepGraphConstructor::forSourceFile( SF, depTracker, outputPath, includeIntrafileDeps, hadCompilationError); @@ -832,13 +812,9 @@ bool swift::fine_grained_dependencies::emitReferenceDependencies( // If path is stdout, cannot read it back, so check for "-" assert(outputPath == "-" || g.verifyReadsWhatIsWritten(outputPath)); - if (alsoEmitDotFile) { - std::string dotFileName = outputPath.str() + ".dot"; - withOutputFile(diags, dotFileName, [&](llvm::raw_pwrite_stream &out) { - DotFileEmitter(out, g, false, false).emit(); - return false; - }); - } + if (alsoEmitDotFile) + g.emitDotFile(outputPath, diags); + return hadError; } @@ -952,7 +928,10 @@ SourceFileDepGraph SourceFileDepGraph::simulateLoad( // clang-format off SourceFileDepGraphConstructor c( - swiftDepsFilename, includePrivateDeps, hadCompilationError, interfaceHash, + swiftDepsFilename, + includePrivateDeps, + hadCompilationError, + interfaceHash, getSimpleDepends(simpleNamesByRDK[dependsTopLevel]), getCompoundDepends(simpleNamesByRDK[dependsNominal], compoundNamesByRDK[dependsMember]), @@ -961,7 +940,7 @@ SourceFileDepGraph SourceFileDepGraph::simulateLoad( {}, // precedence groups {}, // memberOperatorDecls {}, // operators - getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // topNominals + {}, // topNominals getBaseNameProvides(simpleNamesByRDK[providesTopLevel]), // topValues getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // allNominals getMangledHolderProvides(simpleNamesByRDK[providesNominal]), // potentialMemberHolders diff --git a/lib/Driver/FineGrainedDependencyDriverGraph.cpp b/lib/Driver/FineGrainedDependencyDriverGraph.cpp index 5bda1956f9b4c..95ce4e6c33fa6 100644 --- a/lib/Driver/FineGrainedDependencyDriverGraph.cpp +++ b/lib/Driver/FineGrainedDependencyDriverGraph.cpp @@ -12,9 +12,11 @@ #include "swift/Driver/FineGrainedDependencyDriverGraph.h" // Next two includes needed for reporting errors opening dot file for writing. +#include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/FileSystem.h" #include "swift/Basic/ReferenceDependencyKeys.h" +#include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Demangling/Demangle.h" #include "swift/Driver/Job.h" @@ -57,7 +59,14 @@ ModuleDepGraph::simulateLoad(const Job *cmd, swiftDeps, includePrivateDeps, hadCompilationError, interfaceHash, simpleNames, compoundNames); - return loadFromSourceFileDepGraph(cmd, sfdg); + SourceManager sm; + DiagnosticEngine diags(sm); + // help for debugging: emit imported file, too + if (emitFineGrainedDependencyDotFileAfterEveryImport) { + sfdg.emitDotFile(swiftDeps, diags); + } + + return loadFromSourceFileDepGraph(cmd, sfdg, diags); } std::string SourceFileDepGraph::noncascading(std::string name) { @@ -104,31 +113,35 @@ ModuleDepGraph::Changes ModuleDepGraph::loadFromPath(const Job *Cmd, auto buffer = llvm::MemoryBuffer::getFile(path); if (!buffer) return None; - auto r = loadFromBuffer(Cmd, *buffer.get()); + auto r = loadFromBuffer(Cmd, *buffer.get(), diags); assert(path == getSwiftDeps(Cmd) && "Should be reading the job's swiftdeps"); assert(!r || !nodeMap[path].empty() && "Must have a node for the whole file"); - if (emitFineGrainedDependencyDotFileAfterEveryImport) - emitDotFileForJob(diags, Cmd); - if (verifyFineGrainedDependencyGraphAfterEveryImport) - verify(); return r; } /// Returns None for error or a set of changed keys ModuleDepGraph::Changes -ModuleDepGraph::loadFromBuffer(const Job *job, llvm::MemoryBuffer &buffer) { +ModuleDepGraph::loadFromBuffer(const Job *job, llvm::MemoryBuffer &buffer, + DiagnosticEngine &diags) { Optional sourceFileDepGraph = SourceFileDepGraph::loadFromBuffer(buffer); if (!sourceFileDepGraph) return None; - return loadFromSourceFileDepGraph(job, sourceFileDepGraph.getValue()); + return loadFromSourceFileDepGraph(job, sourceFileDepGraph.getValue(), diags); } ModuleDepGraph::Changes ModuleDepGraph::loadFromSourceFileDepGraph( - const Job *job, const SourceFileDepGraph &sourceFileDepGraph) { + const Job *job, const SourceFileDepGraph &sourceFileDepGraph, + DiagnosticEngine &diags) { registerJob(job); - return integrate(sourceFileDepGraph, getSwiftDeps(job)); + auto changes = integrate(sourceFileDepGraph, getSwiftDeps(job)); + + if (verifyFineGrainedDependencyGraphAfterEveryImport) + verify(); + if (emitFineGrainedDependencyDotFileAfterEveryImport) + emitDotFileForJob(diags, job); + return changes; } bool ModuleDepGraph::haveAnyNodesBeenTraversedIn(const Job *cmd) const { @@ -254,7 +267,9 @@ ModuleDepGraph::Changes ModuleDepGraph::integrate(const SourceFileDepGraph &g, auto disappearedNodes = nodeMap[swiftDepsOfJob]; // When done, changeDependencyKeys contains a list of keys that changed // as a result of this integration. - auto changedNodes = std::unordered_set(); + // Or if the integration failed, None. + Optional> changedNodes = + std::unordered_set(); g.forEachNode([&](const SourceFileDepGraphNode *integrand) { const auto &key = integrand->getKey(); @@ -264,24 +279,30 @@ ModuleDepGraph::Changes ModuleDepGraph::integrate(const SourceFileDepGraph &g, preexistingMatch.getValue().first == LocationOfPreexistingNode::here) disappearedNodes.erase(key); // Node was and still is. Do not erase it. - NullablePtr newNodeOrChangedNode = + Optional> newNodeOrChangedNode = integrateSourceFileDepGraphNode(g, integrand, preexistingMatch, swiftDepsOfJob); - if (auto *n = newNodeOrChangedNode.getPtrOrNull()) - changedNodes.insert(n); + if (!newNodeOrChangedNode) + changedNodes = None; + else if (!changedNodes) + ; + else if (auto *n = newNodeOrChangedNode.getValue().getPtrOrNull()) + changedNodes.getValue().insert(n); }); + if (!changedNodes) + return None; for (auto &p : disappearedNodes) { - changedNodes.insert(p.second); + changedNodes.getValue().insert(p.second); eraseNodeFromJob(p.second); } // Make sure the changes can be retraced: - for (auto *n : changedNodes) + for (auto *n : changedNodes.getValue()) n->clearHasBeenTraced(); - return changedNodes; + return changedNodes.getValue(); } ModuleDepGraph::PreexistingNodeIfAny ModuleDepGraph::findPreexistingMatch( @@ -310,13 +331,20 @@ ModuleDepGraph::PreexistingNodeIfAny ModuleDepGraph::findPreexistingMatch( return None; } -NullablePtr ModuleDepGraph::integrateSourceFileDepGraphNode( +Optional> +ModuleDepGraph::integrateSourceFileDepGraphNode( const SourceFileDepGraph &g, const SourceFileDepGraphNode *integrand, const PreexistingNodeIfAny preexistingMatch, const StringRef swiftDepsOfJob) { + if (!EnableTypeFingerprints && + integrand->getKey().getKind() != NodeKind::sourceFileProvide && + integrand->getFingerprint()) + return None; + if (!integrand->getIsProvides()) - return nullptr; // depends are captured by recordWhatUseDependsUpon below + return NullablePtr(); // depends are captured by + // recordWhatUseDependsUpon below auto changedAndIntegrationResultNode = integrateSourceFileDeclNode(integrand, swiftDepsOfJob, preexistingMatch); @@ -550,6 +578,14 @@ void ModuleDepGraph::emitDotFile(llvm::raw_ostream &out) { // MARK: ModuleDepGraph debugging //============================================================================== +void ModuleDepGraphNode::dump(llvm::raw_ostream &out) const { + DepGraphNode::dump(out); + if (getIsProvides()) + out << " swiftDeps: <" << getSwiftDepsOfProvides() << ">\n"; + else + out << " no swiftDeps\n"; +} + void ModuleDepGraphNode::dump() const { DepGraphNode::dump(); if (getIsProvides()) From ced1c2b3d1da041ad7ea03422b5e11325886b43a Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 9 Feb 2020 11:09:48 -0800 Subject: [PATCH 230/237] test: repair the SourceKit test for Windows (NFC) Ignore the line ending as in the rest of the SourceKit tests. --- test/SourceKit/CodeComplete/complete_typerelation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/SourceKit/CodeComplete/complete_typerelation.swift b/test/SourceKit/CodeComplete/complete_typerelation.swift index 603d57df21eb5..ccfca2572684d 100644 --- a/test/SourceKit/CodeComplete/complete_typerelation.swift +++ b/test/SourceKit/CodeComplete/complete_typerelation.swift @@ -31,10 +31,10 @@ func testUnknown() { } // RUN: %sourcekitd-test -req=complete -pos=13:17 %s -- %s > %t.identical.response -// RUN: diff -u %s.identical.response %t.identical.response +// RUN: diff --strip-trailing-cr -u %s.identical.response %t.identical.response // RUN: %sourcekitd-test -req=complete -pos=17:17 %s -- %s > %t.convertible.response -// RUN: diff -u %s.convertible.response %t.convertible.response +// RUN: diff --strip-trailing-cr -u %s.convertible.response %t.convertible.response // RUN: %empty-directory(%t/cache) // RUN: %sourcekitd-test -req=complete.cache.ondisk -cache-path %t/cache == -req=complete -pos=21:10 %s -- %s | %FileCheck %s --check-prefix=BOOLCONTEXT From 4b64be48111246e3934a3ec440a1c1837fadb0bc Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 9 Feb 2020 15:05:51 -0800 Subject: [PATCH 231/237] Add presets for source compat suite. This should have been done some time ago, but it fell through the cracks. Ran into some problems here, so I am taking care of it real quickly. --- utils/build-presets.ini | 89 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 367c43944f7fd..83d6b82f355cb 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -2221,3 +2221,92 @@ mixin-preset=stdlib_DA_standalone,build test validation-test + +#===----------------------------------------------------------------------===# +# Preset for Source Compatibility Suite +#===----------------------------------------------------------------------===# + +[preset: source_compat_suite_base] +build-ninja +llbuild +swiftpm +install-llbuild +install-swift +install-swiftpm +reconfigure +verbose-build +skip-build-benchmarks + +install-destdir=%(install_destdir)s +install-prefix=%(install_prefix)s +installable-package=%(installable_package)s + +[preset: source_compat_suite_macos_base] +mixin-preset=source_compat_suite_base +build-subdir=compat_macos +ios +tvos +watchos +compiler-vendor=apple +darwin-install-extract-symbols +darwin-toolchain-alias=swift +darwin-toolchain-bundle-identifier=org.swift.compat-macos +darwin-toolchain-display-name-short=Swift Development Snapshot +darwin-toolchain-display-name=Swift Development Snapshot +darwin-toolchain-name=swift-DEVELOPMENT-SNAPSHOT +darwin-toolchain-version=3.999.999 +llvm-install-components=libclang;libclang-headers;dsymutil +swift-install-components=compiler;clang-builtin-headers;stdlib;sdk-overlay;license;sourcekit-xpc-service;swift-remote-mirror;swift-remote-mirror-headers +symbols-package=%(symbols_package)s +install-symroot=%(install_symroot)s + +[preset: source_compat_suite_linux_base] +mixin-preset=source_compat_suite_base +build-subdir=compat_linux +foundation +libdispatch +xctest +install-foundation +install-libdispatch +install-xctest +swift-install-components=autolink-driver;compiler;clang-builtin-headers;stdlib;swift-remote-mirror;sdk-overlay;license + +[preset: source_compat_suite_macos_DA] +mixin-preset=source_compat_suite_macos_base +debug +assertions + +[preset: source_compat_suite_macos_RA] +mixin-preset=source_compat_suite_macos_base +release +assertions + +[preset: source_compat_suite_macos_R] +mixin-preset=source_compat_suite_macos_base +release +no-assertions + +[preset: source_compat_suite_macos_D] +mixin-preset=source_compat_suite_macos_base +debug +no-assertions + +[preset: source_compat_suite_linux_DA] +mixin-preset=source_compat_suite_linux_base +debug +assertions + +[preset: source_compat_suite_linux_RA] +mixin-preset=source_compat_suite_linux_base +release +assertions + +[preset: source_compat_suite_linux_R] +mixin-preset=source_compat_suite_linux_base +release +no-assertions + +[preset: source_compat_suite_linux_D] +mixin-preset=source_compat_suite_linux_base +debug +no-assertions From 704b0cc1690c0a4d2fd8605cb52f57ac3fe97a5a Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Sun, 9 Feb 2020 16:00:39 -0800 Subject: [PATCH 232/237] Presets add missing preset default for build_swift test. --- utils/build_swift/tests/build_swift/test_presets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/build_swift/tests/build_swift/test_presets.py b/utils/build_swift/tests/build_swift/test_presets.py index 29b5779ffd5c3..2b1257237e30a 100644 --- a/utils/build_swift/tests/build_swift/test_presets.py +++ b/utils/build_swift/tests/build_swift/test_presets.py @@ -39,6 +39,7 @@ 'install_destdir': '/tmp/install', 'install_symroot': '/tmp/install/symroot', 'install_toolchain_dir': '/tmp/install/toolchain', + 'install_prefix': '/usr', 'installable_package': '/tmp/install/pkg', 'swift_install_destdir': '/tmp/install/swift', 'symbols_package': '/path/to/symbols/package', From a60ada1ce21082083443f715deb95839dc59aa93 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sun, 9 Feb 2020 21:22:32 -0800 Subject: [PATCH 233/237] Gate a test that dumps stats on an asserts build --- test/Serialization/interface-module-nested-types.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Serialization/interface-module-nested-types.swift b/test/Serialization/interface-module-nested-types.swift index 1342498dc0702..9233bc0349422 100644 --- a/test/Serialization/interface-module-nested-types.swift +++ b/test/Serialization/interface-module-nested-types.swift @@ -1,3 +1,5 @@ +// REQUIRES: asserts + // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/ModuleCache) // RUN: %empty-directory(%t/Build) From f53b1a7ba1bac02eb4c7356c28e8617c6e883b05 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sun, 9 Feb 2020 21:38:45 -0800 Subject: [PATCH 234/237] =?UTF-8?q?Revert=20"Emit=20debug=20info=20for=20g?= =?UTF-8?q?eneric=20type=20aliases.=20=20=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/IRGen/DebugTypeInfo.cpp | 29 ++++++++----------- lib/IRGen/IRGenSIL.cpp | 6 ++-- .../bound-generic-struct-extension.swift | 28 ------------------ test/DebugInfo/generic-typealias.swift | 19 ------------ 4 files changed, 15 insertions(+), 67 deletions(-) delete mode 100644 test/DebugInfo/bound-generic-struct-extension.swift delete mode 100644 test/DebugInfo/generic-typealias.swift diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index 51e1868b76d66..34fa19bd537f0 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -60,25 +60,20 @@ DebugTypeInfo DebugTypeInfo::getFromTypeInfo(swift::Type Ty, DebugTypeInfo DebugTypeInfo::getLocalVariable(VarDecl *Decl, swift::Type Ty, const TypeInfo &Info) { + + auto DeclType = Decl->getInterfaceType(); + auto RealType = Ty; + + // DynamicSelfType is also sugar as far as debug info is concerned. + auto Sugared = DeclType; + if (auto DynSelfTy = DeclType->getAs()) + Sugared = DynSelfTy->getSelfType(); + // Prefer the original, potentially sugared version of the type if // the type hasn't been mucked with by an optimization pass. - swift::Type DeclType = Decl->getInterfaceType(); - swift::Type RealType = Ty; - - swift::Type DebugType; - if (auto DynSelfTy = DeclType->getAs()) { - // DynamicSelfType is also sugar as far as debug info is concerned. - auto DesugaredSelf = DynSelfTy->getSelfType(); - DebugType = DesugaredSelf->isEqual(RealType) ? DynSelfTy : RealType; - } else { - // Map the sugared type into the context to resolve bound generics and - // generic type aliases. - DeclContext *DeclCtx = Decl->getDeclContext(); - swift::Type Sugared = - DeclCtx ? DeclCtx->mapTypeIntoContext(DeclType) : DeclType; - DebugType = Sugared->isEqual(RealType) ? Sugared : RealType; - } - return getFromTypeInfo(DebugType, Info); + auto *Type = Sugared->isEqual(RealType) ? DeclType.getPointer() + : RealType.getPointer(); + return getFromTypeInfo(Type, Info); } DebugTypeInfo DebugTypeInfo::getMetadata(swift::Type Ty, llvm::Type *StorageTy, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 81d885aaeac80..7292f9e0160ca 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3660,7 +3660,7 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { llvm::SmallVector Copy; emitShadowCopyIfNeeded(SILVal, i->getDebugScope(), *VarInfo, IsAnonymous, Copy); - bindArchetypes(RealTy); + bindArchetypes(DbgTy.getType()); if (!IGM.DebugInfo) return; @@ -3691,7 +3691,7 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) { auto DbgTy = DebugTypeInfo::getLocalVariable( Decl, RealType, getTypeInfo(SILVal->getType())); - bindArchetypes(RealType); + bindArchetypes(DbgTy.getType()); if (!IGM.DebugInfo) return; @@ -3991,7 +3991,7 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, auto RealType = SILTy.getASTType(); auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type); - bindArchetypes(RealType); + bindArchetypes(DbgTy.getType()); if (IGM.DebugInfo) emitDebugVariableDeclaration(addr, DbgTy, SILTy, DS, Decl, *VarInfo, Indirection); diff --git a/test/DebugInfo/bound-generic-struct-extension.swift b/test/DebugInfo/bound-generic-struct-extension.swift deleted file mode 100644 index ec6e13aa235f2..0000000000000 --- a/test/DebugInfo/bound-generic-struct-extension.swift +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s - -// Test that a bound generic type is fully resolved in the debug info. - -public protocol P {} - -public struct S : P { - var x: Int -} -// This is significant, it must be bound to S: main.BoundGeneric -// CHECK-DAG: ![[S:[0-9]+]] = !DICompositeType({{.*}}identifier: "$s4main12BoundGenericVyAA1SVGD") - -public extension BoundGeneric where T == S { - func f() { -// CHECK-DAG: !DILocalVariable(name: "self", arg: 1,{{.*}} line: [[@LINE-1]],{{.*}} type: ![[C_BGS:[0-9]+]], -// CHECK-DAG: ![[C_BGS]] = !DIDerivedType(tag: DW_TAG_const_type,{{.*}} baseType: ![[BGS:[0-9]+]]) -// CHECK-DAG: ![[BGS]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}} elements: ![[ELTS:[0-9]+]], -// CHECK-DAG: ![[ELTS]] = !{![[MEMBER:[0-9]+]]} -// CHECK-DAG: ![[MEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[S]], - } -} - -public struct BoundGeneric where T : P { - let x : T -} - -public let pat = BoundGeneric(x: S(x: 23)) -pat.f() diff --git a/test/DebugInfo/generic-typealias.swift b/test/DebugInfo/generic-typealias.swift deleted file mode 100644 index 3b4c4399e4008..0000000000000 --- a/test/DebugInfo/generic-typealias.swift +++ /dev/null @@ -1,19 +0,0 @@ -// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s - -// Test that a generic type alias is represented in the debug info. - -public struct S { - public typealias Alias = (T, T) - // CHECK: ![[T_T:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sx_xtD" - public let member : Alias - public func f(t : Alias) -> Alias { return t } - // CHECK: !DILocalVariable(name: "t", arg: 1,{{.*}} line: [[@LINE-1]], - // CHECK-SAME: type: ![[C_ALIAS:[0-9]+]]) - // CHECK: ![[C_ALIAS]] = !DIDerivedType(tag: DW_TAG_const_type, - // CHECK-SAME: baseType: ![[ALIAS:[0-9]+]]) - // CHECK: ![[ALIAS]] = !DIDerivedType(tag: DW_TAG_typedef, name: "$ - // CHECK-SAME: baseType: ![[T_T]]) - -} - -public let s = S(member: (4, 2)) From 6d40e7b350c7f5ba839ae6ea1de5876e84c39e45 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 10 Feb 2020 11:24:13 -0800 Subject: [PATCH 235/237] XFAIL a test holding up the rebranch process --- .../swift_build_sdk_interfaces/compiler-crash.test-sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ModuleInterface/swift_build_sdk_interfaces/compiler-crash.test-sh b/test/ModuleInterface/swift_build_sdk_interfaces/compiler-crash.test-sh index 8b7f48b97f6ec..4cbb318c65f05 100644 --- a/test/ModuleInterface/swift_build_sdk_interfaces/compiler-crash.test-sh +++ b/test/ModuleInterface/swift_build_sdk_interfaces/compiler-crash.test-sh @@ -1,3 +1,5 @@ +REQUIRES: rdar59318361 + RUN: not %swift_build_sdk_interfaces -sdk %S/Inputs/mock-sdk/ -o %t/output -debug-crash-compiler 2>&1 | %FileCheck %s CHECK: Program arguments: From df31c7265b80ec1abfd34171f2c007988f5300cf Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Mon, 10 Feb 2020 14:59:01 -0800 Subject: [PATCH 236/237] Disable running indexstoreDB tests part of Swift pull request preset. (59327164) --- utils/build-presets.ini | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 83d6b82f355cb..bcce86d9e5c91 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -584,6 +584,9 @@ swift-primary-variant-arch=x86_64 # Don't build the benchmarks skip-build-benchmarks +# FIX: rdar://problem/59327164 +skip-test-indexstore-db + [preset: buildbot_incremental,tools=RA,stdlib=RD,smoketest=macosx,flto] mixin-preset=buildbot_incremental,tools=RA,stdlib=RD,smoketest=macosx build-subdir=buildbot_incremental @@ -1472,6 +1475,8 @@ sil-verify-all skip-test-ios-host skip-test-watchos-host +# FIX: rdar://problem/59327164 +skip-test-indexstore-db #===------------------------------------------------------------------------===# # Test watchOS on OS X builder From 7952f051a3401494655c09aa785a058a7c997c31 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Mon, 10 Feb 2020 15:44:31 -0800 Subject: [PATCH 237/237] Disable linux-fatal-backtrace.swift (59328972) --- test/Runtime/linux-fatal-backtrace.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Runtime/linux-fatal-backtrace.swift b/test/Runtime/linux-fatal-backtrace.swift index 7e84dc9719a62..92467a7b93c2f 100644 --- a/test/Runtime/linux-fatal-backtrace.swift +++ b/test/Runtime/linux-fatal-backtrace.swift @@ -4,6 +4,7 @@ // REQUIRES: executable_test // REQUIRES: OS=linux-gnu // REQUIRES: lldb +// REQUIRES: rdar59328972 // XFAIL: CPU=s390x // NOTE: not.py is used above instead of "not --crash" because %target-run