diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 9281ebcf55c2b..7697baf4d1d01 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -62,6 +62,7 @@ namespace swift { class BoundGenericType; class ClangModuleLoader; class ClangNode; + class ClangTypeConverter; class ConcreteDeclRef; class ConstructorDecl; class Decl; @@ -586,6 +587,10 @@ class ASTContext final { Type getBridgedToObjC(const DeclContext *dc, Type type, Type *bridgedValueType = nullptr) const; +private: + ClangTypeConverter &getClangTypeConverter(); + +public: /// Get the Clang type corresponding to a Swift function type. /// /// \param params The function parameters. @@ -595,6 +600,14 @@ class ASTContext final { getClangFunctionType(ArrayRef params, Type resultTy, FunctionTypeRepresentation trueRep); + /// Get the canonical Clang type corresponding to a SIL function type. + /// + /// SIL analog of \c ASTContext::getClangFunctionType . + const clang::Type * + getCanonicalClangFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation trueRep); + /// Get the Swift declaration that a Clang declaration was exported from, /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c21cac7948679..5842456999735 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -63,6 +63,7 @@ namespace swift { class Type; class Expr; class DeclRefExpr; + class ForeignAsyncConvention; class ForeignErrorConvention; class LiteralExpr; class BraceStmt; @@ -6148,7 +6149,15 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl { /// being dropped altogether. `None` is returned for a normal function /// or method. Optional getForeignFunctionAsMethodSelfParameterIndex() const; - + + /// Set information about the foreign async convention used by this + /// declaration. + void setForeignAsyncConvention(const ForeignAsyncConvention &convention); + + /// Get information about the foreign async convention used by this + /// declaration, given that it is @objc and 'async'. + Optional getForeignAsyncConvention() const; + static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_AbstractFunctionDecl && D->getKind() <= DeclKind::Last_AbstractFunctionDecl; @@ -6277,7 +6286,8 @@ class FuncDecl : public AbstractFunctionDecl { DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, bool Throws, + DeclName Name, SourceLoc NameLoc, + bool Async, bool Throws, ParameterList *BodyParams, Type FnRetType, DeclContext *Parent, ClangNode ClangN); diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index f4c39ce036d06..72d8d64bbecb7 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -188,5 +188,19 @@ ERROR(scanner_find_cycle, none, ERROR(scanner_arguments_invalid, none, "dependencies scanner cannot be configured with arguments: '%0'", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: custom attribute diagnostics +//------------------------------------------------------------------------------ + +ERROR(ambiguous_custom_attribute_ref,none, + "ambiguous use of attribute %0", (Identifier)) +NOTE(ambiguous_custom_attribute_ref_fix,none, + "use '%0.' to reference the attribute %1 in module %2", + (StringRef, Identifier, Identifier)) +NOTE(found_attribute_candidate,none, + "found this attribute", ()) +ERROR(unknown_attribute,none, + "unknown attribute '%0'", (StringRef)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6c7f4e6533b04..3a278eb7dea14 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1324,8 +1324,6 @@ ERROR(replace_equal_with_colon_for_value,none, "'=' has been replaced with ':' in attribute arguments", ()) ERROR(expected_attribute_name,none, "expected an attribute name", ()) -ERROR(unknown_attribute,none, - "unknown attribute '%0'", (StringRef)) ERROR(unexpected_lparen_in_attribute,none, "unexpected '(' in attribute '%0'", (StringRef)) ERROR(duplicate_attribute,none, diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9374d59db3e3f..9d36c35bc7c80 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2823,15 +2823,16 @@ WARNING(differentiable_nondiff_type_implicit_noderivative_fixit,none, /*nominalCanDeriveAdditiveArithmetic*/ bool)) WARNING(differentiable_immutable_wrapper_implicit_noderivative_fixit,none, "synthesis of the 'Differentiable.move(along:)' requirement for %1 " - "requires 'wrappedValue' in property wrapper %0 to be mutable; " - "add an explicit '@noDerivative' attribute" + "requires 'wrappedValue' in property wrapper %0 to be mutable or have a " + "non-mutating 'move(along:)'; add an explicit '@noDerivative' attribute" "%select{|, or conform %1 to 'AdditiveArithmetic'}2", (/*wrapperType*/ Identifier, /*nominalName*/ Identifier, /*nominalCanDeriveAdditiveArithmetic*/ bool)) WARNING(differentiable_let_property_implicit_noderivative_fixit,none, "synthesis of the 'Differentiable.move(along:)' requirement for %0 " "requires all stored properties not marked with `@noDerivative` to be " - "mutable; use 'var' instead, or add an explicit '@noDerivative' attribute" + "mutable or have a non-mutating 'move(along:)'; use 'var' instead, or " + "add an explicit '@noDerivative' attribute " "%select{|, or conform %0 to 'AdditiveArithmetic'}1", (/*nominalName*/ Identifier, /*nominalCanDeriveAdditiveArithmetic*/ bool)) diff --git a/include/swift/AST/ExtInfo.h b/include/swift/AST/ExtInfo.h index 656564dae31a9..6f258723fb887 100644 --- a/include/swift/AST/ExtInfo.h +++ b/include/swift/AST/ExtInfo.h @@ -195,16 +195,8 @@ class ASTExtInfoBuilder { using Representation = FunctionTypeRepresentation; - static void assertIsFunctionType(const clang::Type *); - ASTExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) - : bits(bits), clangTypeInfo(clangTypeInfo) { - // TODO: [clang-function-type-serialization] Once we start serializing - // the Clang type, we should also assert that the pointer is non-null. - auto Rep = Representation(bits & RepresentationMask); - if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) - assertIsFunctionType(clangTypeInfo.type); - } + : bits(bits), clangTypeInfo(clangTypeInfo) {} public: // Constructor with all defaults. @@ -255,10 +247,7 @@ class ASTExtInfoBuilder { DifferentiabilityKind::NonDifferentiable; } - /// Get the underlying ClangTypeInfo value if it is not the default value. - Optional getClangTypeInfo() const { - return clangTypeInfo.empty() ? Optional() : clangTypeInfo; - } + ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; } constexpr SILFunctionTypeRepresentation getSILRepresentation() const { unsigned rawRep = bits & RepresentationMask; @@ -405,9 +394,7 @@ class ASTExtInfo { constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } - Optional getClangTypeInfo() const { - return builder.getClangTypeInfo(); - } + ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); } constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } @@ -519,20 +506,32 @@ class SILExtInfoBuilder { SILExtInfoBuilder(unsigned bits, ClangTypeInfo clangTypeInfo) : bits(bits), clangTypeInfo(clangTypeInfo) {} + static constexpr unsigned makeBits(Representation rep, bool isPseudogeneric, + bool isNoEscape, bool isAsync, + DifferentiabilityKind diffKind) { + return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | + (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | + (((unsigned)diffKind << DifferentiabilityMaskOffset) & + DifferentiabilityMask); + } + public: // Constructor with all defaults. - SILExtInfoBuilder() : bits(0), clangTypeInfo(ClangTypeInfo(nullptr)) {} + SILExtInfoBuilder() : SILExtInfoBuilder(0, ClangTypeInfo(nullptr)) {} // Constructor for polymorphic type. SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape, bool isAsync, DifferentiabilityKind diffKind, const clang::Type *type) - : SILExtInfoBuilder( - ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) | - (isNoEscape ? NoEscapeMask : 0) | (isAsync ? AsyncMask : 0) | - (((unsigned)diffKind << DifferentiabilityMaskOffset) & - DifferentiabilityMask), - ClangTypeInfo(type)) {} + : SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape, isAsync, + diffKind), + ClangTypeInfo(type)) {} + + SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric) + : SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric, + info.isNoEscape(), info.isAsync(), + info.getDifferentiabilityKind()), + info.getClangTypeInfo()) {} void checkInvariants() const; @@ -567,10 +566,8 @@ class SILExtInfoBuilder { DifferentiabilityKind::NonDifferentiable; } - /// Get the underlying ClangTypeInfo value if it is not the default value. - Optional getClangTypeInfo() const { - return clangTypeInfo.empty() ? Optional() : clangTypeInfo; - } + /// Get the underlying ClangTypeInfo value. + ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; } constexpr bool hasSelfParam() const { switch (getRepresentation()) { @@ -698,9 +695,7 @@ class SILExtInfo { constexpr bool isDifferentiable() const { return builder.isDifferentiable(); } - Optional getClangTypeInfo() const { - return builder.getClangTypeInfo(); - } + ClangTypeInfo getClangTypeInfo() const { return builder.getClangTypeInfo(); } constexpr bool hasSelfParam() const { return builder.hasSelfParam(); } diff --git a/include/swift/AST/ForeignAsyncConvention.h b/include/swift/AST/ForeignAsyncConvention.h new file mode 100644 index 0000000000000..f3657766e02af --- /dev/null +++ b/include/swift/AST/ForeignAsyncConvention.h @@ -0,0 +1,81 @@ +//===--- ForeignAsyncConvention.h - Async conventions -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the ForeignAsyncConvention structure, which +// describes the rules for how to detect that a foreign API is asynchronous. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_FOREIGN_ASYNC_CONVENTION_H +#define SWIFT_FOREIGN_ASYNC_CONVENTION_H + +#include "swift/AST/Type.h" + +namespace swift { + +/// A small structure describing the async convention of a foreign declaration. +class ForeignAsyncConvention { + /// The index of the completion handler parameters. + unsigned CompletionHandlerParamIndex; + + /// When non-zero, indicates which parameter to the completion handler is the + /// Error? parameter (minus one) that makes this async function also throwing. + unsigned CompletionHandlerErrorParamIndex; +public: + ForeignAsyncConvention() + : CompletionHandlerParamIndex(0), CompletionHandlerErrorParamIndex(0) { } + + ForeignAsyncConvention(unsigned completionHandlerParamIndex, + Optional completionHandlerErrorParamIndex) + : CompletionHandlerParamIndex(completionHandlerParamIndex), + CompletionHandlerErrorParamIndex( + completionHandlerErrorParamIndex + ? *completionHandlerErrorParamIndex + 1 + : 0) {} + + /// Retrieve the index of the completion handler parameter, which will be + /// erased from the Swift signature of the imported async function. + unsigned completionHandlerParamIndex() const { + return CompletionHandlerParamIndex; + } + + /// Retrieve the index of the \c Error? parameter in the completion handler's + /// parameter list. When argument passed to this parameter is non-null, the + /// provided error will be thrown by the async function. + Optional completionHandlerErrorParamIndex() const { + if (CompletionHandlerErrorParamIndex == 0) + return None; + + return CompletionHandlerErrorParamIndex - 1; + } + + /// Whether the async function is throwing due to the completion handler + /// having an \c Error? parameter. + /// + /// Equivalent to \c static_cast(completionHandlerErrorParamIndex()). + bool isThrowing() const { + return CompletionHandlerErrorParamIndex != 0; + } + + bool operator==(ForeignAsyncConvention other) const { + return CompletionHandlerParamIndex == other.CompletionHandlerParamIndex + && CompletionHandlerErrorParamIndex == + other.CompletionHandlerErrorParamIndex; + } + bool operator!=(ForeignAsyncConvention other) const { + return !(*this == other); + } +}; + +} + +#endif diff --git a/include/swift/AST/ForeignErrorConvention.h b/include/swift/AST/ForeignErrorConvention.h index 48d65d4612856..d09a7a80c0d86 100644 --- a/include/swift/AST/ForeignErrorConvention.h +++ b/include/swift/AST/ForeignErrorConvention.h @@ -81,6 +81,10 @@ class ForeignErrorConvention { } Info() = default; + + Kind getKind() const { + return static_cast(TheKind); + } }; private: @@ -178,11 +182,24 @@ class ForeignErrorConvention { /// Returns the physical result type of the function, for functions /// that completely erase this information. CanType getResultType() const { - assert(getKind() == ZeroResult || - getKind() == NonZeroResult); + assert(resultTypeErasedToVoid(getKind())); return ResultType; } - + + /// Whether this kind of error import erases the result type to 'Void'. + static bool resultTypeErasedToVoid(Kind kind) { + switch (kind) { + case ZeroResult: + case NonZeroResult: + return true; + + case ZeroPreservedResult: + case NilResult: + case NonNilError: + return false; + } + } + bool operator==(ForeignErrorConvention other) const { return info.TheKind == other.info.TheKind && info.ErrorIsOwned == other.info.ErrorIsOwned diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 434d4df8d266a..328f4c0aff762 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2877,7 +2877,7 @@ class AnyFunctionType : public TypeBase { unsigned NumParams, ExtInfo Info) : TypeBase(Kind, CanTypeContext, properties), Output(Output) { Bits.AnyFunctionType.ExtInfoBits = Info.getBits(); - Bits.AnyFunctionType.HasClangTypeInfo = Info.getClangTypeInfo().hasValue(); + Bits.AnyFunctionType.HasClangTypeInfo = !Info.getClangTypeInfo().empty(); Bits.AnyFunctionType.NumParams = NumParams; assert(Bits.AnyFunctionType.NumParams == NumParams && "Params dropped!"); // The use of both assert() and static_assert() is intentional. diff --git a/include/swift/Demangling/TypeDecoder.h b/include/swift/Demangling/TypeDecoder.h index 1726133027988..074e4aa26027d 100644 --- a/include/swift/Demangling/TypeDecoder.h +++ b/include/swift/Demangling/TypeDecoder.h @@ -18,10 +18,12 @@ #ifndef SWIFT_DEMANGLING_TYPEDECODER_H #define SWIFT_DEMANGLING_TYPEDECODER_H +#include "TypeLookupError.h" #include "swift/ABI/MetadataValues.h" +#include "swift/Basic/LLVM.h" #include "swift/Demangling/Demangler.h" #include "swift/Demangling/NamespaceMacros.h" -#include "swift/Basic/LLVM.h" +#include "swift/Runtime/Portability.h" #include "swift/Runtime/Unreachable.h" #include "swift/Strings.h" #include "llvm/ADT/ArrayRef.h" @@ -328,6 +330,14 @@ getObjCClassOrProtocolName(NodePointer node) { } #endif +#define MAKE_NODE_TYPE_ERROR(Node, Fmt, ...) \ + TypeLookupError("TypeDecoder.h:%d: Node kind %u \"%.*s\" - " Fmt, __LINE__, \ + Node->getKind(), \ + Node->hasText() ? (int)Node->getText().size() : 0, \ + Node->hasText() ? Node->getText().data() : "", __VA_ARGS__) + +#define MAKE_NODE_TYPE_ERROR0(Node, Str) MAKE_NODE_TYPE_ERROR(Node, "%s", Str) + /// Decode a mangled type to construct an abstract type, forming such /// types by invoking a custom builder. template @@ -344,24 +354,25 @@ class TypeDecoder { : Builder(Builder) {} /// Given a demangle tree, attempt to turn it into a type. - BuiltType decodeMangledType(NodePointer Node) { - if (!Node) return BuiltType(); + TypeLookupErrorOr decodeMangledType(NodePointer Node) { + if (!Node) + return TypeLookupError("Node is NULL"); using NodeKind = Demangle::Node::Kind; switch (Node->getKind()) { case NodeKind::Global: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::TypeMangling: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::Type: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children."); return decodeMangledType(Node->getChild(0)); case NodeKind::Class: @@ -380,8 +391,8 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (!decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) - return BuiltType(); + if (auto error = decodeMangledTypeDecl(Node, typeDecl, parent, typeAlias)) + return *error; if (typeAlias) return Builder.createTypeAliasType(typeDecl, parent); @@ -395,19 +406,21 @@ class TypeDecoder { case NodeKind::BoundGenericTypeAlias: case NodeKind::BoundGenericOtherNominalType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); llvm::SmallVector args; const auto &genericArgs = Node->getChild(1); if (genericArgs->getKind() != NodeKind::TypeList) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(genericArgs, "is not TypeList"); for (auto genericArg : *genericArgs) { auto paramType = decodeMangledType(genericArg); - if (!paramType) - return BuiltType(); - args.push_back(paramType); + if (paramType.isError()) + return paramType; + args.push_back(paramType.getType()); } auto ChildNode = Node->getChild(0); @@ -424,9 +437,9 @@ class TypeDecoder { BuiltTypeDecl typeDecl = BuiltTypeDecl(); BuiltType parent = BuiltType(); bool typeAlias = false; - if (!decodeMangledTypeDecl(ChildNode, typeDecl, - parent, typeAlias)) - return BuiltType(); + if (auto error = + decodeMangledTypeDecl(ChildNode, typeDecl, parent, typeAlias)) + return *error; return Builder.createBoundGenericType(typeDecl, args, parent); } @@ -456,11 +469,15 @@ class TypeDecoder { // But when resolving it to a type, we want to *keep* the argument // so that the parent type becomes 'S' and not 'P'. if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); const auto &genericArgs = Node->getChild(1); if (genericArgs->getNumChildren() != 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(genericArgs, + "expected 1 generic argument, saw %u", + genericArgs->getNumChildren()); return decodeMangledType(genericArgs->getChild(0)); } @@ -480,7 +497,7 @@ class TypeDecoder { auto reprNode = Node->getChild(i++); if (reprNode->getKind() != NodeKind::MetatypeRepresentation || !reprNode->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(reprNode, "wrong node kind or no text"); if (reprNode->getText() == "@thin") repr = ImplMetatypeRepresentation::Thin; else if (reprNode->getText() == "@thick") @@ -488,26 +505,28 @@ class TypeDecoder { else if (reprNode->getText() == "@objc_metatype") repr = ImplMetatypeRepresentation::ObjC; } else if (Node->getNumChildren() < 1) { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); } auto instance = decodeMangledType(Node->getChild(i)); - if (!instance) - return BuiltType(); + if (instance.isError()) + return instance; if (Node->getKind() == NodeKind::Metatype) { - return Builder.createMetatypeType(instance, repr); + return Builder.createMetatypeType(instance.getType(), repr); } else if (Node->getKind() == NodeKind::ExistentialMetatype) { - return Builder.createExistentialMetatypeType(instance, repr); + return Builder.createExistentialMetatypeType(instance.getType(), repr); } else { assert(false); - return nullptr; + return MAKE_NODE_TYPE_ERROR0(Node, + "Metatype/ExistentialMetatype Node " + "had a different kind when re-checked"); } } case NodeKind::ProtocolList: case NodeKind::ProtocolListWithAnyObject: case NodeKind::ProtocolListWithClass: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); // Find the protocol list. llvm::SmallVector Protocols; @@ -522,7 +541,8 @@ class TypeDecoder { if (auto Protocol = decodeMangledProtocolType(componentType)) Protocols.push_back(Protocol); else - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(componentType, + "failed to decode protocol type"); } // Superclass or AnyObject, if present. @@ -530,11 +550,15 @@ class TypeDecoder { auto Superclass = BuiltType(); if (Node->getKind() == NodeKind::ProtocolListWithClass) { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto superclassNode = Node->getChild(1); - Superclass = decodeMangledType(superclassNode); - if (!Superclass) return BuiltType(); + auto result = decodeMangledType(superclassNode); + if (result.isError()) + return result; + Superclass = result.getType(); IsClassBound = true; } else if (Node->getKind() == NodeKind::ProtocolListWithAnyObject) { @@ -552,17 +576,18 @@ class TypeDecoder { /*IsClassBound=*/false); } - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "failed to decode protocol type"); } case NodeKind::DynamicSelf: { if (Node->getNumChildren() != 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, "expected 1 child, saw %u", + Node->getNumChildren()); auto selfType = decodeMangledType(Node->getChild(0)); - if (!selfType) - return BuiltType(); + if (selfType.isError()) + return selfType; - return Builder.createDynamicSelfType(selfType); + return Builder.createDynamicSelfType(selfType.getType()); } case NodeKind::DependentGenericParamType: { auto depth = Node->getChild(0)->getIndex(); @@ -582,7 +607,9 @@ class TypeDecoder { case NodeKind::EscapingLinearFunctionType: case NodeKind::FunctionType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); FunctionTypeFlags flags; if (Node->getKind() == NodeKind::ObjCBlock || @@ -622,13 +649,16 @@ class TypeDecoder { flags = flags.withAsync(isAsync).withThrows(isThrow); if (Node->getNumChildren() < firstChildIdx + 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (%u)", + Node->getNumChildren(), firstChildIdx + 2); bool hasParamFlags = false; llvm::SmallVector, 8> parameters; if (!decodeMangledFunctionInputType(Node->getChild(firstChildIdx), parameters, hasParamFlags)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node->getChild(firstChildIdx), + "failed to decode function type"); flags = flags.withNumParameters(parameters.size()) .withParameterFlags(hasParamFlags) @@ -642,8 +672,9 @@ class TypeDecoder { NodeKind::EscapingLinearFunctionType); auto result = decodeMangledType(Node->getChild(firstChildIdx+1)); - if (!result) return BuiltType(); - return Builder.createFunctionType(parameters, result, flags); + if (result.isError()) + return result; + return Builder.createFunctionType(parameters, result.getType(), flags); } case NodeKind::ImplFunctionType: { auto calleeConvention = ImplParameterConvention::Direct_Unowned; @@ -657,7 +688,7 @@ class TypeDecoder { if (child->getKind() == NodeKind::ImplConvention) { if (!child->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); if (child->getText() == "@convention(thin)") { flags = @@ -667,7 +698,7 @@ class TypeDecoder { } } else if (child->getKind() == NodeKind::ImplFunctionAttribute) { if (!child->hasText()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "expected text"); StringRef text = child->getText(); if (text == "@convention(c)") { @@ -687,15 +718,18 @@ class TypeDecoder { flags = flags.withEscaping(); } else if (child->getKind() == NodeKind::ImplParameter) { if (decodeImplFunctionParam(child, parameters)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplResult) { if (decodeImplFunctionParam(child, results)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function parameter"); } else if (child->getKind() == NodeKind::ImplErrorResult) { if (decodeImplFunctionPart(child, errorResults)) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, + "failed to decode function part"); } else { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(child, "unexpected kind"); } } @@ -707,7 +741,8 @@ class TypeDecoder { errorResult = errorResults.front(); break; default: - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, "got %zu errors", + errorResults.size()); } // TODO: Some cases not handled above, but *probably* they cannot @@ -722,13 +757,13 @@ class TypeDecoder { case NodeKind::ArgumentTuple: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); return decodeMangledType(Node->getChild(0)); case NodeKind::ReturnType: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); return decodeMangledType(Node->getChild(0)); @@ -737,12 +772,13 @@ class TypeDecoder { std::string labels; for (auto &element : *Node) { if (element->getKind() != NodeKind::TupleElement) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind"); // If the tuple element is labeled, add its label to 'labels'. unsigned typeChildIndex = 0; if (element->getChild(typeChildIndex)->getKind() == NodeKind::VariadicMarker) { - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(element->getChild(typeChildIndex), + "no children"); } if (element->getChild(typeChildIndex)->getKind() == NodeKind::TupleElementName) { // Add spaces to terminate all the previous labels if this @@ -760,22 +796,23 @@ class TypeDecoder { } // Decode the element type. - BuiltType elementType = - decodeMangledType(element->getChild(typeChildIndex)); - if (!elementType) - return BuiltType(); + auto elementType = decodeMangledType(element->getChild(typeChildIndex)); + if (elementType.isError()) + return elementType; - elements.push_back(elementType); + elements.push_back(elementType.getType()); } return Builder.createTupleType(elements, std::move(labels)); } case NodeKind::TupleElement: if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); if (Node->getChild(0)->getKind() == NodeKind::TupleElementName) { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } @@ -783,68 +820,75 @@ class TypeDecoder { case NodeKind::DependentGenericType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } case NodeKind::DependentMemberType: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; auto assocTypeChild = Node->getChild(1); auto member = assocTypeChild->getFirstChild()->getText(); if (assocTypeChild->getNumChildren() < 2) - return Builder.createDependentMemberType(member.str(), base); + return Builder.createDependentMemberType(member.str(), base.getType()); auto protocol = decodeMangledProtocolType(assocTypeChild->getChild(1)); if (!protocol) return BuiltType(); - return Builder.createDependentMemberType(member.str(), base, protocol); + return Builder.createDependentMemberType(member.str(), base.getType(), + protocol); } case NodeKind::DependentAssociatedTypeRef: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); return decodeMangledType(Node->getChild(1)); } case NodeKind::Unowned: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createUnownedStorageType(base); + if (base.isError()) + return base; + return Builder.createUnownedStorageType(base.getType()); } case NodeKind::Unmanaged: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createUnmanagedStorageType(base); + if (base.isError()) + return base; + return Builder.createUnmanagedStorageType(base.getType()); } case NodeKind::Weak: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createWeakStorageType(base); + if (base.isError()) + return base; + return Builder.createWeakStorageType(base.getType()); } case NodeKind::SILBoxType: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); - return Builder.createSILBoxType(base); + if (base.isError()) + return base; + return Builder.createSILBoxType(base.getType()); } case NodeKind::SILBoxTypeWithLayout: { // TODO: Implement SILBoxTypeRefs with layout. As a stopgap, specify the @@ -853,57 +897,62 @@ class TypeDecoder { } case NodeKind::SugaredOptional: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createOptionalType(base); + return Builder.createOptionalType(base.getType()); } case NodeKind::SugaredArray: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createArrayType(base); + return Builder.createArrayType(base.getType()); } case NodeKind::SugaredDictionary: { if (Node->getNumChildren() < 2) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (2)", + Node->getNumChildren()); auto key = decodeMangledType(Node->getChild(0)); - if (!key) - return BuiltType(); + if (key.isError()) + return key; auto value = decodeMangledType(Node->getChild(1)); - if (!key) - return BuiltType(); + if (value.isError()) + return value; - return Builder.createDictionaryType(key, value); + return Builder.createDictionaryType(key.getType(), value.getType()); } case NodeKind::SugaredParen: { if (Node->getNumChildren() < 1) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "no children"); auto base = decodeMangledType(Node->getChild(0)); - if (!base) - return BuiltType(); + if (base.isError()) + return base; - return Builder.createParenType(base); + return Builder.createParenType(base.getType()); } case NodeKind::OpaqueType: { if (Node->getNumChildren() < 3) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR(Node, + "fewer children (%u) than required (3)", + Node->getNumChildren()); auto descriptor = Node->getChild(0); auto ordinalNode = Node->getChild(1); if (ordinalNode->getKind() != NodeKind::Index || !ordinalNode->hasIndex()) - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(ordinalNode, + "unexpected kind or no index"); auto ordinal = ordinalNode->getIndex(); std::vector genericArgsBuf; @@ -916,9 +965,9 @@ class TypeDecoder { break; for (auto argNode : *genericsNode) { auto arg = decodeMangledType(argNode); - if (!arg) - return BuiltType(); - genericArgsBuf.push_back(arg); + if (arg.isError()) + return arg; + genericArgsBuf.push_back(arg.getType()); } } genericArgsLevels.push_back(genericArgsBuf.size()); @@ -934,7 +983,7 @@ class TypeDecoder { // TODO: Handle OpaqueReturnType, when we're in the middle of reconstructing // the defining decl default: - return BuiltType(); + return MAKE_NODE_TYPE_ERROR0(Node, "unexpected kind"); } } @@ -954,11 +1003,11 @@ class TypeDecoder { T::getConventionFromString(conventionString); if (!convention) return true; - BuiltType type = decodeMangledType(node->getChild(1)); - if (!type) + auto type = decodeMangledType(node->getChild(1)); + if (type.isError()) return true; - results.emplace_back(type, *convention); + results.emplace_back(type.getType(), *convention); return false; } @@ -979,8 +1028,8 @@ class TypeDecoder { auto convention = T::getConventionFromString(conventionString); if (!convention) return true; - BuiltType type = decodeMangledType(typeNode); - if (!type) + auto result = decodeMangledType(typeNode); + if (result.isError()) return true; auto diffKind = T::DifferentiabilityType::DifferentiableOrNotApplicable; @@ -995,14 +1044,13 @@ class TypeDecoder { diffKind = *optDiffKind; } - results.emplace_back(type, *convention, diffKind); + results.emplace_back(result.getType(), *convention, diffKind); return false; } - bool decodeMangledTypeDecl(Demangle::NodePointer node, - BuiltTypeDecl &typeDecl, - BuiltType &parent, - bool &typeAlias) { + llvm::Optional + decodeMangledTypeDecl(Demangle::NodePointer node, BuiltTypeDecl &typeDecl, + BuiltType &parent, bool &typeAlias) { if (node->getKind() == NodeKind::Type) return decodeMangledTypeDecl(node->getChild(0), typeDecl, parent, typeAlias); @@ -1013,7 +1061,9 @@ class TypeDecoder { declNode = node; } else { if (node->getNumChildren() < 2) - return false; + return MAKE_NODE_TYPE_ERROR( + node, "Number of node children (%u) less than required (2)", + node->getNumChildren()); auto parentContext = node->getChild(0); @@ -1029,11 +1079,14 @@ class TypeDecoder { case Node::Kind::Extension: // Decode the type being extended. if (parentContext->getNumChildren() < 2) - return false; + return MAKE_NODE_TYPE_ERROR(parentContext, + "Number of parentContext children (%u) " + "less than required (2)", + node->getNumChildren()); parentContext = parentContext->getChild(1); LLVM_FALLTHROUGH; default: - parent = decodeMangledType(parentContext); + parent = decodeMangledType(parentContext).getType(); // Remove any generic arguments from the context node, producing a // node that references the nominal type declaration. declNode = Demangle::getUnspecialized(node, Builder.getNodeFactory()); @@ -1041,9 +1094,10 @@ class TypeDecoder { } } typeDecl = Builder.createTypeDecl(declNode, typeAlias); - if (!typeDecl) return false; + if (!typeDecl) + return TypeLookupError("Failed to create type decl"); - return true; + return llvm::None; } BuiltProtocolDecl decodeMangledProtocolType(Demangle::NodePointer node) { @@ -1108,10 +1162,10 @@ class TypeDecoder { } auto paramType = decodeMangledType(node); - if (!paramType) + if (paramType.isError()) return false; - param.setType(paramType); + param.setType(paramType.getType()); return true; }; @@ -1169,14 +1223,12 @@ class TypeDecoder { } }; -template -inline typename BuilderType::BuiltType -decodeMangledType(BuilderType &Builder, - NodePointer Node) { +template +inline TypeLookupErrorOr +decodeMangledType(BuilderType &Builder, NodePointer Node) { return TypeDecoder(Builder).decodeMangledType(Node); } - SWIFT_END_INLINE_NAMESPACE } // end namespace Demangle } // end namespace swift diff --git a/include/swift/Demangling/TypeLookupError.h b/include/swift/Demangling/TypeLookupError.h new file mode 100644 index 0000000000000..c67fc35598be8 --- /dev/null +++ b/include/swift/Demangling/TypeLookupError.h @@ -0,0 +1,198 @@ +//===--- TypeLookupError.h - Type lookup error value. -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Provides the TypeLookupError class, which represents errors when demangling +// or looking up types. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DEMANGLING_TypeLookupError_H +#define SWIFT_DEMANGLING_TypeLookupError_H + +#include "swift/Basic/TaggedUnion.h" +#include "swift/Runtime/Portability.h" + +namespace swift { + +/// An error that occurred while looking up a type at runtime from a mangled +/// name. +/// /// +/// This ultimately just provides a string, but is built to take up minimal +/// space when passed around, and perform as much work lazily as possible. We +/// don't want to spend a lot of time building strings when the caller is going +/// to handle the error gracefully and try a fallback. We only want to waste +/// time/space on that when the error message is actually relevant, so build it +/// as late as possible. +/// /// +/// To be as compact as possible, this type holds a context pointer and a +/// callback function. The callback function uses the context pointer to return +/// an error string when requested. The callback function also does quadruple +/// duty to copy/destroy the context as needed, and free the returned error +/// string if needed. Commands are passed to the callback to request the +/// different operations, which means we only have to store one function pointer +/// instead of four. +class TypeLookupError { +public: + /// The commands that can be passed to the callback function. + enum class Command { + /// Return the error string to the caller, as a char *. + CopyErrorString, + + /// Destroy the error string returned from CopyErrorString, if necessary. + /// The return value is ignored. + DestroyErrorString, + + /// Return a copy of the context pointer (used for copying TypeLookupError + /// objects.) + CopyContext, + + /// Destroy the context pointer. The return value is ignored. + DestroyContext, + }; + + /// The callback used to respond to the commands. The parameters are: + /// - `context`: the context that the value was initialized with, or the + /// context returned from a CopyContext call + /// - `command`: the command to respond to + /// - `param`: when `command` is `DestroyErrorString`, the string pointer to + /// destroy, otherwise NULL + using Callback = void *(*)(void *context, Command command, void *param); + +private: + void *Context; + Callback Fn; + + /// A no-op callback used to avoid a bunch of `if (Fn)` checks. + static void *nop(void *context, Command command, void *param) { + return nullptr; + } + + /// Helper functions for getting a C string from a lambda. These allow us to + /// wrap lambdas returning `char *` or `std::string` and standardize them on + /// `char *`. + static char *getCString(char *str) { return str; } + + static char *getCString(const std::string &str) { + return strdup(str.c_str()); + } + +public: + TypeLookupError(const TypeLookupError &other) { + Fn = other.Fn; + Context = other.Fn(other.Context, Command::CopyContext, nullptr); + } + + TypeLookupError(TypeLookupError &&other) { + Fn = other.Fn; + Context = other.Context; + + other.Fn = nop; + other.Context = nullptr; + } + + ~TypeLookupError() { Fn(Context, Command::DestroyContext, nullptr); } + + TypeLookupError(void *context, Callback fn) : Context(context), Fn(fn ? fn : nop) {} + + TypeLookupError &operator=(const TypeLookupError &other) { + if (this == &other) + return *this; + + Fn(Context, Command::DestroyContext, nullptr); + Fn = other.Fn; + Context = Fn(Context, Command::CopyContext, nullptr); + + return *this; + } + + /// Construct a TypeLookupError that just returns a constant C string. + TypeLookupError(const char *str) + : TypeLookupError([=] { return const_cast(str); }) {} + + /// Construct a TypeLookupError that creates a string using asprintf. The passed-in + /// format string and arguments are passed directly to swift_asprintf when + /// the string is requested. The arguments are captured and the string is only + /// formatted when needed. + template + TypeLookupError(const char *fmt, Args... args) + : TypeLookupError([=] { + char *str; + swift_asprintf(&str, fmt, args...); + return str; + }) {} + + /// Construct a TypeLookupError that wraps a function returning a string. The + /// passed-in function can return either a `std::string` or `char *`. If it + /// returns `char *` then the string will be destroyed with `free()`. + template TypeLookupError(const F &fn) { + Context = new F(fn); + Fn = [](void *context, Command command, void *param) -> void * { + auto castContext = reinterpret_cast(context); + switch (command) { + case Command::CopyErrorString: { + return TypeLookupError::getCString((*castContext)()); + } + case Command::DestroyErrorString: + free(param); + return nullptr; + case Command::CopyContext: + return new F(*castContext); + case Command::DestroyContext: + delete castContext; + return nullptr; + } + }; + } + + /// Get the error string from the error value. The value must be passed to + /// `freeErrorString` when done. (Unless you're just calling a `fatalError` + /// in which case there's no point.) + char *copyErrorString() { + return reinterpret_cast( + Fn(Context, Command::CopyErrorString, nullptr)); + } + + /// Free an error string previously obtained from `copyErrorString`. + void freeErrorString(char *str) { + Fn(Context, Command::DestroyErrorString, str); + } +}; + +/// A value that's either a `TypeLookupError` or some parameterized type value `T`. A +/// convenience wrapper around `TaggedUnion`. +template class TypeLookupErrorOr { + TaggedUnion Value; + +public: + TypeLookupErrorOr(const T &t) : Value(t) { + if (!t) + Value = TypeLookupError("unknown error"); + } + + TypeLookupErrorOr(const TypeLookupError &te) : Value(te) {} + + T getType() { + if (auto *ptr = Value.template dyn_cast()) + return *ptr; + return T(); + } + + TypeLookupError *getError() { + return Value.template dyn_cast(); + } + + bool isError() { return getError() != nullptr; } +}; + +} // namespace swift + +#endif // SWIFT_DEMANGLING_TypeLookupError_H diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index f5f75f13e7694..39ab041dbcc1b 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -621,7 +621,7 @@ class CompilerInstance { /// /// This is similar to a parse-only invocation, but module imports will also /// be processed. - void performParseAndResolveImportsOnly(); + bool performParseAndResolveImportsOnly(); /// Performs mandatory, diagnostic, and optimization passes over the SIL. /// \param silModule The SIL module that was generated during SILGen. diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 724cf7e6efa31..c961794d35582 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -294,9 +294,16 @@ class FrontendOptions { /// '.../lib/swift', otherwise '.../lib/swift_static'. bool UseSharedResourceFolder = true; - /// \return true if action only parses without doing other compilation steps. + /// \return true if the given action only parses without doing other compilation steps. static bool shouldActionOnlyParse(ActionType); + /// \return true if the given action requires the standard library to be + /// loaded before it is run. + static bool doesActionRequireSwiftStandardLibrary(ActionType); + + /// \return true if the given action requires input files to be provided. + static bool doesActionRequireInputs(ActionType action); + /// Return a hash code of any components from these options that should /// contribute to a Swift Bridging PCH hash. llvm::hash_code getPCHHashComponents() const { diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 723fdcd956a22..7f1b3ddba9249 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1133,8 +1133,7 @@ class Parser { ParseDeclOptions Flags, DeclAttributes &Attributes, bool HasFuncKeyword = true); - ParserResult - parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); + BraceStmt *parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); void parseAbstractFunctionBody(AbstractFunctionDecl *AFD); BraceStmt *parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD); ParserResult parseDeclProtocol(ParseDeclOptions Flags, diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index f375918a5ff5d..3914810cbfd8f 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -618,8 +618,8 @@ class TypeRefBuilder { }), OpaqueUnderlyingTypeReader( [&reader](uint64_t descriptorAddr, unsigned ordinal) -> const TypeRef* { - return reader.readUnderlyingTypeForOpaqueTypeDescriptor(descriptorAddr, - ordinal); + return reader.readUnderlyingTypeForOpaqueTypeDescriptor( + descriptorAddr, ordinal).getType(); }) {} diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 4450066283afc..0090ae0746ed6 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -463,7 +463,8 @@ class MetadataReader { } /// Given a demangle tree, attempt to turn it into a type. - BuiltType decodeMangledType(NodePointer Node) { + TypeLookupErrorOr + decodeMangledType(NodePointer Node) { return swift::Demangle::decodeMangledType(Builder, Node); } @@ -925,8 +926,8 @@ class MetadataReader { swift_runtime_unreachable("Unhandled MetadataKind in switch"); } - BuiltType readTypeFromMangledName(const char *MangledTypeName, - size_t Length) { + TypeLookupErrorOr + readTypeFromMangledName(const char *MangledTypeName, size_t Length) { Demangle::Demangler Dem; Demangle::NodePointer Demangled = Dem.demangleSymbol(StringRef(MangledTypeName, Length)); @@ -1183,14 +1184,14 @@ class MetadataReader { MangledNameKind::Type, Dem); } - BuiltType + TypeLookupErrorOr readUnderlyingTypeForOpaqueTypeDescriptor(StoredPointer contextAddr, unsigned ordinal) { Demangle::Demangler Dem; auto node = readUnderlyingTypeManglingForOpaqueTypeDescriptor(contextAddr, ordinal, Dem); if (!node) - return BuiltType(); + return TypeLookupError("Failed to read type mangling for descriptor."); return decodeMangledType(node); } diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 86e19642e51a7..f14e8d16f1554 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -21,7 +21,6 @@ #include "swift/Runtime/Unreachable.h" #include #include -#include #include #include @@ -248,39 +247,6 @@ std::atomic _swift_debug_metadataAllocationBacktraceList; SWIFT_RUNTIME_STDLIB_SPI const void * const _swift_debug_protocolConformanceStatePointer; -SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE -inline static int swift_asprintf(char **strp, const char *fmt, ...) { - va_list args; - va_start(args, fmt); -#if defined(_WIN32) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" - int len = _vscprintf(fmt, args); -#pragma GCC diagnostic pop - if (len < 0) { - va_end(args); - return -1; - } - char *buffer = static_cast(malloc(len + 1)); - if (!buffer) { - va_end(args); - return -1; - } - int result = vsprintf(buffer, fmt, args); - if (result < 0) { - va_end(args); - free(buffer); - return -1; - } - *strp = buffer; -#else - int result = vasprintf(strp, fmt, args); -#endif - va_end(args); - return result; -} - - // namespace swift } diff --git a/include/swift/Runtime/Portability.h b/include/swift/Runtime/Portability.h index 9e4fc418e162f..cd19b2c4193ba 100644 --- a/include/swift/Runtime/Portability.h +++ b/include/swift/Runtime/Portability.h @@ -16,8 +16,47 @@ #ifndef SWIFT_RUNTIME_PORTABILITY_H #define SWIFT_RUNTIME_PORTABILITY_H + +#include #include +#include +#include size_t _swift_strlcpy(char *dst, const char *src, size_t maxlen); +// Skip the attribute when included by the compiler. +#ifdef SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE +SWIFT_RUNTIME_ATTRIBUTE_ALWAYS_INLINE +#endif +inline static int swift_asprintf(char **strp, const char *fmt, ...) { + va_list args; + va_start(args, fmt); +#if defined(_WIN32) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + int len = _vscprintf(fmt, args); +#pragma GCC diagnostic pop + if (len < 0) { + va_end(args); + return -1; + } + char *buffer = static_cast(malloc(len + 1)); + if (!buffer) { + va_end(args); + return -1; + } + int result = vsprintf(buffer, fmt, args); + if (result < 0) { + va_end(args); + free(buffer); + return -1; + } + *strp = buffer; +#else + int result = vasprintf(strp, fmt, args); +#endif + va_end(args); + return result; +} + #endif diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index aba919ee0090c..344087f0a1aaf 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -992,7 +992,6 @@ class TypeConverter { /// Given a function type, yield its bridged formal type. CanAnyFunctionType getBridgedFunctionType(AbstractionPattern fnPattern, CanAnyFunctionType fnType, - AnyFunctionType::ExtInfo extInfo, Bridgeability bridging); /// Given a referenced value and the substituted formal type of a diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fa010c0a30c7f..7a4648f45a459 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -18,6 +18,7 @@ #include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" @@ -281,6 +282,10 @@ struct ASTContext::Implementation { llvm::DenseMap ForeignErrorConventions; + /// Map from declarations to foreign async conventions. + llvm::DenseMap ForeignAsyncConventions; + /// Cache of previously looked-up precedence queries. AssociativityCacheType AssociativityCache; @@ -2239,6 +2244,24 @@ AbstractFunctionDecl::getForeignErrorConvention() const { return it->second; } +void AbstractFunctionDecl::setForeignAsyncConvention( + const ForeignAsyncConvention &conv) { + assert(hasAsync() && "setting error convention on non-throwing decl"); + auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions; + assert(!conventionsMap.count(this) && "error convention already set"); + conventionsMap.insert({this, conv}); +} + +Optional +AbstractFunctionDecl::getForeignAsyncConvention() const { + if (!hasAsync()) + return None; + auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions; + auto it = conventionsMap.find(this); + if (it == conventionsMap.end()) return None; + return it->second; +} + Optional swift::getKnownFoundationEntity(StringRef name){ return llvm::StringSwitch>(name) #define FOUNDATION_ENTITY(Name) .Case(#Name, KnownFoundationEntity::Name) @@ -3144,16 +3167,16 @@ FunctionType *FunctionType::get(ArrayRef params, return funcTy; } - Optional clangTypeInfo = info.getClangTypeInfo(); + auto clangTypeInfo = info.getClangTypeInfo(); size_t allocSize = totalSizeToAlloc( - params.size(), clangTypeInfo.hasValue() ? 1 : 0); + params.size(), clangTypeInfo.empty() ? 0 : 1); void *mem = ctx.Allocate(allocSize, alignof(FunctionType), arena); bool isCanonical = isFunctionTypeCanonical(params, result); - if (clangTypeInfo.hasValue()) { + if (!clangTypeInfo.empty()) { if (ctx.LangOpts.UseClangFunctionTypes) - isCanonical &= clangTypeInfo->type->isCanonicalUnqualified(); + isCanonical &= clangTypeInfo.getType()->isCanonicalUnqualified(); else isCanonical = false; } @@ -3175,8 +3198,8 @@ FunctionType::FunctionType(ArrayRef params, std::uninitialized_copy(params.begin(), params.end(), getTrailingObjects()); auto clangTypeInfo = info.getClangTypeInfo(); - if (clangTypeInfo.hasValue()) - *getTrailingObjects() = clangTypeInfo.getValue(); + if (!clangTypeInfo.empty()) + *getTrailingObjects() = clangTypeInfo; } void GenericFunctionType::Profile(llvm::FoldingSetNodeID &ID, @@ -4471,16 +4494,29 @@ Type ASTContext::getBridgedToObjC(const DeclContext *dc, Type type, return Type(); } -const clang::Type * -ASTContext::getClangFunctionType(ArrayRef params, - Type resultTy, - FunctionTypeRepresentation trueRep) { +ClangTypeConverter &ASTContext::getClangTypeConverter() { auto &impl = getImpl(); if (!impl.Converter) { auto *cml = getClangModuleLoader(); impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); } - return impl.Converter.getValue().getFunctionType(params, resultTy, trueRep); + return impl.Converter.getValue(); +} + +const clang::Type * +ASTContext::getClangFunctionType(ArrayRef params, + Type resultTy, + FunctionTypeRepresentation trueRep) { + return getClangTypeConverter().getFunctionType(params, resultTy, trueRep); +} + +const clang::Type * +ASTContext::getCanonicalClangFunctionType( + ArrayRef params, + Optional result, + SILFunctionType::Representation trueRep) { + auto *ty = getClangTypeConverter().getFunctionType(params, result, trueRep); + return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; } const Decl * diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 327b401cd5df8..d5fbfa63f56ae 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -44,7 +44,7 @@ Type swift::Demangle::getTypeForMangling(ASTContext &ctx, return Type(); ASTBuilder builder(ctx); - return swift::Demangle::decodeMangledType(builder, node); + return swift::Demangle::decodeMangledType(builder, node).getType(); } TypeDecl *swift::Demangle::getTypeDeclForMangling(ASTContext &ctx, @@ -200,21 +200,16 @@ createSubstitutionMapFromGenericArgs(GenericSignature genericSig, if (!genericSig) return SubstitutionMap(); - SmallVector genericParams; - genericSig->forEachParam([&](GenericTypeParamType *gp, bool canonical) { - if (canonical) - genericParams.push_back(gp); - }); - if (genericParams.size() != args.size()) + if (genericSig->getGenericParams().size() != args.size()) return SubstitutionMap(); return SubstitutionMap::get( genericSig, [&](SubstitutableType *t) -> Type { - for (unsigned i = 0, e = genericParams.size(); i < e; ++i) { - if (t->isEqual(genericParams[i])) - return args[i]; - } + auto *gp = cast(t); + unsigned ordinal = genericSig->getGenericParamOrdinal(gp); + if (ordinal < args.size()) + return args[ordinal]; return Type(); }, LookUpConformanceInModule(moduleDecl)); @@ -846,8 +841,8 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( if (child->getNumChildren() != 2) return CanGenericSignature(); - auto subjectType = swift::Demangle::decodeMangledType( - *this, child->getChild(0)); + auto subjectType = + swift::Demangle::decodeMangledType(*this, child->getChild(0)).getType(); if (!subjectType) return CanGenericSignature(); @@ -856,8 +851,9 @@ CanGenericSignature ASTBuilder::demangleGenericSignature( Demangle::Node::Kind::DependentGenericConformanceRequirement || child->getKind() == Demangle::Node::Kind::DependentGenericSameTypeRequirement) { - constraintType = swift::Demangle::decodeMangledType( - *this, child->getChild(1)); + constraintType = + swift::Demangle::decodeMangledType(*this, child->getChild(1)) + .getType(); if (!constraintType) return CanGenericSignature(); } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 73ac64e84e964..168f249898cf5 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3630,7 +3630,7 @@ void printCType(ASTContext &Ctx, ASTPrinter &Printer, ExtInfo &info) { auto *cml = Ctx.getClangModuleLoader(); SmallString<64> buf; llvm::raw_svector_ostream os(buf); - info.getClangTypeInfo().getValue().printType(cml, os); + info.getClangTypeInfo().printType(cml, os); Printer << ", cType: " << QuotedString(os.str()); } @@ -4066,7 +4066,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; // [TODO: Clang-type-plumbing] Remove the second check. - if (printNameOnly || !info.getClangTypeInfo().hasValue()) + if (printNameOnly || info.getClangTypeInfo().empty()) break; printCType(Ctx, Printer, info); break; @@ -4132,7 +4132,7 @@ class TypePrinter : public TypeVisitor { case SILFunctionType::Representation::CFunctionPointer: Printer << "c"; // [TODO: Clang-type-plumbing] Remove the second check. - if (printNameOnly || !info.getClangTypeInfo().hasValue()) + if (printNameOnly || info.getClangTypeInfo().empty()) break; printCType(Ctx, Printer, info); break; diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index d4ccea512f069..9fd13f797798d 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -158,6 +158,57 @@ const clang::Type *ClangTypeConverter::getFunctionType( llvm_unreachable("invalid representation"); } +const clang::Type *ClangTypeConverter::getFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation repr) { + + // Using the interface type is sufficient as type parameters get mapped to + // `id`, since ObjC lightweight generics use type erasure. (See also: SE-0057) + auto resultClangTy = result.hasValue() + ? convert(result.getValue().getInterfaceType()) + : ClangASTContext.VoidTy; + + if (resultClangTy.isNull()) + return nullptr; + + SmallVector extParamInfos; + SmallVector paramsClangTy; + bool someParamIsConsumed = false; + for (auto &p : params) { + auto pc = convert(p.getInterfaceType()); + if (pc.isNull()) + return nullptr; + clang::FunctionProtoType::ExtParameterInfo extParamInfo; + if (p.isConsumed()) { + someParamIsConsumed = true; + extParamInfo = extParamInfo.withIsConsumed(true); + } + extParamInfos.push_back(extParamInfo); + paramsClangTy.push_back(pc); + } + + clang::FunctionProtoType::ExtProtoInfo info(clang::CallingConv::CC_C); + if (someParamIsConsumed) + info.ExtParameterInfos = extParamInfos.begin(); + auto fn = ClangASTContext.getFunctionType(resultClangTy, paramsClangTy, info); + if (fn.isNull()) + return nullptr; + + switch (repr) { + case SILFunctionType::Representation::CFunctionPointer: + return ClangASTContext.getPointerType(fn).getTypePtr(); + case SILFunctionType::Representation::Block: + return ClangASTContext.getBlockPointerType(fn).getTypePtr(); + case SILFunctionType::Representation::Thick: + case SILFunctionType::Representation::Thin: + case SILFunctionType::Representation::Method: + case SILFunctionType::Representation::ObjCMethod: + case SILFunctionType::Representation::WitnessMethod: + case SILFunctionType::Representation::Closure: + llvm_unreachable("Expected a C-compatible representation."); + } +} + clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, StringRef memberName) { auto memberTypeDecl = cast( @@ -576,12 +627,19 @@ clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type) { } clang::QualType ClangTypeConverter::visitSILFunctionType(SILFunctionType *type) { - llvm::report_fatal_error("Expected only AST types but found a SIL function."); + // We must've already computed it before if applicable. + return clang::QualType(type->getClangTypeInfo().getType(), 0); } clang::QualType ClangTypeConverter::visitSILBlockStorageType(SILBlockStorageType *type) { - llvm::report_fatal_error("Expected only AST types but found a SIL block."); + // We'll select (void)(^)(). This isn't correct for all blocks, but block + // storage type should only be converted for function signature lowering, + // where the parameter types do not matter. + auto &clangCtx = ClangASTContext; + auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy); + auto blockTy = clangCtx.getBlockPointerType(fnTy); + return clangCtx.getCanonicalType(blockTy); } clang::QualType diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index 450b9e30cf19a..e41a824163312 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -74,6 +74,11 @@ class ClangTypeConverter : ArrayRef params, Type resultTy, AnyFunctionType::Representation repr); + /// Compute the C function type for a SIL function type. + const clang::Type *getFunctionType( + ArrayRef params, Optional result, + SILFunctionType::Representation repr); + /// Check whether the given Clang declaration is an export of a Swift /// declaration introduced by this converter, and if so, return the original /// Swift declaration. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index f35ff446cc486..336527aae6a1e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7256,13 +7256,14 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, DeclName Name, SourceLoc NameLoc, - bool Throws, ParameterList *BodyParams, + bool Async, bool Throws, + ParameterList *BodyParams, Type FnRetType, DeclContext *Parent, ClangNode ClangN) { assert(ClangN && FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - /*Async=*/false, SourceLoc(), Throws, SourceLoc(), + Async, SourceLoc(), Throws, SourceLoc(), /*GenericParams=*/nullptr, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); diff --git a/lib/AST/ExtInfo.cpp b/lib/AST/ExtInfo.cpp index 9b03d36d61d74..aba9bb2577ce0 100644 --- a/lib/AST/ExtInfo.cpp +++ b/lib/AST/ExtInfo.cpp @@ -19,6 +19,18 @@ #include "clang/AST/Type.h" +static void assertIsFunctionType(const clang::Type *type) { +#ifndef NDEBUG + if (!(type->isFunctionPointerType() || type->isBlockPointerType() || + type->isFunctionReferenceType())) { + llvm::errs() << "Expected a Clang function type wrapped in a pointer type " + << "or a block pointer type but found:\n"; + type->dump(); + llvm_unreachable("\nUnexpected Clang type when creating ExtInfo!"); + } +#endif +} + namespace swift { // MARK: - ClangTypeInfo @@ -54,21 +66,12 @@ void ClangTypeInfo::dump(llvm::raw_ostream &os, // MARK: - ASTExtInfoBuilder -void ASTExtInfoBuilder::assertIsFunctionType(const clang::Type *type) { -#ifndef NDEBUG - if (!(type->isFunctionPointerType() || type->isBlockPointerType() || - type->isFunctionReferenceType())) { - llvm::errs() << "Expected a Clang function type wrapped in a pointer type " - << "or a block pointer type but found:\n"; - type->dump(); - llvm_unreachable("\nUnexpected Clang type when creating ExtInfo!"); - } -#endif - return; -} - void ASTExtInfoBuilder::checkInvariants() const { - // TODO: Add validation checks here while making sure things don't blow up. + // TODO: [clang-function-type-serialization] Once we start serializing + // the Clang type, we should also assert that the pointer is non-null. + auto Rep = Representation(bits & RepresentationMask); + if ((Rep == Representation::CFunctionPointer) && clangTypeInfo.type) + assertIsFunctionType(clangTypeInfo.type); } // MARK: - ASTExtInfo diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index b581137926415..7ef21cadcb205 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -2434,6 +2434,53 @@ CustomAttrNominalRequest::evaluate(Evaluator &evaluator, } } + // If we have more than one attribute declaration, we have an ambiguity. + // So, emit an ambiguity diagnostic. + if (auto typeRepr = attr->getTypeRepr()) { + if (nominals.size() > 1) { + SmallVector ambiguousCandidates; + // Filter out declarations that cannot be attributes. + for (auto decl : nominals) { + if (isa(decl)) { + continue; + } + ambiguousCandidates.push_back(decl); + } + if (ambiguousCandidates.size() > 1) { + auto attrName = nominals.front()->getName(); + ctx.Diags.diagnose(typeRepr->getLoc(), + diag::ambiguous_custom_attribute_ref, attrName); + for (auto candidate : ambiguousCandidates) { + ctx.Diags.diagnose(candidate->getLoc(), + diag::found_attribute_candidate); + // If the candidate is a top-level attribute, let's suggest + // adding module name to resolve the ambiguity. + if (candidate->getDeclContext()->isModuleScopeContext()) { + auto moduleName = candidate->getParentModule()->getName(); + ctx.Diags + .diagnose(typeRepr->getLoc(), + diag::ambiguous_custom_attribute_ref_fix, + moduleName.str(), attrName, moduleName) + .fixItInsert(typeRepr->getLoc(), moduleName.str().str() + "."); + } + } + return nullptr; + } + } + } + + // There is no nominal type with this name, so complain about this being + // an unknown attribute. + std::string typeName; + if (auto typeRepr = attr->getTypeRepr()) { + llvm::raw_string_ostream out(typeName); + typeRepr->print(out); + } else { + typeName = attr->getType().getString(); + } + + ctx.Diags.diagnose(attr->getLocation(), diag::unknown_attribute, typeName); + return nullptr; } diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index 3f5acc3f7c2a0..6c38957639336 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -724,8 +724,7 @@ bool importer::isUnavailableInSwift( return false; } -OptionalTypeKind importer::getParamOptionality(version::Version swiftVersion, - const clang::ParmVarDecl *param, +OptionalTypeKind importer::getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull) { auto &clangCtx = param->getASTContext(); diff --git a/lib/ClangImporter/ClangAdapter.h b/lib/ClangImporter/ClangAdapter.h index 0b066364bd622..9a15bbd523efa 100644 --- a/lib/ClangImporter/ClangAdapter.h +++ b/lib/ClangImporter/ClangAdapter.h @@ -161,15 +161,11 @@ bool isUnavailableInSwift(const clang::Decl *decl, const PlatformAvailability &, /// Determine the optionality of the given Clang parameter. /// -/// \param swiftLanguageVersion What version of Swift we're using, which affects -/// how optionality is inferred. -/// /// \param param The Clang parameter. /// /// \param knownNonNull Whether a function- or method-level "nonnull" attribute /// applies to this parameter. -OptionalTypeKind getParamOptionality(version::Version swiftLanguageVersion, - const clang::ParmVarDecl *param, +OptionalTypeKind getParamOptionality(const clang::ParmVarDecl *param, bool knownNonNull); } } diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 4aab51c8879c9..c7ad76043b74e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -3681,6 +3681,7 @@ void ClangImporter::Implementation::lookupValue( clangDecl->getMostRecentDecl(); CurrentVersion.forEachOtherImportNameVersion( + SwiftContext.LangOpts.EnableExperimentalConcurrency, [&](ImportNameVersion nameVersion) { if (anyMatching) return; @@ -3690,6 +3691,12 @@ void ClangImporter::Implementation::lookupValue( if (!newName.getDeclName().matchesRef(name)) return; + // If we asked for an async import and didn't find one, skip this. + // This filters out duplicates. + if (nameVersion.supportsConcurrency() && + !newName.getAsyncInfo()) + return; + const clang::DeclContext *clangDC = newName.getEffectiveContext().getAsDeclContext(); if (!clangDC || !clangDC->isFileContext()) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index a919c8758d057..b61bf24376e08 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -163,6 +163,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, DeclName name, SourceLoc nameLoc, ParameterList *bodyParams, Type resultTy, + bool async, bool throws, DeclContext *dc, ClangNode clangNode) { @@ -179,7 +180,7 @@ static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, bodyParams, resultTy, dc, clangNode); } else { - return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, throws, + return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, async, throws, bodyParams, resultTy, dc, clangNode); } } @@ -2259,7 +2260,7 @@ namespace { /// Whether the names we're importing are from the language version the user /// requested, or if these are decls from another version bool isActiveSwiftVersion() const { - return getVersion() == getActiveSwiftVersion(); + return getVersion().withConcurrency(false) == getActiveSwiftVersion().withConcurrency(false); } void recordMemberInContext(const DeclContext *dc, ValueDecl *member) { @@ -2305,7 +2306,7 @@ namespace { return canonicalName; } - // Special handling when we import using the older Swift name. + // Special handling when we import using the alternate Swift name. // // Import using the alternate Swift name. If that fails, or if it's // identical to the active Swift name, we won't introduce an alternate @@ -2314,6 +2315,19 @@ namespace { if (!alternateName) return ImportedName(); + // Importing for concurrency is special in that the same declaration + // is imported both with a completion handler parameter and as 'async', + // creating two separate declarations. + if (getVersion().supportsConcurrency()) { + // If the resulting name isn't special for concurrency, it's not + // different. + if (!alternateName.getAsyncInfo()) + return ImportedName(); + + // Otherwise, it's a legitimately different import. + return alternateName; + } + if (alternateName.getDeclName() == canonicalName.getDeclName() && alternateName.getEffectiveContext().equalsWithoutResolving( canonicalName.getEffectiveContext())) { @@ -2475,6 +2489,13 @@ namespace { return; } + // If this the active and current Swift versions differ based on + // concurrency, it's not actually a variant. + if (getVersion().supportsConcurrency() != + getActiveSwiftVersion().supportsConcurrency()) { + return; + } + // TODO: some versions should be deprecated instead of unavailable ASTContext &ctx = decl->getASTContext(); @@ -3842,9 +3863,10 @@ namespace { // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); - result = createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*throws*/ false, dc, decl); + result = createFuncOrAccessor( + Impl.SwiftContext, loc, accessorInfo, name, + nameLoc, bodyParams, resultTy, + /*async*/ false, /*throws*/ false, dc, decl); if (!dc->isModuleScopeContext()) { if (selfIsInOut) @@ -4415,6 +4437,16 @@ namespace { } } + // Determine whether the function is throwing and/or async. + bool throws = importedName.getErrorInfo().hasValue(); + bool async = false; + auto asyncConvention = importedName.getAsyncInfo(); + if (asyncConvention) { + async = true; + if (asyncConvention->isThrowing()) + throws = true; + } + auto resultTy = importedType.getType(); auto isIUO = importedType.isImplicitlyUnwrapped(); @@ -4444,8 +4476,7 @@ namespace { importedName.getDeclName(), /*nameLoc*/SourceLoc(), bodyParams, resultTy, - importedName.getErrorInfo().hasValue(), - dc, decl); + async, throws, dc, decl); result->setAccess(getOverridableAccessLevel(dc)); @@ -4477,6 +4508,11 @@ namespace { result->setForeignErrorConvention(*errorConvention); } + // Record the async convention. + if (asyncConvention) { + result->setForeignAsyncConvention(*asyncConvention); + } + // Handle attributes. if (decl->hasAttr() && isa(result) && @@ -4508,6 +4544,7 @@ namespace { Impl.addAlternateDecl(result, cast(imported)); } } + return result; } @@ -6448,6 +6485,7 @@ ConstructorDecl *SwiftDeclConverter::importConstructor( return known->second; // Create the actual constructor. + assert(!importedName.getAsyncInfo()); auto result = Impl.createDeclWithClangNode( objcMethod, AccessLevel::Public, importedName.getDeclName(), /*NameLoc=*/SourceLoc(), failability, /*FailabilityLoc=*/SourceLoc(), @@ -7150,6 +7188,11 @@ void SwiftDeclConverter::importMirroredProtocolMembers( if (isa(afd)) return; + // Asynch methods are also always imported without async, so don't + // record them here. + if (afd->hasAsync()) + return; + auto objcMethod = dyn_cast_or_null(member->getClangDecl()); if (!objcMethod) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 46817ce93de2a..fc0313bbc91df 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -807,8 +807,6 @@ static bool omitNeedlessWordsInFunctionName( Optional errorParamIndex, bool returnsSelf, bool isInstanceMethod, NameImporter &nameImporter) { clang::ASTContext &clangCtx = nameImporter.getClangContext(); - const version::Version &swiftLanguageVersion = - nameImporter.getLangOpts().EffectiveLanguageVersion; // Collect the parameter type names. StringRef firstParamName; @@ -837,8 +835,7 @@ static bool omitNeedlessWordsInFunctionName( bool hasDefaultArg = ClangImporter::Implementation::inferDefaultArgument( param->getType(), - getParamOptionality(swiftLanguageVersion, param, - !nonNullArgs.empty() && nonNullArgs[i]), + getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[i]), nameImporter.getIdentifier(baseName), argumentName, i == 0, isLastParameter, nameImporter) != DefaultArgumentKind::None; @@ -1139,6 +1136,181 @@ Optional NameImporter::considerErrorImport( return None; } +/// Whether the given parameter name identifies a completion handler. +static bool isCompletionHandlerParamName(StringRef paramName) { + return paramName == "completionHandler" || paramName == "completion" || + paramName == "withCompletionHandler"; +} + +/// Whether the give base name implies that the first parameter is a completion +/// handler. +/// +/// \returns a trimmed base name when it does, \c None others +static Optional isCompletionHandlerInBaseName(StringRef basename) { + if (basename.endswith("WithCompletionHandler")) { + return basename.drop_back(strlen("WithCompletionHandler")); + } + + if (basename.endswith("WithCompletion")) { + return basename.drop_back(strlen("WithCompletion")); + } + + return None; +} + + +// Determine whether the given type is a nullable NSError type. +static bool isNullableNSErrorType( + clang::ASTContext &clangCtx, clang::QualType type) { + auto objcPtrType = type->getAs(); + if (!objcPtrType) + return false; + + auto iface = objcPtrType->getInterfaceDecl(); + if (!iface || iface->getName() != "NSError") + return false; + + // If nullability is specified, check it. + if (auto nullability = type->getNullability(clangCtx)) { + switch (translateNullability(*nullability)) { + case OTK_None: + return false; + + case OTK_ImplicitlyUnwrappedOptional: + case OTK_Optional: + return true; + } + } + + // Otherwise, assume it's nullable. + return true; +} + +Optional +NameImporter::considerAsyncImport( + const clang::ObjCMethodDecl *clangDecl, + StringRef &baseName, + SmallVectorImpl ¶mNames, + ArrayRef params, + bool isInitializer, bool hasCustomName, + Optional errorInfo) { + // If there are no unclaimed parameters, there's no . + unsigned errorParamAdjust = errorInfo ? 1 : 0; + if (params.size() - errorParamAdjust == 0) + return None; + + // If the # of parameter names doesn't line up with the # of parameters, + // bail out. There are extra C parameters on the method or a custom name + // was incorrect. + if (params.size() != paramNames.size() + errorParamAdjust) + return None; + + // The last parameter will be the completion handler for an async function. + unsigned completionHandlerParamIndex = params.size() - 1; + unsigned completionHandlerParamNameIndex = paramNames.size() - 1; + + // Determine whether the naming indicates that this is a completion + // handler. + Optional newBaseName; + if (isCompletionHandlerParamName(paramNames[completionHandlerParamNameIndex])) { + // The argument label itself has an appropriate name. + } else if (!hasCustomName && completionHandlerParamIndex == 0 && + (newBaseName = isCompletionHandlerInBaseName(baseName))) { + // The base name implies that the first parameter is a completion handler. + } else if (isCompletionHandlerParamName( + params[completionHandlerParamIndex]->getName())) { + // The parameter has an appropriate name. + } else { + return None; + } + + // Used for returns once we've determined that the method cannot be + // imported as async, even though it has what looks like a completion handler + // parameter. + auto notAsync = [&](const char *reason) -> Optional { +#ifdef ASYNC_IMPORT_DEBUG + llvm::errs() << "*** failed async import: " << reason << "\n"; + clangDecl->dump(llvm::errs()); +#endif + + return None; + }; + + // Initializers cannot be 'async'. + // FIXME: We might eventually allow this. + if (isInitializer) + return notAsync("initializers cannot be async"); + + // Accessors are never imported as async. + if (clangDecl->isPropertyAccessor()) + return notAsync("method is a property accessor"); + + // Check whether we method has a suitable return type. + if (clangDecl->getReturnType()->isVoidType()) { + // 'void' is the common case; the method produces no synchronous result. + } else if (errorInfo && + ForeignErrorConvention::resultTypeErasedToVoid( + errorInfo->getKind())) { + // The method has been imported as throwing in a manner that erased the + // result type to Void. + } else { + return notAsync("method does not return void"); + } + + // The completion handler parameter must have block type. + auto completionHandlerParam = params[completionHandlerParamIndex]; + if (!isBlockParameter(completionHandlerParam)) + return notAsync("parameter is not a block"); + + // Dig out the function type of the completion handler's block type. + // If there is no prototype, (e.g., the completion handler is of type + // void (^)()), we cannot importer it. + auto completionHandlerFunctionType = + completionHandlerParam->getType()->castAs() + ->getPointeeType()->getAs(); + if (!completionHandlerFunctionType) + return notAsync("block parameter does not have a prototype"); + + // The completion handler parameter must itself return 'void'. + if (!completionHandlerFunctionType->getReturnType()->isVoidType()) + return notAsync("completion handler parameter does not return 'void'"); + + // Scan the parameters of the block type to look for a parameter of a + // nullable NSError type, which would indicate that the async method could + // throw. + Optional completionHandlerErrorParamIndex; + auto completionHandlerParamTypes = + completionHandlerFunctionType->getParamTypes(); + auto &clangCtx = clangDecl->getASTContext(); + for (unsigned paramIdx : indices(completionHandlerParamTypes)) { + auto paramType = completionHandlerParamTypes[paramIdx]; + + // We are only interested in nullable NSError parameters. + if (!isNullableNSErrorType(clangCtx, paramType)) + continue; + + // If this is the first nullable error parameter, note that. + if (!completionHandlerErrorParamIndex) { + completionHandlerErrorParamIndex = paramIdx; + continue; + } + + // More than one nullable NSError parameter. Don't import as throwing. + completionHandlerErrorParamIndex = None; + break; + } + + // Drop the completion handler parameter name. + paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex); + + // Update the base name, if needed. + if (newBaseName && !hasCustomName) + baseName = *newBaseName; + + return ForeignAsyncConvention( + completionHandlerParamIndex, completionHandlerErrorParamIndex); +} + bool NameImporter::hasErrorMethodNameCollision( const clang::ObjCMethodDecl *method, unsigned paramIndex, StringRef suffixToStrip) { @@ -1350,7 +1522,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, else if (parsedName.IsSetter) result.info.accessorKind = ImportedAccessorKind::PropertySetter; - if (method && parsedName.IsFunctionName) { + if (method && parsedName.IsFunctionName && + result.info.accessorKind == ImportedAccessorKind::None) { // Get the parameters. ArrayRef params{method->param_begin(), method->param_end()}; @@ -1362,6 +1535,21 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.hasErrorInfo = true; result.info.errorInfo = *errorInfo; } + + if (version.supportsConcurrency()) { + if (auto asyncInfo = considerAsyncImport( + method, parsedName.BaseName, parsedName.ArgumentLabels, + params, isInitializer, /*hasCustomName=*/true, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + + // Update the name to reflect the new parameter labels. + result.declName = formDeclName( + swiftCtx, parsedName.BaseName, parsedName.ArgumentLabels, + /*isFunction=*/true, isInitializer); + } + } } return result; @@ -1624,6 +1812,17 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, result.info.accessorKind = ImportedAccessorKind::SubscriptSetter; } + if (version.supportsConcurrency() && + result.info.accessorKind == ImportedAccessorKind::None) { + if (auto asyncInfo = considerAsyncImport( + objcMethod, baseName, argumentNames, params, isInitializer, + /*hasCustomName=*/false, + result.getErrorInfo())) { + result.info.hasAsyncInfo = true; + result.info.asyncInfo = *asyncInfo; + } + } + break; } } @@ -1895,6 +2094,7 @@ bool NameImporter::forEachDistinctImportName( seenNames.push_back(key); activeVersion.forEachOtherImportNameVersion( + swiftCtx.LangOpts.EnableExperimentalConcurrency, [&](ImportNameVersion nameVersion) { // Check to see if the name is different. ImportedName newName = importName(decl, nameVersion); diff --git a/lib/ClangImporter/ImportName.h b/lib/ClangImporter/ImportName.h index ddb4b0aa4105c..09d90703563c9 100644 --- a/lib/ClangImporter/ImportName.h +++ b/lib/ClangImporter/ImportName.h @@ -23,6 +23,7 @@ #include "swift/Basic/Version.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "clang/Sema/Sema.h" @@ -42,15 +43,18 @@ enum { NumImportedAccessorKindBits = 3 }; /// The name version class ImportNameVersion : public RelationalOperationsBase { - unsigned rawValue; + unsigned rawValue : 31; + unsigned concurrency : 1; + friend llvm::DenseMapInfo; enum AsConstExpr_t { AsConstExpr }; - constexpr ImportNameVersion() : rawValue(0) {} + constexpr ImportNameVersion() : rawValue(0), concurrency(false) {} constexpr ImportNameVersion(unsigned version, AsConstExpr_t) - : rawValue(version) {} - explicit ImportNameVersion(unsigned version) : rawValue(version) { + : rawValue(version), concurrency(false) {} + explicit ImportNameVersion(unsigned version, bool concurrency = false) + : rawValue(version), concurrency(concurrency) { assert(version >= 2 && "only Swift 2 and later are supported"); } public: @@ -67,7 +71,7 @@ class ImportNameVersion : public RelationalOperationsBase { return ImportNameVersion::swift4_2(); } unsigned major = version[0]; - return ImportNameVersion(major >= 5 ? major + 1 : major); + return ImportNameVersion(major >= 5 ? major + 1 : major, false); } unsigned majorVersionNumber() const { @@ -89,11 +93,21 @@ class ImportNameVersion : public RelationalOperationsBase { return llvm::VersionTuple(majorVersionNumber(), minorVersionNumber()); } + /// Whether to consider importing functions as 'async'. + bool supportsConcurrency() const { return concurrency; } + + ImportNameVersion withConcurrency(bool concurrency) const { + ImportNameVersion result = *this; + result.concurrency = concurrency; + return result; + } + bool operator==(ImportNameVersion other) const { - return rawValue == other.rawValue; + return rawValue == other.rawValue && concurrency == other.concurrency; } bool operator<(ImportNameVersion other) const { - return rawValue < other.rawValue; + return rawValue < other.rawValue || + (rawValue == other.rawValue && concurrency < other.concurrency); } /// Calls \p action for each name version other than this one, first going @@ -102,10 +116,19 @@ class ImportNameVersion : public RelationalOperationsBase { /// /// This is the most useful order for importing compatibility stubs. void forEachOtherImportNameVersion( + bool withConcurrency, llvm::function_ref action) const { assert(*this >= ImportNameVersion::swift2()); ImportNameVersion nameVersion = *this; + assert(!nameVersion.supportsConcurrency()); + + // If we've been asked to also consider concurrency, do so for the + // primary version (only). + if (withConcurrency) { + action(nameVersion.withConcurrency(true)); + } + while (nameVersion > ImportNameVersion::swift2()) { --nameVersion.rawValue; action(nameVersion); @@ -175,6 +198,10 @@ class ImportedName { /// throwing Swift methods, describes how the mapping is performed. ForeignErrorConvention::Info errorInfo; + /// For names that map Objective-C completion handlers into async + /// Swift methods, describes how the mapping is performed. + ForeignAsyncConvention asyncInfo; + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. unsigned selfIndex; @@ -201,11 +228,13 @@ class ImportedName { unsigned hasErrorInfo : 1; + unsigned hasAsyncInfo : 1; + Info() : errorInfo(), selfIndex(), initKind(CtorInitializerKind::Designated), accessorKind(ImportedAccessorKind::None), hasCustomName(false), droppedVariadic(false), importAsMember(false), hasSelfIndex(false), - hasErrorInfo(false) {} + hasErrorInfo(false), hasAsyncInfo(false) {} } info; public: @@ -239,6 +268,14 @@ class ImportedName { return None; } + /// For names that map Objective-C methods with completion handlers into + /// async Swift methods, describes how the mapping is performed. + Optional getAsyncInfo() const { + if (info.hasAsyncInfo) + return info.asyncInfo; + return None; + } + /// For a declaration name that makes the declaration into an /// instance member, the index of the "Self" parameter. Optional getSelfIndex() const { @@ -416,6 +453,14 @@ class NameImporter { ArrayRef params, bool isInitializer, bool hasCustomName); + Optional + considerAsyncImport(const clang::ObjCMethodDecl *clangDecl, + StringRef &baseName, + SmallVectorImpl ¶mNames, + ArrayRef params, + bool isInitializer, bool hasCustomName, + Optional errorInfo); + EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *, const clang::DeclContext *, ImportNameVersion version); diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 113ad9cdb7ee5..b386d03579cef 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -1753,8 +1753,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( // Check nullability of the parameter. OptionalTypeKind OptionalityOfParam = - getParamOptionality(SwiftContext.LangOpts.EffectiveLanguageVersion, - param, !nonNullArgs.empty() && nonNullArgs[index]); + getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[index]); ImportTypeKind importKind = ImportTypeKind::Parameter; if (param->hasAttr()) @@ -1995,6 +1994,42 @@ static Type mapGenericArgs(const DeclContext *fromDC, return type.subst(subs); } +/// Decompose the type of a completion handler parameter in a function +/// imported as 'async' and produce the result type of the 'async' function. +static Type decomposeCompletionHandlerType( + Type paramTy, ForeignAsyncConvention info) { + auto fnType = paramTy->lookThroughAllOptionalTypes()->getAs(); + if (!fnType) + return Type(); + + SmallVector resultTypeElts; + auto params = fnType->getParams(); + for (unsigned paramIdx : indices(params)) { + const auto ¶m = params[paramIdx]; + if (param.isInOut() || param.isVariadic()) + return Type(); + + // If there is an error parameter to the completion handler, it is + // not part of the result type of the asynchronous function. + if (info.completionHandlerErrorParamIndex() && + paramIdx == *info.completionHandlerErrorParamIndex()) + continue; + + resultTypeElts.push_back(param.getPlainType()); + } + + switch (resultTypeElts.size()) { + case 0: + return paramTy->getASTContext().getVoidDecl()->getDeclaredInterfaceType(); + + case 1: + return resultTypeElts.front().getType(); + + default: + return TupleType::get(resultTypeElts, paramTy->getASTContext()); + } +} + ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( const DeclContext *dc, const clang::ObjCMethodDecl *clangDecl, ArrayRef params, bool isVariadic, @@ -2031,6 +2066,7 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( CanType origSwiftResultTy; Optional errorInfo = importedName.getErrorInfo(); + auto asyncInfo = importedName.getAsyncInfo(); OptionalTypeKind OptionalityOfReturn; if (clangDecl->hasAttr()) { OptionalityOfReturn = OTK_None; @@ -2129,12 +2165,14 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( continue; } + bool paramIsCompletionHandler = + asyncInfo && paramIndex == asyncInfo->completionHandlerParamIndex(); + // Import the parameter type into Swift. // Check nullability of the parameter. OptionalTypeKind optionalityOfParam - = getParamOptionality(SwiftContext.LangOpts.EffectiveLanguageVersion, - param, + = getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[paramIndex]); bool allowNSUIntegerAsIntInParam = isFromSystemModule; @@ -2199,6 +2237,21 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( continue; } + // If this is a completion handler, figure out it's effect on the result + // type but don't build it into the parameter type. + if (paramIsCompletionHandler) { + if (Type replacedSwiftResultTy = + decomposeCompletionHandlerType(swiftParamTy, *asyncInfo)) { + swiftResultTy = replacedSwiftResultTy; + + // FIXME: We will need an equivalent to "error parameter is replaced" + // for asynchronous functions. Perhaps add "async: ()"? + continue; + } + + llvm_unreachable("async info computed incorrectly?"); + } + // Map __attribute__((noescape)) to @noescape. bool addNoEscapeAttr = false; if (param->hasAttr()) { diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index 3ac0d0fe45d0c..6653b2d0ee38d 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -1857,6 +1857,7 @@ SwiftNameLookupExtension::hashExtension(llvm::hash_code code) const { SWIFT_LOOKUP_TABLE_VERSION_MAJOR, SWIFT_LOOKUP_TABLE_VERSION_MINOR, inferImportAsMember, + swiftCtx.LangOpts.EnableExperimentalConcurrency, version::getSwiftFullVersion()); } diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 4e12495bb35df..c876a44f87383 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -406,8 +406,7 @@ bool ArgsToFrontendOptionsConverter::setUpInputKindAndImmediateArgs() { if (Opts.InputsAndOutputs.verifyInputs( Diags, treatAsSIL, Opts.RequestedAction == FrontendOptions::ActionType::REPL, - (Opts.RequestedAction == FrontendOptions::ActionType::NoneAction || - Opts.RequestedAction == FrontendOptions::ActionType::PrintVersion))){ + !FrontendOptions::doesActionRequireInputs(Opts.RequestedAction))) { return true; } if (Opts.RequestedAction == FrontendOptions::ActionType::Immediate) { diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 0d6263b2e544c..688acbf0b29dd 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -848,7 +848,7 @@ void CompilerInstance::setMainModule(ModuleDecl *newMod) { Context->addLoadedModule(newMod); } -void CompilerInstance::performParseAndResolveImportsOnly() { +bool CompilerInstance::performParseAndResolveImportsOnly() { FrontendStatsTracer tracer(getStatsReporter(), "parse-and-resolve-imports"); // Resolve imports for all the source files. @@ -867,6 +867,7 @@ void CompilerInstance::performParseAndResolveImportsOnly() { mainModule->setHasResolvedImports(); bindExtensions(*mainModule); + return Context->hadError(); } void CompilerInstance::performSema() { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 83718a733666e..16fe1877a6d25 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -73,20 +73,104 @@ bool FrontendOptions::needsProperModuleName(ActionType action) { bool FrontendOptions::shouldActionOnlyParse(ActionType action) { switch (action) { - case FrontendOptions::ActionType::Parse: - case FrontendOptions::ActionType::DumpParse: - case FrontendOptions::ActionType::EmitSyntax: - case FrontendOptions::ActionType::DumpInterfaceHash: - case FrontendOptions::ActionType::EmitImportedModules: - case FrontendOptions::ActionType::ScanDependencies: - case FrontendOptions::ActionType::ScanClangDependencies: - case FrontendOptions::ActionType::PrintVersion: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::PrintVersion: return true; default: return false; } } +bool FrontendOptions::doesActionRequireSwiftStandardLibrary(ActionType action) { + switch (action) { + case ActionType::NoneAction: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::PrintVersion: + case ActionType::EmitPCH: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::CompileModuleFromInterface: + case ActionType::TypecheckModuleFromInterface: + return false; + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpAST: + case ActionType::PrintAST: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::EmitSILGen: + case ActionType::EmitSIL: + case ActionType::EmitModuleOnly: + case ActionType::MergeModules: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::Immediate: + case ActionType::REPL: + case ActionType::EmitAssembly: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitObject: + case ActionType::DumpTypeInfo: + assert(!FrontendOptions::shouldActionOnlyParse(action) && + "Parse-only actions should not load modules!"); + return true; + } + llvm_unreachable("Unknown ActionType"); +} + +bool FrontendOptions::doesActionRequireInputs(ActionType action) { + switch (action) { + case ActionType::NoneAction: + case ActionType::PrintVersion: + return false; + case ActionType::REPL: + case ActionType::Parse: + case ActionType::DumpParse: + case ActionType::EmitSyntax: + case ActionType::DumpInterfaceHash: + case ActionType::EmitImportedModules: + case ActionType::ScanDependencies: + case ActionType::ScanClangDependencies: + case ActionType::EmitPCH: + case ActionType::EmitPCM: + case ActionType::DumpPCM: + case ActionType::CompileModuleFromInterface: + case ActionType::TypecheckModuleFromInterface: + case ActionType::ResolveImports: + case ActionType::Typecheck: + case ActionType::DumpAST: + case ActionType::PrintAST: + case ActionType::DumpScopeMaps: + case ActionType::DumpTypeRefinementContexts: + case ActionType::EmitSILGen: + case ActionType::EmitSIL: + case ActionType::EmitModuleOnly: + case ActionType::MergeModules: + case ActionType::EmitSIBGen: + case ActionType::EmitSIB: + case ActionType::Immediate: + case ActionType::EmitAssembly: + case ActionType::EmitIR: + case ActionType::EmitBC: + case ActionType::EmitObject: + case ActionType::DumpTypeInfo: + return true; + } + llvm_unreachable("Unknown ActionType"); +} + void FrontendOptions::forAllOutputPaths( const InputFile &input, llvm::function_ref fn) const { if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly && diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index c567bc8acc68b..5afe379da5d1e 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -1230,7 +1230,7 @@ static void verifyGenericSignaturesIfNeeded(const CompilerInvocation &Invocation GenericSignatureBuilder::verifyGenericSignaturesInModule(module); } -static void dumpAndPrintScopeMap(const CompilerInstance &Instance, +static bool dumpAndPrintScopeMap(const CompilerInstance &Instance, SourceFile *SF) { // Not const because may require reexpansion ASTScope &scope = SF->getScope(); @@ -1240,13 +1240,14 @@ static void dumpAndPrintScopeMap(const CompilerInstance &Instance, llvm::errs() << "***Complete scope map***\n"; scope.buildFullyExpandedTree(); scope.print(llvm::errs()); - return; + return Instance.getASTContext().hadError(); } // Probe each of the locations, and dump what we find. for (auto lineColumn : opts.DumpScopeMapLocations) { scope.buildFullyExpandedTree(); scope.dumpOneScopeMapLocation(lineColumn); } + return Instance.getASTContext().hadError(); } static SourceFile * @@ -1261,7 +1262,7 @@ getPrimaryOrMainSourceFile(const CompilerInstance &Instance) { /// Dumps the AST of all available primary source files. If corresponding output /// files were specified, use them; otherwise, dump the AST to stdout. -static void dumpAST(CompilerInstance &Instance) { +static bool dumpAST(CompilerInstance &Instance) { auto primaryFiles = Instance.getPrimarySourceFiles(); if (!primaryFiles.empty()) { for (SourceFile *sourceFile: primaryFiles) { @@ -1276,55 +1277,7 @@ static void dumpAST(CompilerInstance &Instance) { auto *SF = getPrimaryOrMainSourceFile(Instance); SF->dump(llvm::outs(), /*parseIfNeeded*/ true); } -} - -/// We may have been told to dump the AST (either after parsing or -/// type-checking, which is already differentiated in -/// CompilerInstance::performSema()), so dump or print the main source file and -/// return. - -static Optional dumpASTIfNeeded(CompilerInstance &Instance) { - const auto &opts = Instance.getInvocation().getFrontendOptions(); - const FrontendOptions::ActionType Action = opts.RequestedAction; - ASTContext &Context = Instance.getASTContext(); - switch (Action) { - default: - return None; - - case FrontendOptions::ActionType::PrintAST: - getPrimaryOrMainSourceFile(Instance) - ->print(llvm::outs(), PrintOptions::printEverything()); - break; - - case FrontendOptions::ActionType::DumpScopeMaps: - dumpAndPrintScopeMap(Instance, getPrimaryOrMainSourceFile(Instance)); - break; - - case FrontendOptions::ActionType::DumpTypeRefinementContexts: - getPrimaryOrMainSourceFile(Instance) - ->getTypeRefinementContext() - ->dump(llvm::errs(), Context.SourceMgr); - break; - - case FrontendOptions::ActionType::DumpInterfaceHash: - getPrimaryOrMainSourceFile(Instance)->dumpInterfaceHash(llvm::errs()); - break; - - case FrontendOptions::ActionType::EmitSyntax: - emitSyntax(getPrimaryOrMainSourceFile(Instance), - opts.InputsAndOutputs.getSingleOutputFilename()); - break; - - case FrontendOptions::ActionType::DumpParse: - case FrontendOptions::ActionType::DumpAST: - dumpAST(Instance); - break; - - case FrontendOptions::ActionType::EmitImportedModules: - emitImportedModules(Context, Instance.getMainModule(), opts); - break; - } - return Context.hadError(); + return Instance.getASTContext().hadError(); } static void emitReferenceDependenciesForAllPrimaryInputsIfNeeded( @@ -1701,6 +1654,7 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { // it's -emit-imported-modules, which can load modules. auto action = opts.RequestedAction; if (FrontendOptions::shouldActionOnlyParse(action) && + !ctx.getLoadedModules().empty() && action != FrontendOptions::ActionType::EmitImportedModules) { assert(ctx.getNumLoadedModules() == 1 && "Loaded a module during parse-only"); @@ -1737,9 +1691,15 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { } } - // Emit dependencies and index data. + // FIXME: This predicate matches the status quo, but there's no reason + // indexing cannot run for actions that do not require stdlib e.g. to better + // facilitate tests. + if (FrontendOptions::doesActionRequireSwiftStandardLibrary(action)) { + emitIndexData(Instance); + } + + // Emit dependencies. emitReferenceDependenciesForAllPrimaryInputsIfNeeded(Instance); - emitIndexData(Instance); emitMakeDependenciesIfNeeded(Instance.getDiags(), Instance.getDependencyTracker(), opts); @@ -1748,39 +1708,190 @@ static void performEndOfPipelineActions(CompilerInstance &Instance) { emitCompiledSourceForAllPrimaryInputsIfNeeded(Instance); } -/// Performs the compile requested by the user. -/// \param Instance Will be reset after performIRGeneration when the verifier -/// mode is NoVerify and there were no errors. -/// \returns true on error -static bool performCompile(CompilerInvocation &Invok, - CompilerInstance &Instance, - ArrayRef Args, - int &ReturnValue, - FrontendObserver *observer) { +static bool printSwiftVersion(const CompilerInvocation &Invocation) { + llvm::outs() << version::getSwiftFullVersion( + version::Version::getCurrentLanguageVersion()) + << '\n'; + llvm::outs() << "Target: " << Invocation.getLangOptions().Target.str() + << '\n'; + return false; +} + +static bool +withSemanticAnalysis(CompilerInstance &Instance, FrontendObserver *observer, + llvm::function_ref cont) { const auto &Invocation = Instance.getInvocation(); const auto &opts = Invocation.getFrontendOptions(); - const FrontendOptions::ActionType Action = opts.RequestedAction; + assert(!FrontendOptions::shouldActionOnlyParse(opts.RequestedAction) && + "Action may only parse, but has requested semantic analysis!"); + + Instance.performSema(); + if (observer) + observer->performedSemanticAnalysis(Instance); + + switch (opts.CrashMode) { + case FrontendOptions::DebugCrashMode::AssertAfterParse: + debugFailWithAssertion(); + return true; + case FrontendOptions::DebugCrashMode::CrashAfterParse: + debugFailWithCrash(); + return true; + case FrontendOptions::DebugCrashMode::None: + break; + } + (void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance); + + if (Instance.getASTContext().hadError()) + return true; + + return cont(Instance); +} + +static bool performScanDependencies(CompilerInstance &Instance) { + auto batchScanInput = + Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; + if (batchScanInput.empty()) { + return scanDependencies(Instance); + } else { + return batchScanModuleDependencies(Instance, batchScanInput); + } +} + +static bool performParseOnly(ModuleDecl &MainModule) { + // A -parse invocation only cares about the side effects of parsing, so + // force the parsing of all the source files. + for (auto *file : MainModule.getFiles()) { + if (auto *SF = dyn_cast(file)) + (void)SF->getTopLevelDecls(); + } + return MainModule.getASTContext().hadError(); +} + +static bool performAction(CompilerInstance &Instance, + int &ReturnValue, + FrontendObserver *observer) { + const auto &opts = Instance.getInvocation().getFrontendOptions(); + auto &Context = Instance.getASTContext(); + switch (Instance.getInvocation().getFrontendOptions().RequestedAction) { + // MARK: Trivial Actions + case FrontendOptions::ActionType::NoneAction: + return Context.hadError(); + case FrontendOptions::ActionType::PrintVersion: + return printSwiftVersion(Instance.getInvocation()); + case FrontendOptions::ActionType::REPL: + llvm::report_fatal_error("Compiler-internal integrated REPL has been " + "removed; use the LLDB-enhanced REPL instead."); + + // MARK: Actions for Clang and Clang Modules // We've been asked to precompile a bridging header or module; we want to // avoid touching any other inputs and just parse, emit and exit. - if (Action == FrontendOptions::ActionType::EmitPCH) + case FrontendOptions::ActionType::EmitPCH: return precompileBridgingHeader(Instance); - if (Action == FrontendOptions::ActionType::EmitPCM) + case FrontendOptions::ActionType::EmitPCM: return precompileClangModule(Instance); - if (Action == FrontendOptions::ActionType::DumpPCM) + case FrontendOptions::ActionType::DumpPCM: return dumpPrecompiledClangModule(Instance); - if (Action == FrontendOptions::ActionType::PrintVersion) { - llvm::outs() << version::getSwiftFullVersion( - version::Version::getCurrentLanguageVersion()) << '\n'; - llvm::outs() << "Target: " - << Invocation.getLangOptions().Target.str() << '\n'; - return false; - } - if (Action == FrontendOptions::ActionType::CompileModuleFromInterface || - Action == FrontendOptions::ActionType::TypecheckModuleFromInterface) + + // MARK: Module Interface Actions + case FrontendOptions::ActionType::CompileModuleFromInterface: + case FrontendOptions::ActionType::TypecheckModuleFromInterface: return buildModuleFromInterface(Instance); - if (Invocation.getInputKind() == InputFileKind::LLVM) + // MARK: Actions that Dump + case FrontendOptions::ActionType::DumpParse: + return dumpAST(Instance); + case FrontendOptions::ActionType::DumpAST: { + // FIXME: -dump-ast expects to be able to write output even if type checking + // fails which does not cleanly fit the model \c withSemanticAnalysis is + // trying to impose. Once there is a request for the "semantic AST", this + // point is moot. + Instance.performSema(); + return dumpAST(Instance); + } + case FrontendOptions::ActionType::PrintAST: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance)->print( + llvm::outs(), PrintOptions::printEverything()); + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::DumpScopeMaps: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + return dumpAndPrintScopeMap(Instance, + getPrimaryOrMainSourceFile(Instance)); + }); + case FrontendOptions::ActionType::DumpTypeRefinementContexts: + return withSemanticAnalysis( + Instance, observer, [](CompilerInstance &Instance) { + getPrimaryOrMainSourceFile(Instance) + ->getTypeRefinementContext() + ->dump(llvm::errs(), Instance.getASTContext().SourceMgr); + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::DumpInterfaceHash: + getPrimaryOrMainSourceFile(Instance)->dumpInterfaceHash(llvm::errs()); + return Context.hadError(); + case FrontendOptions::ActionType::EmitSyntax: + return emitSyntax(getPrimaryOrMainSourceFile(Instance), + opts.InputsAndOutputs.getSingleOutputFilename()); + case FrontendOptions::ActionType::EmitImportedModules: + return emitImportedModules(Instance.getMainModule(), opts); + + // MARK: Dependency Scanning Actions + case FrontendOptions::ActionType::ScanDependencies: + return performScanDependencies(Instance); + case FrontendOptions::ActionType::ScanClangDependencies: + return scanClangDependencies(Instance); + + // MARK: General Compilation Actions + case FrontendOptions::ActionType::Parse: + return performParseOnly(*Instance.getMainModule()); + case FrontendOptions::ActionType::ResolveImports: + return Instance.performParseAndResolveImportsOnly(); + case FrontendOptions::ActionType::Typecheck: + return withSemanticAnalysis(Instance, observer, + [](CompilerInstance &Instance) { + return Instance.getASTContext().hadError(); + }); + case FrontendOptions::ActionType::EmitSILGen: + case FrontendOptions::ActionType::EmitSIBGen: + case FrontendOptions::ActionType::EmitSIL: + case FrontendOptions::ActionType::EmitSIB: + case FrontendOptions::ActionType::EmitModuleOnly: + case FrontendOptions::ActionType::MergeModules: + case FrontendOptions::ActionType::Immediate: + case FrontendOptions::ActionType::EmitAssembly: + case FrontendOptions::ActionType::EmitIR: + case FrontendOptions::ActionType::EmitBC: + case FrontendOptions::ActionType::EmitObject: + case FrontendOptions::ActionType::DumpTypeInfo: + return withSemanticAnalysis( + Instance, observer, [&](CompilerInstance &Instance) { + assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) && + "All actions not requiring SILGen must have been handled!"); + return performCompileStepsPostSema(Instance, ReturnValue, observer); + }); + } + + assert(false && "Unhandled case in performCompile!"); + return Context.hadError(); +} + +/// Performs the compile requested by the user. +/// \param Instance Will be reset after performIRGeneration when the verifier +/// mode is NoVerify and there were no errors. +/// \returns true on error +static bool performCompile(CompilerInstance &Instance, + int &ReturnValue, + FrontendObserver *observer) { + const auto &Invocation = Instance.getInvocation(); + const auto &opts = Invocation.getFrontendOptions(); + const FrontendOptions::ActionType Action = opts.RequestedAction; + + // To compile LLVM IR, just pass it off unmodified. + if (Instance.getInvocation().getInputKind() == InputFileKind::LLVM) return compileLLVMIR(Instance); // If we aren't in a parse-only context and expect an implicit stdlib import, @@ -1789,100 +1900,33 @@ static bool performCompile(CompilerInvocation &Invok, // trigger a bunch of other errors due to the stdlib being missing, or at // worst crash downstream as many call sites don't currently handle a missing // stdlib. - if (!FrontendOptions::shouldActionOnlyParse(Action)) { + if (FrontendOptions::doesActionRequireSwiftStandardLibrary(Action)) { if (Instance.loadStdlibIfNeeded()) return true; } - bool didFinishPipeline = false; - SWIFT_DEFER { - assert(didFinishPipeline && "Returned without calling finishPipeline"); - }; - - auto finishPipeline = [&](bool hadError) -> bool { - // We might have freed the ASTContext already, but in that case we would - // have already performed these actions. - if (Instance.hasASTContext()) { - performEndOfPipelineActions(Instance); - hadError |= Instance.getASTContext().hadError(); + assert([&]() -> bool { + if (FrontendOptions::shouldActionOnlyParse(Action)) { + // Parsing gets triggered lazily, but let's make sure we have the right + // input kind. + auto kind = Invocation.getInputKind(); + return kind == InputFileKind::Swift || + kind == InputFileKind::SwiftLibrary || + kind == InputFileKind::SwiftModuleInterface; } - didFinishPipeline = true; - return hadError; - }; - - auto &Context = Instance.getASTContext(); - if (FrontendOptions::shouldActionOnlyParse(Action)) { - // Parsing gets triggered lazily, but let's make sure we have the right - // input kind. - auto kind = Invocation.getInputKind(); - assert((kind == InputFileKind::Swift || - kind == InputFileKind::SwiftLibrary || - kind == InputFileKind::SwiftModuleInterface) && - "Only supports parsing .swift files"); - (void)kind; - } else if (Action == FrontendOptions::ActionType::ResolveImports) { - Instance.performParseAndResolveImportsOnly(); - return finishPipeline(Context.hadError()); - } else { - Instance.performSema(); - } - - if (Action == FrontendOptions::ActionType::Parse) { - // A -parse invocation only cares about the side effects of parsing, so - // force the parsing of all the source files. - for (auto *file : Instance.getMainModule()->getFiles()) { - if (auto *SF = dyn_cast(file)) - (void)SF->getTopLevelDecls(); - } - return finishPipeline(Context.hadError()); - } - - if (Action == FrontendOptions::ActionType::ScanDependencies) { - auto batchScanInput = Instance.getASTContext().SearchPathOpts.BatchScanInputFilePath; - if (batchScanInput.empty()) - return finishPipeline(scanDependencies(Instance)); - else - return finishPipeline(batchScanModuleDependencies(Invok, - Instance, - batchScanInput)); - } - - if (Action == FrontendOptions::ActionType::ScanClangDependencies) - return finishPipeline(scanClangDependencies(Instance)); - - if (observer) - observer->performedSemanticAnalysis(Instance); - - { - FrontendOptions::DebugCrashMode CrashMode = opts.CrashMode; - if (CrashMode == FrontendOptions::DebugCrashMode::AssertAfterParse) - debugFailWithAssertion(); - else if (CrashMode == FrontendOptions::DebugCrashMode::CrashAfterParse) - debugFailWithCrash(); - } + return true; + }() && "Only supports parsing .swift files"); - (void)migrator::updateCodeAndEmitRemapIfNeeded(&Instance); + bool hadError = performAction(Instance, ReturnValue, observer); - if (Action == FrontendOptions::ActionType::REPL) { - llvm::report_fatal_error("Compiler-internal integrated REPL has been " - "removed; use the LLDB-enhanced REPL instead."); + // We might have freed the ASTContext already, but in that case we would + // have already performed these actions. + if (Instance.hasASTContext() && + FrontendOptions::doesActionRequireInputs(Action)) { + performEndOfPipelineActions(Instance); + hadError |= Instance.getASTContext().hadError(); } - - if (auto r = dumpASTIfNeeded(Instance)) - return finishPipeline(*r); - - if (Context.hadError()) - return finishPipeline(/*hadError*/ true); - - // We've just been told to perform a typecheck, so we can return now. - if (Action == FrontendOptions::ActionType::Typecheck) - return finishPipeline(/*hadError*/ false); - - assert(FrontendOptions::doesActionGenerateSIL(Action) && - "All actions not requiring SILGen must have been handled!"); - - return finishPipeline( - performCompileStepsPostSema(Instance, ReturnValue, observer)); + return hadError; } static bool serializeSIB(SILModule *SM, const PrimarySpecificPaths &PSPs, @@ -2656,7 +2700,7 @@ int swift::performFrontend(ArrayRef Args, } int ReturnValue = 0; - bool HadError = performCompile(Invocation, *Instance, Args, ReturnValue, observer); + bool HadError = performCompile(*Instance, ReturnValue, observer); if (verifierEnabled) { DiagnosticEngine &diags = Instance->getDiags(); diff --git a/lib/FrontendTool/ImportedModules.cpp b/lib/FrontendTool/ImportedModules.cpp index 90a33895ee52a..d750417737264 100644 --- a/lib/FrontendTool/ImportedModules.cpp +++ b/lib/FrontendTool/ImportedModules.cpp @@ -42,9 +42,9 @@ static void findAllClangImports(const clang::Module *module, } } -bool swift::emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, +bool swift::emitImportedModules(ModuleDecl *mainModule, const FrontendOptions &opts) { - + auto &Context = mainModule->getASTContext(); std::string path = opts.InputsAndOutputs.getSingleOutputFilename(); std::error_code EC; llvm::raw_fd_ostream out(path, EC, llvm::sys::fs::F_None); diff --git a/lib/FrontendTool/ImportedModules.h b/lib/FrontendTool/ImportedModules.h index 42b4e76babaf4..510fa4ccdedde 100644 --- a/lib/FrontendTool/ImportedModules.h +++ b/lib/FrontendTool/ImportedModules.h @@ -20,8 +20,7 @@ class FrontendOptions; class ModuleDecl; /// Emit the names of the modules imported by \c mainModule. -bool emitImportedModules(ASTContext &Context, ModuleDecl *mainModule, - const FrontendOptions &opts); +bool emitImportedModules(ModuleDecl *mainModule, const FrontendOptions &opts); } // end namespace swift #endif diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index 50946720e8b8d..8d92a42028b14 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -690,9 +690,10 @@ bool swift::scanClangDependencies(CompilerInstance &instance) { .InputsAndOutputs.getSingleOutputFilename()); } -bool swift::batchScanModuleDependencies(CompilerInvocation &invok, - CompilerInstance &instance, +bool swift::batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile) { + const CompilerInvocation &invok = instance.getInvocation(); + (void)instance.getMainModule(); llvm::BumpPtrAllocator alloc; llvm::StringSaver saver(alloc); diff --git a/lib/FrontendTool/ScanDependencies.h b/lib/FrontendTool/ScanDependencies.h index 4604496ba66dc..fab7d26bbb190 100644 --- a/lib/FrontendTool/ScanDependencies.h +++ b/lib/FrontendTool/ScanDependencies.h @@ -21,8 +21,7 @@ class CompilerInvocation; class CompilerInstance; /// Batch scan the dependencies for modules specified in \c batchInputFile. -bool batchScanModuleDependencies(CompilerInvocation &invok, - CompilerInstance &instance, +bool batchScanModuleDependencies(CompilerInstance &instance, llvm::StringRef batchInputFile); /// Scans the dependencies of the main module of \c instance. diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index b1f315fce3ff2..904f42a949f24 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -101,6 +101,11 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) { typeCheckPatternBinding(PBD, i); } } + } else if (auto *defaultArg = dyn_cast(DC)) { + if (auto *AFD = dyn_cast(defaultArg->getParent())) { + auto *Param = AFD->getParameters()->get(defaultArg->getIndex()); + (void)Param->getTypeCheckedDefaultExpr(); + } } break; diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index bc788fab6904a..8bc35dd99c134 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -132,10 +132,10 @@ namespace { /// ABI. class GenClangType : public CanTypeVisitor { IRGenModule &IGM; - ClangTypeConverter &Converter; + irgen::ClangTypeConverter &Converter; public: - GenClangType(IRGenModule &IGM, ClangTypeConverter &converter) + GenClangType(IRGenModule &IGM, irgen::ClangTypeConverter &converter) : IGM(IGM), Converter(converter) {} const clang::ASTContext &getClangASTContext() const { @@ -264,8 +264,8 @@ static clang::CanQualType getClangBuiltinTypeFromTypedef( } clang::CanQualType -ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, - CanStructType type) { +irgen::ClangTypeConverter::reverseBuiltinTypeMapping(IRGenModule &IGM, + CanStructType type) { // Handle builtin types by adding entries to the cache that reverse // the mapping done by the importer. We could try to look at the // members of the struct instead, but even if that's ABI-equivalent @@ -748,7 +748,7 @@ clang::CanQualType GenClangType::visitType(CanType type) { llvm_unreachable("Unexpected type in Clang type generation."); } -clang::CanQualType ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { +clang::CanQualType irgen::ClangTypeConverter::convert(IRGenModule &IGM, CanType type) { // Look in the cache. auto it = Cache.find(type); if (it != Cache.end()) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index aa3cf8ce5cad1..ac374bee7e53b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -6442,10 +6442,9 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, return DCC.fixupParserResult(FD); } -/// Parse a function body for \p AFD and returns it without setting the body -/// to \p AFD . -ParserResult -Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { +/// Parse a function body for \p AFD, setting the body to \p AFD before +/// returning it. +BraceStmt *Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { assert(Tok.is(tok::l_brace)); // Enter the arguments for the function into a new function-body scope. We @@ -6473,13 +6472,70 @@ Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { CodeCompletion->completeAccessorBeginning(CCE); RBraceLoc = Tok.getLoc(); consumeToken(tok::code_complete); - return makeParserCodeCompletionResult( - BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc, - /*implicit*/ true)); + auto *BS = BraceStmt::create(Context, LBraceLoc, ASTNode(CCE), RBraceLoc, + /*implicit*/ true); + AFD->setBodyParsed(BS); + return BS; } } - return parseBraceItemList(diag::invalid_diagnostic); + ParserResult Body = parseBraceItemList(diag::invalid_diagnostic); + if (Body.isNull()) + return nullptr; + + BraceStmt *BS = Body.get(); + AFD->setBodyParsed(BS); + + // If the body consists of a single expression, turn it into a return + // statement. + // + // But don't do this transformation during code completion, as the source + // may be incomplete and the type mismatch in return statement will just + // confuse the type checker. + if (BS->getNumElements() != 1 || Body.hasCodeCompletion()) + return BS; + + auto Element = BS->getFirstElement(); + if (auto *stmt = Element.dyn_cast()) { + if (isa(AFD)) { + if (auto *returnStmt = dyn_cast(stmt)) { + if (!returnStmt->hasResult()) { + auto returnExpr = TupleExpr::createEmpty(Context, + SourceLoc(), + SourceLoc(), + /*implicit*/true); + returnStmt->setResult(returnExpr); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(returnExpr); + } + } + } + } else if (auto *E = Element.dyn_cast()) { + if (auto SE = dyn_cast(E->getSemanticsProvidingExpr())) { + if (SE->getNumElements() > 1 && isa(SE->getElement(1))) { + // This is an assignment. We don't want to implicitly return + // it. + return BS; + } + } + if (isa(AFD)) { + auto RS = new (Context) ReturnStmt(SourceLoc(), E); + BS->setFirstElement(RS); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(E); + } else if (auto *F = dyn_cast(AFD)) { + if (F->isFailable() && isa(E)) { + // If it's a nil literal, just insert return. This is the only + // legal thing to return. + auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); + BS->setFirstElement(RS); + AFD->setHasSingleExpressionBody(); + AFD->setSingleExpressionBody(E); + } + } + } + + return BS; } /// Parse function body into \p AFD or skip it for delayed parsing. @@ -6504,60 +6560,7 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { } Scope S(this, ScopeKind::FunctionBody); - - ParserResult Body = parseAbstractFunctionBodyImpl(AFD); - if (!Body.isNull()) { - BraceStmt * BS = Body.get(); - AFD->setBodyParsed(BS); - - // If the body consists of a single expression, turn it into a return - // statement. - // - // But don't do this transformation during code completion, as the source - // may be incomplete and the type mismatch in return statement will just - // confuse the type checker. - if (!Body.hasCodeCompletion() && BS->getNumElements() == 1) { - auto Element = BS->getFirstElement(); - if (auto *stmt = Element.dyn_cast()) { - if (isa(AFD)) { - if (auto *returnStmt = dyn_cast(stmt)) { - if (!returnStmt->hasResult()) { - auto returnExpr = TupleExpr::createEmpty(Context, - SourceLoc(), - SourceLoc(), - /*implicit*/true); - returnStmt->setResult(returnExpr); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(returnExpr); - } - } - } - } else if (auto *E = Element.dyn_cast()) { - if (auto SE = dyn_cast(E->getSemanticsProvidingExpr())) { - if (SE->getNumElements() > 1 && isa(SE->getElement(1))) { - // This is an assignment. We don't want to implicitly return - // it. - return; - } - } - if (isa(AFD)) { - auto RS = new (Context) ReturnStmt(SourceLoc(), E); - BS->setFirstElement(RS); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(E); - } else if (auto *F = dyn_cast(AFD)) { - if (F->isFailable() && isa(E)) { - // If it's a nil literal, just insert return. This is the only - // legal thing to return. - auto RS = new (Context) ReturnStmt(E->getStartLoc(), E); - BS->setFirstElement(RS); - AFD->setHasSingleExpressionBody(); - AFD->setSingleExpressionBody(E); - } - } - } - } - } + (void)parseAbstractFunctionBodyImpl(AFD); } BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { @@ -6589,7 +6592,7 @@ BraceStmt *Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) { Scope TopLevelScope(this, ScopeKind::TopLevel); Scope S(this, ScopeKind::FunctionBody); - return parseAbstractFunctionBodyImpl(AFD).getPtrOrNull(); + return parseAbstractFunctionBodyImpl(AFD); } /// Parse a 'enum' declaration, returning true (and doing no token diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 1a42b3832b09a..7c753b4c2f226 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -802,8 +802,6 @@ ParserResult Parser::parseExprSelector() { parseExpr(selectorKind == ObjCSelectorExpr::Method ? diag::expr_selector_expected_method_expr : diag::expr_selector_expected_property_expr); - if (subExpr.hasCodeCompletion()) - return makeParserCodeCompletionResult(); // Parse the closing ')'. SourceLoc rParenLoc; @@ -819,7 +817,7 @@ ParserResult Parser::parseExprSelector() { } // If the subexpression was in error, just propagate the error. - if (subExpr.isParseError()) + if (subExpr.isParseError() && !subExpr.hasCodeCompletion()) return makeParserResult( new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc))); diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index e26a0dd1cd238..c98ea0c6f04da 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -130,8 +130,10 @@ static ParserStatus parseDefaultArgument( defaultArgs->HasDefaultArgument = true; - if (initR.hasCodeCompletion()) + if (initR.hasCodeCompletion()) { + init = initR.get(); return makeParserCodeCompletionStatus(); + } if (initR.isNull()) return makeParserError(); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index e39f0daf1846b..c4a64ca54973e 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -898,9 +898,7 @@ ParserResult Parser::parseStmtThrow(SourceLoc tryLoc) { exprLoc = Tok.getLoc(); ParserResult Result = parseExpr(diag::expected_expr_throw); - - if (Result.hasCodeCompletion()) - return makeParserCodeCompletionResult(); + bool hasCodeCompletion = Result.hasCodeCompletion(); if (Result.isNull()) Result = makeParserErrorResult(new (Context) ErrorExpr(throwLoc)); @@ -916,6 +914,9 @@ ParserResult Parser::parseStmtThrow(SourceLoc tryLoc) { Result = makeParserResult(new (Context) TryExpr(exprLoc, Result.get())); } + if (hasCodeCompletion) + Result.setHasCodeCompletion(); + return makeParserResult(Result, new (Context) ThrowStmt(throwLoc, Result.get())); } @@ -2012,9 +2013,6 @@ ParserResult Parser::parseStmtCatch() { GuardedPattern PatternResult; parseGuardedPattern(*this, PatternResult, status, boundDecls, GuardedPatternContext::Catch, isFirst); - if (status.hasCodeCompletion()) { - return makeParserCodeCompletionResult(); - } caseLabelItems.emplace_back(PatternResult.ThePattern, PatternResult.WhereLoc, PatternResult.Guard); isFirst = false; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 7b56a1f629368..25161aabaa9ee 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -203,7 +203,7 @@ void Parser::performCodeCompletionSecondPassImpl( case CodeCompletionDelayedDeclKind::FunctionBody: { auto *AFD = cast(DC); - AFD->setBodyParsed(parseAbstractFunctionBodyImpl(AFD).getPtrOrNull()); + (void)parseAbstractFunctionBodyImpl(AFD); break; } } diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 686a8421f642d..0f937e95cec77 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -632,9 +632,10 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl { SubstitutionMap substitutions, unsigned ordinal) override { auto underlyingType = Reader - .readUnderlyingTypeForOpaqueTypeDescriptor(opaqueDescriptor.getAddressData(), - ordinal); - + .readUnderlyingTypeForOpaqueTypeDescriptor( + opaqueDescriptor.getAddressData(), ordinal) + .getType(); + if (!underlyingType) return getFailure(); diff --git a/lib/SIL/IR/SILFunctionType.cpp b/lib/SIL/IR/SILFunctionType.cpp index 2d3e10054313d..29a40a911d107 100644 --- a/lib/SIL/IR/SILFunctionType.cpp +++ b/lib/SIL/IR/SILFunctionType.cpp @@ -3984,27 +3984,17 @@ SILFunctionType::substituteOpaqueArchetypes(TypeConverter &TC, CanAnyFunctionType TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, CanAnyFunctionType t, - AnyFunctionType::ExtInfo extInfo, Bridgeability bridging) { // Pull out the generic signature. CanGenericSignature genericSig = t.getOptGenericSignature(); - switch (auto rep = t->getExtInfo().getSILRepresentation()) { - case SILFunctionTypeRepresentation::Thick: - case SILFunctionTypeRepresentation::Thin: - case SILFunctionTypeRepresentation::Method: - case SILFunctionTypeRepresentation::Closure: - case SILFunctionTypeRepresentation::WitnessMethod: { + auto rep = t->getExtInfo().getSILRepresentation(); + switch (getSILFunctionLanguage(rep)) { + case SILFunctionLanguage::Swift: { // No bridging needed for native functions. - if (t->getExtInfo().isEqualTo(extInfo, useClangTypes(t))) - return t; - return CanAnyFunctionType::get(genericSig, t.getParams(), t.getResult(), - extInfo); + return t; } - - case SILFunctionTypeRepresentation::CFunctionPointer: - case SILFunctionTypeRepresentation::Block: - case SILFunctionTypeRepresentation::ObjCMethod: { + case SILFunctionLanguage::C: { SmallVector params; getBridgedParams(rep, pattern, t->getParams(), params, bridging); @@ -4016,7 +4006,7 @@ TypeConverter::getBridgedFunctionType(AbstractionPattern pattern, suppressOptional); return CanAnyFunctionType::get(genericSig, llvm::makeArrayRef(params), - result, extInfo); + result, t->getExtInfo()); } } llvm_unreachable("bad calling convention"); @@ -4098,7 +4088,6 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, auto bridging = Bridgeability::Full; unsigned numParameterLists = constant.getParameterListCount(); - auto extInfo = fnType->getExtInfo(); // Form an abstraction pattern for bridging purposes. AbstractionPattern bridgingFnPattern = @@ -4108,12 +4097,13 @@ TypeConverter::getLoweredFormalTypes(SILDeclRef constant, // Fast path: no uncurrying required. if (numParameterLists == 1) { auto bridgedFnType = - getBridgedFunctionType(bridgingFnPattern, fnType, extInfo, bridging); + getBridgedFunctionType(bridgingFnPattern, fnType, bridging); bridgingFnPattern.rewriteType(bridgingFnPattern.getGenericSignature(), bridgedFnType); return { bridgingFnPattern, bridgedFnType }; } + auto extInfo = fnType->getExtInfo(); SILFunctionTypeRepresentation rep = extInfo.getSILRepresentation(); assert(rep != SILFunctionType::Representation::Block && "objc blocks cannot be curried"); diff --git a/lib/SIL/IR/TypeLowering.cpp b/lib/SIL/IR/TypeLowering.cpp index e1d4417e42dd6..45a725074cb8d 100644 --- a/lib/SIL/IR/TypeLowering.cpp +++ b/lib/SIL/IR/TypeLowering.cpp @@ -1964,7 +1964,7 @@ TypeConverter::computeLoweredRValueType(TypeExpansionContext forExpansion, // Bridge the parameters and result of the function type. auto bridgedFnType = - TC.getBridgedFunctionType(origType, substFnType, extInfo, bridging); + TC.getBridgedFunctionType(origType, substFnType, bridging); substFnType = bridgedFnType; // Also rewrite the type of the abstraction pattern. diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index bffecc2c2c187..22d3136ca776c 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -76,7 +76,6 @@ getIndirectApplyAbstractionPattern(SILGenFunction &SGF, // bridged to a foreign type. auto bridgedType = SGF.SGM.Types.getBridgedFunctionType(pattern, fnType, - fnType->getExtInfo(), Bridgeability::Full); pattern.rewriteType(CanGenericSignature(), bridgedType); return pattern; diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 7eedc3b1975fa..0c1bb7d417a89 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -350,8 +350,7 @@ getParameterTypes(AnyFunctionType::CanParamArrayRef params) { static CanAnyFunctionType getBridgedBlockType(SILGenModule &SGM, CanAnyFunctionType blockType) { return SGM.Types.getBridgedFunctionType(AbstractionPattern(blockType), - blockType, blockType->getExtInfo(), - Bridgeability::Full); + blockType, Bridgeability::Full); } static void buildFuncToBlockInvokeBody(SILGenFunction &SGF, diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 0aef4de8034b3..0f4866879fb90 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -216,11 +216,9 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, .forwardInto(SGF, Loc, init.get()); ++elti; } else { -#ifndef NDEBUG - assert( - field->getType()->isEqual(field->getParentInitializer()->getType()) - && "Checked by sema"); -#endif + assert(field->getType()->getReferenceStorageReferent()->isEqual( + field->getParentInitializer()->getType()) && + "Initialization of field with mismatched type!"); // Cleanup after this initialization. FullExpr scope(SGF.Cleanups, field->getParentPatternBinding()); diff --git a/lib/SILOptimizer/CMakeLists.txt b/lib/SILOptimizer/CMakeLists.txt index 4bf45e0ebd8db..ec1440d2b06a1 100644 --- a/lib/SILOptimizer/CMakeLists.txt +++ b/lib/SILOptimizer/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(LoopTransforms) add_subdirectory(Mandatory) add_subdirectory(PassManager) add_subdirectory(SILCombiner) +add_subdirectory(SemanticARC) add_subdirectory(Transforms) add_subdirectory(UtilityPasses) add_subdirectory(Utils) diff --git a/lib/SILOptimizer/SemanticARC/CMakeLists.txt b/lib/SILOptimizer/SemanticARC/CMakeLists.txt new file mode 100644 index 0000000000000..91b9e107c5dc8 --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/CMakeLists.txt @@ -0,0 +1,3 @@ +target_sources(swiftSILOptimizer PRIVATE + SemanticARCOpts.cpp + OwnershipLiveRange.cpp) diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp new file mode 100644 index 0000000000000..187b28e68c27b --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp @@ -0,0 +1,354 @@ +//===--- OwnershipLiveRange.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 "OwnershipLiveRange.h" +#include "OwnershipPhiOperand.h" + +using namespace swift; +using namespace swift::semanticarc; + +OwnershipLiveRange::OwnershipLiveRange(SILValue value) + : introducer(*OwnedValueIntroducer::get(value)), destroyingUses(), + ownershipForwardingUses(), unknownConsumingUses() { + assert(introducer.value.getOwnershipKind() == ValueOwnershipKind::Owned); + + SmallVector tmpDestroyingUses; + SmallVector tmpForwardingConsumingUses; + SmallVector tmpUnknownConsumingUses; + + // We know that our silvalue produces an @owned value. Look through all of our + // uses and classify them as either consuming or not. + SmallVector worklist(introducer.value->getUses()); + while (!worklist.empty()) { + auto *op = worklist.pop_back_val(); + + // Skip type dependent operands. + if (op->isTypeDependent()) + continue; + + // Do a quick check that we did not add ValueOwnershipKind that are not + // owned to the worklist. + assert(op->get().getOwnershipKind() == ValueOwnershipKind::Owned && + "Added non-owned value to worklist?!"); + + auto *user = op->getUser(); + + // Ok, this constraint can take something owned as live. Assert that it + // can also accept something that is guaranteed. Any non-consuming use of + // an owned value should be able to take a guaranteed parameter as well + // (modulo bugs). We assert to catch these. + if (!op->isConsumingUse()) { + continue; + } + + // Ok, we know now that we have a consuming use. See if we have a destroy + // value, quickly up front. If we do have one, stash it and continue. + if (isa(user)) { + tmpDestroyingUses.push_back(op); + continue; + } + + // Otherwise, see if we have a forwarding value that has a single + // non-trivial operand that can accept a guaranteed value. If not, we can + // not recursively process it, so be conservative and assume that we /may + // consume/ the value, so the live range must not be eliminated. + // + // DISCUSSION: For now we do not support forwarding instructions with + // multiple non-trivial arguments since we would need to optimize all of + // the non-trivial arguments at the same time. + // + // NOTE: Today we do not support TermInsts for simplicity... we /could/ + // support it though if we need to. + auto *ti = dyn_cast(user); + if ((ti && !ti->isTransformationTerminator()) || + !isGuaranteedForwardingInst(user) || + 1 != count_if(user->getOperandValues( + true /*ignore type dependent operands*/), + [&](SILValue v) { + return v.getOwnershipKind() == + ValueOwnershipKind::Owned; + })) { + tmpUnknownConsumingUses.push_back(op); + continue; + } + + // Ok, this is a forwarding instruction whose ownership we can flip from + // owned -> guaranteed. + tmpForwardingConsumingUses.push_back(op); + + // If we have a non-terminator, just visit its users recursively to see if + // the the users force the live range to be alive. + if (!ti) { + for (SILValue v : user->getResults()) { + if (v.getOwnershipKind() != ValueOwnershipKind::Owned) + continue; + llvm::copy(v->getUses(), std::back_inserter(worklist)); + } + continue; + } + + // Otherwise, we know that we have no only a terminator, but a + // transformation terminator, so we should add the users of its results to + // the worklist. + for (auto &succ : ti->getSuccessors()) { + auto *succBlock = succ.getBB(); + + // If we do not have any arguments, then continue. + if (succBlock->args_empty()) + continue; + + for (auto *succArg : succBlock->getSILPhiArguments()) { + // If we have an any value, just continue. + if (succArg->getOwnershipKind() == ValueOwnershipKind::None) + continue; + + // Otherwise add all users of this BBArg to the worklist to visit + // recursively. + llvm::copy(succArg->getUses(), std::back_inserter(worklist)); + } + } + } + + // The order in which we append these to consumingUses matters since we assume + // their order as an invariant. This is done to ensure that we can pass off + // all of our uses or individual sub-arrays of our users without needing to + // move around memory. + llvm::copy(tmpDestroyingUses, std::back_inserter(consumingUses)); + llvm::copy(tmpForwardingConsumingUses, std::back_inserter(consumingUses)); + llvm::copy(tmpUnknownConsumingUses, std::back_inserter(consumingUses)); + + auto cUseArrayRef = llvm::makeArrayRef(consumingUses); + destroyingUses = cUseArrayRef.take_front(tmpDestroyingUses.size()); + ownershipForwardingUses = cUseArrayRef.slice( + tmpDestroyingUses.size(), tmpForwardingConsumingUses.size()); + unknownConsumingUses = cUseArrayRef.take_back(tmpUnknownConsumingUses.size()); +} + +void OwnershipLiveRange::insertEndBorrowsAtDestroys( + SILValue newGuaranteedValue, DeadEndBlocks &deadEndBlocks, + ValueLifetimeAnalysis::Frontier &scratch) { + assert(scratch.empty() && "Expected scratch to be initially empty?!"); + + // Since we are looking through forwarding uses that can accept guaranteed + // parameters, we can have multiple destroy_value along the same path. We need + // to find the post-dominating block set of these destroy value to ensure that + // we do not insert multiple end_borrow. + // + // TODO: Hoist this out? + SILInstruction *inst = introducer.value->getDefiningInstruction(); + Optional analysis; + if (!inst) { + analysis.emplace(cast(introducer.value), + getAllConsumingInsts()); + } else { + analysis.emplace(inst, getAllConsumingInsts()); + } + + // Use all consuming uses in our value lifetime analysis to ensure correctness + // in the face of unreachable code. + bool foundCriticalEdges = !analysis->computeFrontier( + scratch, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); + (void)foundCriticalEdges; + assert(!foundCriticalEdges); + auto loc = RegularLocation::getAutoGeneratedLocation(); + while (!scratch.empty()) { + auto *insertPoint = scratch.pop_back_val(); + SILBuilderWithScope builder(insertPoint); + builder.createEndBorrow(loc, newGuaranteedValue); + } +} + +static void convertInstructionOwnership(SILInstruction *i, + ValueOwnershipKind oldOwnership, + ValueOwnershipKind newOwnership) { + // If this is a term inst, just convert all of its incoming values that are + // owned to be guaranteed. + if (auto *ti = dyn_cast(i)) { + for (auto &succ : ti->getSuccessors()) { + auto *succBlock = succ.getBB(); + + // If we do not have any arguments, then continue. + if (succBlock->args_empty()) + continue; + + for (auto *succArg : succBlock->getSILPhiArguments()) { + // If we have an any value, just continue. + if (succArg->getOwnershipKind() == oldOwnership) { + succArg->setOwnershipKind(newOwnership); + } + } + } + return; + } + + assert(i->hasResults()); + for (SILValue result : i->getResults()) { + if (auto *svi = dyn_cast(result)) { + if (svi->getOwnershipKind() == oldOwnership) { + svi->setOwnershipKind(newOwnership); + } + continue; + } + + if (auto *ofci = dyn_cast(result)) { + if (ofci->getOwnershipKind() == oldOwnership) { + ofci->setOwnershipKind(newOwnership); + } + continue; + } + + if (auto *sei = dyn_cast(result)) { + if (sei->getOwnershipKind() == oldOwnership) { + sei->setOwnershipKind(newOwnership); + } + continue; + } + + if (auto *mvir = dyn_cast(result)) { + if (mvir->getOwnershipKind() == oldOwnership) { + mvir->setOwnershipKind(newOwnership); + } + continue; + } + + llvm_unreachable("unhandled forwarding instruction?!"); + } +} + +void OwnershipLiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() && { + while (!ownershipForwardingUses.empty()) { + auto *i = ownershipForwardingUses.back()->getUser(); + ownershipForwardingUses = ownershipForwardingUses.drop_back(); + convertInstructionOwnership(i, ValueOwnershipKind::Owned, + ValueOwnershipKind::Guaranteed); + } +} + +void OwnershipLiveRange::convertToGuaranteedAndRAUW( + SILValue newGuaranteedValue, InstModCallbacks callbacks) && { + auto *value = cast(introducer.value); + while (!destroyingUses.empty()) { + auto *d = destroyingUses.back(); + destroyingUses = destroyingUses.drop_back(); + callbacks.deleteInst(d->getUser()); + } + + callbacks.eraseAndRAUWSingleValueInst(value, newGuaranteedValue); + + // Then change all of our guaranteed forwarding insts to have guaranteed + // ownership kind instead of what ever they previously had (ignoring trivial + // results); + std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); +} + +// TODO: If this is useful, move onto OwnedValueIntroducer itself? +static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) { + switch (introducer.kind) { + case OwnedValueIntroducerKind::Phi: { + auto *phiArg = cast(introducer.value); + phiArg->setOwnershipKind(ValueOwnershipKind::Guaranteed); + return phiArg; + } + case OwnedValueIntroducerKind::Struct: { + auto *si = cast(introducer.value); + si->setOwnershipKind(ValueOwnershipKind::Guaranteed); + return si; + } + case OwnedValueIntroducerKind::Tuple: { + auto *ti = cast(introducer.value); + ti->setOwnershipKind(ValueOwnershipKind::Guaranteed); + return ti; + } + case OwnedValueIntroducerKind::Copy: + case OwnedValueIntroducerKind::LoadCopy: + case OwnedValueIntroducerKind::Apply: + case OwnedValueIntroducerKind::BeginApply: + case OwnedValueIntroducerKind::TryApply: + case OwnedValueIntroducerKind::LoadTake: + case OwnedValueIntroducerKind::FunctionArgument: + case OwnedValueIntroducerKind::PartialApplyInit: + case OwnedValueIntroducerKind::AllocBoxInit: + case OwnedValueIntroducerKind::AllocRefInit: + return SILValue(); + } +} + +void OwnershipLiveRange::convertJoinedLiveRangePhiToGuaranteed( + DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, + InstModCallbacks callbacks) && { + + // First convert the phi value itself to be guaranteed. + SILValue phiValue = convertIntroducerToGuaranteed(introducer); + + // Then insert end_borrows at each of our destroys if we are consuming. We + // have to convert the phi to guaranteed first since otherwise, the ownership + // check when we create the end_borrows will trigger. + if (introducer.hasConsumingGuaranteedOperands()) { + insertEndBorrowsAtDestroys(phiValue, deadEndBlocks, scratch); + } + + // Then eliminate all of the destroys... + while (!destroyingUses.empty()) { + auto *d = destroyingUses.back(); + destroyingUses = destroyingUses.drop_back(); + callbacks.deleteInst(d->getUser()); + } + + // and change all of our guaranteed forwarding insts to have guaranteed + // ownership kind instead of what ever they previously had (ignoring trivial + // results); + std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); +} + +OwnershipLiveRange::HasConsumingUse_t +OwnershipLiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const { + // First do a quick check if we have /any/ unknown consuming + // uses. If we do not have any, return false early. + if (unknownConsumingUses.empty()) { + return HasConsumingUse_t::No; + } + + // Ok, we do have some unknown consuming uses. If we aren't assuming we are at + // the fixed point yet, just bail. + if (!assumingAtFixPoint) { + return HasConsumingUse_t::Yes; + } + + // We do not know how to handle yet cases where an owned value is used by + // multiple phi nodes. So we bail early if unknown consuming uses is > 1. + // + // TODO: Build up phi node web. + auto *op = getSingleUnknownConsumingUse(); + if (!op) { + return HasConsumingUse_t::Yes; + } + + // Make sure our single unknown consuming use is a branch inst. If not, bail, + // this is a /real/ unknown consuming use. + if (!OwnershipPhiOperand::get(op)) { + return HasConsumingUse_t::Yes; + } + + // Otherwise, setup the phi to incoming value map mapping the block arguments + // to our introducer. + return HasConsumingUse_t::YesButAllPhiArgs; +} + +OwnershipLiveRange::DestroyingInstsRange +OwnershipLiveRange::getDestroyingInsts() const { + return DestroyingInstsRange(getDestroyingUses(), OperandToUser()); +} + +OwnershipLiveRange::ConsumingInstsRange +OwnershipLiveRange::getAllConsumingInsts() const { + return ConsumingInstsRange(consumingUses, OperandToUser()); +} diff --git a/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h new file mode 100644 index 0000000000000..c9be71b479a3b --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/OwnershipLiveRange.h @@ -0,0 +1,210 @@ +//===--- OwnershipLiveRange.h ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPLIVERANGE_H +#define SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPLIVERANGE_H + +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" + +namespace swift { +namespace semanticarc { + +/// This class represents an "extended live range" of an owned value. Such a +/// representation includes: +/// +/// 1. The owned introducing value. +/// 2. Any forwarding instructions that consume the introduced value +/// (transitively) and then propagate a new owned value. +/// 3. Transitive destroys on the forwarding instructions/destroys on the owned +/// introducing value. +/// 4. Any unknown consuming uses that are not understood by this code. +/// +/// This allows for this to be used to convert such a set of uses from using +/// owned ownership to using guaranteed ownership by converting the +/// destroy_value -> end_borrow and "flipping" the ownership of individual +/// forwarding instructions. +/// +/// NOTE: We do not look through "phi nodes" in the ownership graph (e.x.: real +/// phi arguments, struct, tuple). Instead we represent those as nodes in a +/// larger phi ownership web, connected via individual OwnershipLiveRange. +class LLVM_LIBRARY_VISIBILITY OwnershipLiveRange { + /// The value that we are computing the LiveRange for. Expected to be an owned + /// introducer and not to be forwarding. + OwnedValueIntroducer introducer; + + /// A vector that we store all of our uses into. + /// + /// Some properties of this array are: + /// + /// 1. It is only mutated in the constructor of LiveRange. + /// + /// 2. destroyingUses, ownershipForwardingUses, and unknownConsumingUses are + /// views into this array. We store the respective uses in the aforementioned + /// order. This is why it is important not to mutate consumingUses after we + /// construct the LiveRange since going from small -> large could invalidate + /// the uses. + SmallVector consumingUses; + + /// A list of destroy_values of the live range. + /// + /// This is just a view into consuming uses. + ArrayRef destroyingUses; + + /// A list of forwarding instructions that forward owned ownership, but that + /// are also able to be converted to guaranteed ownership. + /// + /// If we are able to eliminate this LiveRange due to it being from a + /// guaranteed value, we must flip the ownership of all of these instructions + /// to guaranteed from owned. + /// + /// NOTE: Normally only destroying or consuming uses end the live range. We + /// copy these transitive uses as well into the consumingUses array since + /// transitive uses can extend a live range up to an unreachable block without + /// ultimately being consuming. In such a situation if we did not also store + /// this into consuming uses, we would not be able to ascertain just using the + /// "consumingUses" array the true lifetime of the OwnershipLiveRange. + /// + /// Corresponds to isOwnershipForwardingInst(...). + ArrayRef ownershipForwardingUses; + + /// Consuming uses that we were not able to understand as a forwarding + /// instruction or a destroy_value. These must be passed a strongly control + /// equivalent +1 value. + ArrayRef unknownConsumingUses; + +public: + OwnershipLiveRange(SILValue value); + OwnershipLiveRange(const OwnershipLiveRange &) = delete; + OwnershipLiveRange &operator=(const OwnershipLiveRange &) = delete; + + enum class HasConsumingUse_t { + No = 0, + YesButAllPhiArgs = 1, + Yes = 2, + }; + + /// Return true if v only has invalidating uses that are destroy_value. Such + /// an owned value is said to represent a dead "live range". + /// + /// Semantically this implies that a value is never passed off as +1 to memory + /// or another function implying it can be used everywhere at +0. + HasConsumingUse_t + hasUnknownConsumingUse(bool assumingFixedPoint = false) const; + + /// Return an array ref to /all/ consuming uses. Will include all 3 sorts of + /// consuming uses: destroying uses, forwarding consuming uses, and unknown + /// forwarding instruction. + ArrayRef getAllConsumingUses() const { return consumingUses; } + + ArrayRef getDestroyingUses() const { return destroyingUses; } + +private: + struct OperandToUser; + +public: + using DestroyingInstsRange = + TransformRange, OperandToUser>; + DestroyingInstsRange getDestroyingInsts() const; + + using ConsumingInstsRange = + TransformRange, OperandToUser>; + ConsumingInstsRange getAllConsumingInsts() const; + + /// If this LiveRange has a single destroying use, return that use. Otherwise, + /// return nullptr. + Operand *getSingleDestroyingUse() const { + if (destroyingUses.size() != 1) { + return nullptr; + } + return destroyingUses.front(); + } + + /// If this LiveRange has a single unknown destroying use, return that + /// use. Otherwise, return nullptr. + Operand *getSingleUnknownConsumingUse() const { + if (unknownConsumingUses.size() != 1) { + return nullptr; + } + return unknownConsumingUses.front(); + } + + OwnedValueIntroducer getIntroducer() const { return introducer; } + + ArrayRef getOwnershipForwardingUses() const { + return ownershipForwardingUses; + } + + void convertOwnedGeneralForwardingUsesToGuaranteed() &&; + + /// A consuming operation that: + /// + /// 1. If \p insertEndBorrows is true inserts end borrows at all + /// destroying insts locations. + /// + /// 2. Deletes all destroy_values. + /// + /// 3. RAUW value with newGuaranteedValue. + /// + /// 4. Convert all of the general forwarding instructions from + /// @owned -> @guaranteed. "Like Dominoes". + /// + /// 5. Leaves all of the unknown consuming users alone. It is up to + /// the caller to handle converting their ownership. + void convertToGuaranteedAndRAUW(SILValue newGuaranteedValue, + InstModCallbacks callbacks) &&; + + /// A consuming operation that in order: + /// + /// 1. Converts the phi argument to be guaranteed via setOwnership. + /// + /// 2. If this consumes a borrow, insert end_borrows at the relevant + /// destroy_values. + /// + /// 3. Deletes all destroy_values. + /// + /// 4. Converts all of the general forwarding instructions from @owned -> + /// @guaranteed. "Like Dominoes". + /// + /// NOTE: This leaves all of the unknown consuming users alone. It is up to + /// the caller to handle converting their ownership. + /// + /// NOTE: This routine leaves inserting begin_borrows for the incoming values + /// to the caller since those are not part of the LiveRange itself. + void convertJoinedLiveRangePhiToGuaranteed( + DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, + InstModCallbacks callbacks) &&; + + /// Given a new guaranteed value, insert end_borrow for the newGuaranteedValue + /// at all of our destroy_values in prepration for converting from owned to + /// guaranteed. + /// + /// This is used when converting load [copy] -> load_borrow. + void insertEndBorrowsAtDestroys(SILValue newGuaranteedValue, + DeadEndBlocks &deadEndBlocks, + ValueLifetimeAnalysis::Frontier &scratch); +}; + +struct OwnershipLiveRange::OperandToUser { + OperandToUser() {} + + SILInstruction *operator()(const Operand *use) const { + auto *nonConstUse = const_cast(use); + return nonConstUse->getUser(); + } +}; + +} // namespace semanticarc +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPLIVERANGE_H diff --git a/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h new file mode 100644 index 0000000000000..169eb9859bea8 --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h @@ -0,0 +1,122 @@ +//===--- OwnershipPhiOperand.h --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPPHIOPERAND_H +#define SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPPHIOPERAND_H + +#include "swift/Basic/STLExtras.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILUndef.h" +#include "swift/SIL/SILValue.h" + +namespace swift { +namespace semanticarc { + +/// The operand of a "phi" in the induced ownership graph of a def-use graph. +/// +/// Some examples: br, struct, tuple. +class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand { +public: + enum Kind { + Branch, + Struct, + Tuple, + }; + +private: + Operand *op; + + OwnershipPhiOperand(Operand *op) : op(op) {} + +public: + static Optional get(const Operand *op) { + switch (op->getUser()->getKind()) { + case SILInstructionKind::BranchInst: + case SILInstructionKind::StructInst: + case SILInstructionKind::TupleInst: + return {{const_cast(op)}}; + default: + return None; + } + } + + Kind getKind() const { + switch (op->getUser()->getKind()) { + case SILInstructionKind::BranchInst: + return Kind::Branch; + case SILInstructionKind::StructInst: + return Kind::Struct; + case SILInstructionKind::TupleInst: + return Kind::Tuple; + default: + llvm_unreachable("unhandled case?!"); + } + } + + Operand *getOperand() const { return op; } + SILValue getValue() const { return op->get(); } + SILType getType() const { return op->get()->getType(); } + + unsigned getOperandNumber() const { return op->getOperandNumber(); } + + void markUndef() & { + op->set(SILUndef::get(getType(), *op->getUser()->getFunction())); + } + + SILInstruction *getInst() const { return op->getUser(); } + + /// Return true if this phi consumes a borrow. + /// + /// If so, we may need to insert an extra begin_borrow to balance the +1 when + /// converting owned ownership phis to guaranteed ownership phis. + bool isGuaranteedConsuming() const { + switch (getKind()) { + case Kind::Branch: + return true; + case Kind::Tuple: + case Kind::Struct: + return false; + } + } + + bool operator<(const OwnershipPhiOperand &other) const { + return op < other.op; + } + + bool operator==(const OwnershipPhiOperand &other) const { + return op == other.op; + } + + bool visitResults(function_ref visitor) const { + switch (getKind()) { + case Kind::Struct: + return visitor(cast(getInst())); + case Kind::Tuple: + return visitor(cast(getInst())); + case Kind::Branch: { + auto *br = cast(getInst()); + unsigned opNum = getOperandNumber(); + return llvm::all_of( + br->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) { + return visitor(succBlock->getSILPhiArguments()[opNum]); + }); + } + } + } +}; + +} // namespace semanticarc +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_OWNERSHIPPHIOPERAND_H diff --git a/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h b/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h new file mode 100644 index 0000000000000..fce6908ae6252 --- /dev/null +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h @@ -0,0 +1,252 @@ +//===--- SemanticARCOptVisitor.h ------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H +#define SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H + +#include "swift/Basic/BlotSetVector.h" +#include "swift/Basic/FrozenMultiMap.h" +#include "swift/Basic/MultiMapCache.h" +#include "swift/SIL/BasicBlockUtils.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/SILVisitor.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" +#include "swift/SILOptimizer/Utils/ValueLifetime.h" + +namespace swift { +namespace semanticarc { + +bool constructCacheValue( + SILValue initialValue, + SmallVectorImpl &wellBehavedWriteAccumulator); + +/// A visitor that optimizes ownership instructions and eliminates any trivially +/// dead code that results after optimization. It uses an internal worklist that +/// is initialized on construction with targets to avoid iterator invalidation +/// issues. Rather than revisit the entire CFG like SILCombine and other +/// visitors do, we maintain a visitedSinceLastMutation list to ensure that we +/// revisit all interesting instructions in between mutations. +struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor + : SILInstructionVisitor { + /// Our main worklist. We use this after an initial run through. + SmallBlotSetVector worklist; + + /// A set of values that we have visited since the last mutation. We use this + /// to ensure that we do not visit values twice without mutating. + /// + /// This is specifically to ensure that we do not go into an infinite loop + /// when visiting phi nodes. + SmallBlotSetVector visitedSinceLastMutation; + + SILFunction &F; + Optional TheDeadEndBlocks; + ValueLifetimeAnalysis::Frontier lifetimeFrontier; + SmallMultiMapCache addressToExhaustiveWriteListCache; + + /// Are we assuming that we reached a fix point and are re-processing to + /// prepare to use the phiToIncomingValueMultiMap. + bool assumingAtFixedPoint = false; + + /// A map from a value that acts as a "joined owned introducer" in the def-use + /// graph. + /// + /// A "joined owned introducer" is a value with owned ownership whose + /// ownership is derived from multiple non-trivial owned operands of a related + /// instruction. Some examples are phi arguments, tuples, structs. Naturally, + /// all of these instructions must be non-unary instructions and only have + /// this property if they have multiple operands that are non-trivial. + /// + /// In such a case, we can not just treat them like normal forwarding concepts + /// since we can only eliminate optimize such a value if we are able to reason + /// about all of its operands together jointly. This is not amenable to a + /// small peephole analysis. + /// + /// Instead, as we perform the peephole analysis, using the multimap, we map + /// each joined owned value introducer to the set of its @owned operands that + /// we thought we could convert to guaranteed only if we could do the same to + /// the joined owned value introducer. Then once we finish performing + /// peepholes, we iterate through the map and see if any of our joined phi + /// ranges had all of their operand's marked with this property by iterating + /// over the multimap. Since we are dealing with owned values and we know that + /// our LiveRange can not see through joined live ranges, we know that we + /// should only be able to have a single owned value introducer for each + /// consumed operand. + FrozenMultiMap joinedOwnedIntroducerToConsumedOperands; + + /// If set to true, then we should only run cheap optimizations that do not + /// build up data structures or analyze code in depth. + /// + /// As an example, we do not do load [copy] optimizations here since they + /// generally involve more complex analysis, but simple peepholes of + /// copy_values we /do/ allow. + bool onlyGuaranteedOpts; + + using FrozenMultiMapRange = + decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange; + + explicit SemanticARCOptVisitor(SILFunction &F, bool onlyGuaranteedOpts) + : F(F), addressToExhaustiveWriteListCache(constructCacheValue), + onlyGuaranteedOpts(onlyGuaranteedOpts) {} + + DeadEndBlocks &getDeadEndBlocks() { + if (!TheDeadEndBlocks) + TheDeadEndBlocks.emplace(&F); + return *TheDeadEndBlocks; + } + + /// Given a single value instruction, RAUW it with newValue, add newValue to + /// the worklist, and then call eraseInstruction on i. + void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i, + SILValue newValue) { + worklist.insert(newValue); + for (auto *use : i->getUses()) { + for (SILValue result : use->getUser()->getResults()) { + worklist.insert(result); + } + } + i->replaceAllUsesWith(newValue); + eraseInstructionAndAddOperandsToWorklist(i); + } + + /// Add all operands of i to the worklist and then call eraseInstruction on + /// i. Assumes that the instruction doesnt have users. + void eraseInstructionAndAddOperandsToWorklist(SILInstruction *i) { + // Then copy all operands into the worklist for future processing. + for (SILValue v : i->getOperandValues()) { + worklist.insert(v); + } + eraseInstruction(i); + } + + /// Pop values off of visitedSinceLastMutation, adding .some values to the + /// worklist. + void drainVisitedSinceLastMutationIntoWorklist() { + while (!visitedSinceLastMutation.empty()) { + Optional nextValue = visitedSinceLastMutation.pop_back_val(); + if (!nextValue.hasValue()) { + continue; + } + worklist.insert(*nextValue); + } + } + + /// Remove all results of the given instruction from the worklist and then + /// erase the instruction. Assumes that the instruction does not have any + /// users left. + void eraseInstruction(SILInstruction *i) { + // Remove all SILValues of the instruction from the worklist and then erase + // the instruction. + for (SILValue result : i->getResults()) { + worklist.erase(result); + visitedSinceLastMutation.erase(result); + } + i->eraseFromParent(); + + // Add everything else from visitedSinceLastMutation to the worklist. + drainVisitedSinceLastMutationIntoWorklist(); + } + + InstModCallbacks getCallbacks() { + return InstModCallbacks( + [this](SILInstruction *inst) { eraseInstruction(inst); }, + [](SILInstruction *) {}, [](SILValue, SILValue) {}, + [this](SingleValueInstruction *i, SILValue value) { + eraseAndRAUWSingleValueInstruction(i, value); + }); + } + + /// The default visitor. + bool visitSILInstruction(SILInstruction *i) { + assert(!isGuaranteedForwardingInst(i) && + "Should have forwarding visitor for all ownership forwarding " + "instructions"); + return false; + } + + bool visitCopyValueInst(CopyValueInst *cvi); + bool visitBeginBorrowInst(BeginBorrowInst *bbi); + bool visitLoadInst(LoadInst *li); + static bool shouldVisitInst(SILInstruction *i) { + switch (i->getKind()) { + default: + return false; + case SILInstructionKind::CopyValueInst: + case SILInstructionKind::BeginBorrowInst: + case SILInstructionKind::LoadInst: + return true; + } + } + +#define FORWARDING_INST(NAME) \ + bool visit##NAME##Inst(NAME##Inst *cls) { \ + for (SILValue v : cls->getResults()) { \ + worklist.insert(v); \ + } \ + return false; \ + } + FORWARDING_INST(Tuple) + FORWARDING_INST(Struct) + FORWARDING_INST(Enum) + FORWARDING_INST(OpenExistentialRef) + FORWARDING_INST(Upcast) + FORWARDING_INST(UncheckedRefCast) + FORWARDING_INST(ConvertFunction) + FORWARDING_INST(RefToBridgeObject) + FORWARDING_INST(BridgeObjectToRef) + FORWARDING_INST(UnconditionalCheckedCast) + FORWARDING_INST(UncheckedEnumData) + FORWARDING_INST(MarkUninitialized) + FORWARDING_INST(SelectEnum) + FORWARDING_INST(DestructureStruct) + FORWARDING_INST(DestructureTuple) + FORWARDING_INST(TupleExtract) + FORWARDING_INST(StructExtract) + FORWARDING_INST(OpenExistentialValue) + FORWARDING_INST(OpenExistentialBoxValue) + FORWARDING_INST(MarkDependence) + FORWARDING_INST(InitExistentialRef) + FORWARDING_INST(DifferentiableFunction) + FORWARDING_INST(LinearFunction) + FORWARDING_INST(DifferentiableFunctionExtract) + FORWARDING_INST(LinearFunctionExtract) +#undef FORWARDING_INST + +#define FORWARDING_TERM(NAME) \ + bool visit##NAME##Inst(NAME##Inst *cls) { \ + for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ + for (SILValue v : succValues) { \ + worklist.insert(v); \ + } \ + } \ + return false; \ + } + + FORWARDING_TERM(SwitchEnum) + FORWARDING_TERM(CheckedCastBranch) + FORWARDING_TERM(Branch) +#undef FORWARDING_TERM + + bool isWrittenTo(LoadInst *li, const OwnershipLiveRange &lr); + + bool processWorklist(); + bool optimize(); + + bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi); + bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi); + bool tryJoiningCopyValueLiveRangeWithOperand(CopyValueInst *cvi); + bool performPostPeepholeOwnedArgElimination(); +}; + +} // namespace semanticarc +} // namespace swift + +#endif // SWIFT_SILOPTIMIZER_SEMANTICARC_SEMANTICARCOPTVISITOR_H diff --git a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp similarity index 61% rename from lib/SILOptimizer/Transforms/SemanticARCOpts.cpp rename to lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp index 743dac16eaa6b..b501ed7e64cff 100644 --- a/lib/SILOptimizer/Transforms/SemanticARCOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp @@ -11,6 +11,13 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sil-semantic-arc-opts" + +#include "swift/Basic/LLVM.h" + +#include "OwnershipLiveRange.h" +#include "OwnershipPhiOperand.h" +#include "SemanticARCOptVisitor.h" + #include "swift/Basic/BlotSetVector.h" #include "swift/Basic/FrozenMultiMap.h" #include "swift/Basic/MultiMapCache.h" @@ -36,642 +43,7 @@ #include "llvm/Support/CommandLine.h" using namespace swift; - -STATISTIC(NumEliminatedInsts, "number of removed instructions"); -STATISTIC(NumLoadCopyConvertedToLoadBorrow, - "number of load_copy converted to load_borrow"); - -//===----------------------------------------------------------------------===// -// Ownership Phi Operand -//===----------------------------------------------------------------------===// - -namespace { - -/// The operand of a "phi" in the induced ownership graph of a def-use graph. -/// -/// Some examples: br, struct, tuple. -class OwnershipPhiOperand { -public: - enum Kind { - Branch, - Struct, - Tuple, - }; - -private: - Operand *op; - - OwnershipPhiOperand(Operand *op) : op(op) {} - -public: - static Optional get(const Operand *op) { - switch (op->getUser()->getKind()) { - case SILInstructionKind::BranchInst: - case SILInstructionKind::StructInst: - case SILInstructionKind::TupleInst: - return {{const_cast(op)}}; - default: - return None; - } - } - - Kind getKind() const { - switch (op->getUser()->getKind()) { - case SILInstructionKind::BranchInst: - return Kind::Branch; - case SILInstructionKind::StructInst: - return Kind::Struct; - case SILInstructionKind::TupleInst: - return Kind::Tuple; - default: - llvm_unreachable("unhandled case?!"); - } - } - - Operand *getOperand() const { return op; } - SILValue getValue() const { return op->get(); } - SILType getType() const { return op->get()->getType(); } - - unsigned getOperandNumber() const { return op->getOperandNumber(); } - - void markUndef() & { - op->set(SILUndef::get(getType(), *op->getUser()->getFunction())); - } - - SILInstruction *getInst() const { return op->getUser(); } - - /// Return true if this phi consumes a borrow. - /// - /// If so, we may need to insert an extra begin_borrow to balance the +1 when - /// converting owned ownership phis to guaranteed ownership phis. - bool isGuaranteedConsuming() const { - switch (getKind()) { - case Kind::Branch: - return true; - case Kind::Tuple: - case Kind::Struct: - return false; - } - } - - bool operator<(const OwnershipPhiOperand &other) const { - return op < other.op; - } - - bool operator==(const OwnershipPhiOperand &other) const { - return op == other.op; - } - - bool visitResults(function_ref visitor) const { - switch (getKind()) { - case Kind::Struct: - return visitor(cast(getInst())); - case Kind::Tuple: - return visitor(cast(getInst())); - case Kind::Branch: { - auto *br = cast(getInst()); - unsigned opNum = getOperandNumber(); - return llvm::all_of( - br->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) { - return visitor(succBlock->getSILPhiArguments()[opNum]); - }); - } - } - } -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// Live Range Modeling -//===----------------------------------------------------------------------===// - -namespace { - -/// This class represents an "extended live range" of an owned value. Such a -/// representation includes: -/// -/// 1. The owned introducing value. -/// 2. Any forwarding instructions that consume the introduced value -/// (transitively) and then propagate a new owned value. -/// 3. Transitive destroys on the forwarding instructions/destroys on the owned -/// introducing value. -/// 4. Any unknown consuming uses that are not understood by this code. -/// -/// This allows for this to be used to convert such a set of uses from using -/// owned ownership to using guaranteed ownership by converting the -/// destroy_value -> end_borrow and "flipping" the ownership of individual -/// forwarding instructions. -/// -/// NOTE: We do not look through "phi nodes" in the ownership graph (e.x.: real -/// phi arguments, struct, tuple). Instead we represent those as nodes in a -/// larger phi ownership web, connected via individual OwnershipLiveRange. -class OwnershipLiveRange { - /// The value that we are computing the LiveRange for. Expected to be an owned - /// introducer and not to be forwarding. - OwnedValueIntroducer introducer; - - /// A vector that we store all of our uses into. - /// - /// Some properties of this array are: - /// - /// 1. It is only mutated in the constructor of LiveRange. - /// - /// 2. destroyingUses, ownershipForwardingUses, and unknownConsumingUses are - /// views into this array. We store the respective uses in the aforementioned - /// order. This is why it is important not to mutate consumingUses after we - /// construct the LiveRange since going from small -> large could invalidate - /// the uses. - SmallVector consumingUses; - - /// A list of destroy_values of the live range. - /// - /// This is just a view into consuming uses. - ArrayRef destroyingUses; - - /// A list of forwarding instructions that forward owned ownership, but that - /// are also able to be converted to guaranteed ownership. - /// - /// If we are able to eliminate this LiveRange due to it being from a - /// guaranteed value, we must flip the ownership of all of these instructions - /// to guaranteed from owned. - /// - /// NOTE: Normally only destroying or consuming uses end the live range. We - /// copy these transitive uses as well into the consumingUses array since - /// transitive uses can extend a live range up to an unreachable block without - /// ultimately being consuming. In such a situation if we did not also store - /// this into consuming uses, we would not be able to ascertain just using the - /// "consumingUses" array the true lifetime of the OwnershipLiveRange. - /// - /// Corresponds to isOwnershipForwardingInst(...). - ArrayRef ownershipForwardingUses; - - /// Consuming uses that we were not able to understand as a forwarding - /// instruction or a destroy_value. These must be passed a strongly control - /// equivalent +1 value. - ArrayRef unknownConsumingUses; - -public: - OwnershipLiveRange(SILValue value); - OwnershipLiveRange(const OwnershipLiveRange &) = delete; - OwnershipLiveRange &operator=(const OwnershipLiveRange &) = delete; - - enum class HasConsumingUse_t { - No = 0, - YesButAllPhiArgs = 1, - Yes = 2, - }; - - /// Return true if v only has invalidating uses that are destroy_value. Such - /// an owned value is said to represent a dead "live range". - /// - /// Semantically this implies that a value is never passed off as +1 to memory - /// or another function implying it can be used everywhere at +0. - HasConsumingUse_t - hasUnknownConsumingUse(bool assumingFixedPoint = false) const; - - /// Return an array ref to /all/ consuming uses. Will include all 3 sorts of - /// consuming uses: destroying uses, forwarding consuming uses, and unknown - /// forwarding instruction. - ArrayRef getAllConsumingUses() const { return consumingUses; } - - ArrayRef getDestroyingUses() const { return destroyingUses; } - -private: - struct OperandToUser; - -public: - using DestroyingInstsRange = - TransformRange, OperandToUser>; - DestroyingInstsRange getDestroyingInsts() const; - - using ConsumingInstsRange = - TransformRange, OperandToUser>; - ConsumingInstsRange getAllConsumingInsts() const; - - /// If this LiveRange has a single destroying use, return that use. Otherwise, - /// return nullptr. - Operand *getSingleDestroyingUse() const { - if (destroyingUses.size() != 1) { - return nullptr; - } - return destroyingUses.front(); - } - - /// If this LiveRange has a single unknown destroying use, return that - /// use. Otherwise, return nullptr. - Operand *getSingleUnknownConsumingUse() const { - if (unknownConsumingUses.size() != 1) { - return nullptr; - } - return unknownConsumingUses.front(); - } - - OwnedValueIntroducer getIntroducer() const { return introducer; } - - ArrayRef getOwnershipForwardingUses() const { - return ownershipForwardingUses; - } - - void convertOwnedGeneralForwardingUsesToGuaranteed() &&; - - /// A consuming operation that: - /// - /// 1. If \p insertEndBorrows is true inserts end borrows at all - /// destroying insts locations. - /// - /// 2. Deletes all destroy_values. - /// - /// 3. RAUW value with newGuaranteedValue. - /// - /// 4. Convert all of the general forwarding instructions from - /// @owned -> @guaranteed. "Like Dominoes". - /// - /// 5. Leaves all of the unknown consuming users alone. It is up to - /// the caller to handle converting their ownership. - void convertToGuaranteedAndRAUW(SILValue newGuaranteedValue, - InstModCallbacks callbacks) &&; - - /// A consuming operation that in order: - /// - /// 1. Converts the phi argument to be guaranteed via setOwnership. - /// - /// 2. If this consumes a borrow, insert end_borrows at the relevant - /// destroy_values. - /// - /// 3. Deletes all destroy_values. - /// - /// 4. Converts all of the general forwarding instructions from @owned -> - /// @guaranteed. "Like Dominoes". - /// - /// NOTE: This leaves all of the unknown consuming users alone. It is up to - /// the caller to handle converting their ownership. - /// - /// NOTE: This routine leaves inserting begin_borrows for the incoming values - /// to the caller since those are not part of the LiveRange itself. - void convertJoinedLiveRangePhiToGuaranteed( - DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, - InstModCallbacks callbacks) &&; - - /// Given a new guaranteed value, insert end_borrow for the newGuaranteedValue - /// at all of our destroy_values in prepration for converting from owned to - /// guaranteed. - /// - /// This is used when converting load [copy] -> load_borrow. - void insertEndBorrowsAtDestroys(SILValue newGuaranteedValue, - DeadEndBlocks &deadEndBlocks, - ValueLifetimeAnalysis::Frontier &scratch); -}; - -} // end anonymous namespace - -struct OwnershipLiveRange::OperandToUser { - OperandToUser() {} - - SILInstruction *operator()(const Operand *use) const { - auto *nonConstUse = const_cast(use); - return nonConstUse->getUser(); - } -}; - -OwnershipLiveRange::DestroyingInstsRange -OwnershipLiveRange::getDestroyingInsts() const { - return DestroyingInstsRange(getDestroyingUses(), OperandToUser()); -} - -OwnershipLiveRange::ConsumingInstsRange -OwnershipLiveRange::getAllConsumingInsts() const { - return ConsumingInstsRange(consumingUses, OperandToUser()); -} - -OwnershipLiveRange::OwnershipLiveRange(SILValue value) - : introducer(*OwnedValueIntroducer::get(value)), destroyingUses(), - ownershipForwardingUses(), unknownConsumingUses() { - assert(introducer.value.getOwnershipKind() == ValueOwnershipKind::Owned); - - SmallVector tmpDestroyingUses; - SmallVector tmpForwardingConsumingUses; - SmallVector tmpUnknownConsumingUses; - - // We know that our silvalue produces an @owned value. Look through all of our - // uses and classify them as either consuming or not. - SmallVector worklist(introducer.value->getUses()); - while (!worklist.empty()) { - auto *op = worklist.pop_back_val(); - - // Skip type dependent operands. - if (op->isTypeDependent()) - continue; - - // Do a quick check that we did not add ValueOwnershipKind that are not - // owned to the worklist. - assert(op->get().getOwnershipKind() == ValueOwnershipKind::Owned && - "Added non-owned value to worklist?!"); - - auto *user = op->getUser(); - - // Ok, this constraint can take something owned as live. Assert that it - // can also accept something that is guaranteed. Any non-consuming use of - // an owned value should be able to take a guaranteed parameter as well - // (modulo bugs). We assert to catch these. - if (!op->isConsumingUse()) { - continue; - } - - // Ok, we know now that we have a consuming use. See if we have a destroy - // value, quickly up front. If we do have one, stash it and continue. - if (isa(user)) { - tmpDestroyingUses.push_back(op); - continue; - } - - // Otherwise, see if we have a forwarding value that has a single - // non-trivial operand that can accept a guaranteed value. If not, we can - // not recursively process it, so be conservative and assume that we /may - // consume/ the value, so the live range must not be eliminated. - // - // DISCUSSION: For now we do not support forwarding instructions with - // multiple non-trivial arguments since we would need to optimize all of - // the non-trivial arguments at the same time. - // - // NOTE: Today we do not support TermInsts for simplicity... we /could/ - // support it though if we need to. - auto *ti = dyn_cast(user); - if ((ti && !ti->isTransformationTerminator()) || - !isGuaranteedForwardingInst(user) || - 1 != count_if(user->getOperandValues( - true /*ignore type dependent operands*/), - [&](SILValue v) { - return v.getOwnershipKind() == - ValueOwnershipKind::Owned; - })) { - tmpUnknownConsumingUses.push_back(op); - continue; - } - - // Ok, this is a forwarding instruction whose ownership we can flip from - // owned -> guaranteed. - tmpForwardingConsumingUses.push_back(op); - - // If we have a non-terminator, just visit its users recursively to see if - // the the users force the live range to be alive. - if (!ti) { - for (SILValue v : user->getResults()) { - if (v.getOwnershipKind() != ValueOwnershipKind::Owned) - continue; - llvm::copy(v->getUses(), std::back_inserter(worklist)); - } - continue; - } - - // Otherwise, we know that we have no only a terminator, but a - // transformation terminator, so we should add the users of its results to - // the worklist. - for (auto &succ : ti->getSuccessors()) { - auto *succBlock = succ.getBB(); - - // If we do not have any arguments, then continue. - if (succBlock->args_empty()) - continue; - - for (auto *succArg : succBlock->getSILPhiArguments()) { - // If we have an any value, just continue. - if (succArg->getOwnershipKind() == ValueOwnershipKind::None) - continue; - - // Otherwise add all users of this BBArg to the worklist to visit - // recursively. - llvm::copy(succArg->getUses(), std::back_inserter(worklist)); - } - } - } - - // The order in which we append these to consumingUses matters since we assume - // their order as an invariant. This is done to ensure that we can pass off - // all of our uses or individual sub-arrays of our users without needing to - // move around memory. - llvm::copy(tmpDestroyingUses, std::back_inserter(consumingUses)); - llvm::copy(tmpForwardingConsumingUses, std::back_inserter(consumingUses)); - llvm::copy(tmpUnknownConsumingUses, std::back_inserter(consumingUses)); - - auto cUseArrayRef = llvm::makeArrayRef(consumingUses); - destroyingUses = cUseArrayRef.take_front(tmpDestroyingUses.size()); - ownershipForwardingUses = cUseArrayRef.slice( - tmpDestroyingUses.size(), tmpForwardingConsumingUses.size()); - unknownConsumingUses = cUseArrayRef.take_back(tmpUnknownConsumingUses.size()); -} - -void OwnershipLiveRange::insertEndBorrowsAtDestroys( - SILValue newGuaranteedValue, DeadEndBlocks &deadEndBlocks, - ValueLifetimeAnalysis::Frontier &scratch) { - assert(scratch.empty() && "Expected scratch to be initially empty?!"); - - // Since we are looking through forwarding uses that can accept guaranteed - // parameters, we can have multiple destroy_value along the same path. We need - // to find the post-dominating block set of these destroy value to ensure that - // we do not insert multiple end_borrow. - // - // TODO: Hoist this out? - SILInstruction *inst = introducer.value->getDefiningInstruction(); - Optional analysis; - if (!inst) { - analysis.emplace(cast(introducer.value), - getAllConsumingInsts()); - } else { - analysis.emplace(inst, getAllConsumingInsts()); - } - - // Use all consuming uses in our value lifetime analysis to ensure correctness - // in the face of unreachable code. - bool foundCriticalEdges = !analysis->computeFrontier( - scratch, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks); - (void)foundCriticalEdges; - assert(!foundCriticalEdges); - auto loc = RegularLocation::getAutoGeneratedLocation(); - while (!scratch.empty()) { - auto *insertPoint = scratch.pop_back_val(); - SILBuilderWithScope builder(insertPoint); - builder.createEndBorrow(loc, newGuaranteedValue); - } -} - -static void convertInstructionOwnership(SILInstruction *i, - ValueOwnershipKind oldOwnership, - ValueOwnershipKind newOwnership) { - // If this is a term inst, just convert all of its incoming values that are - // owned to be guaranteed. - if (auto *ti = dyn_cast(i)) { - for (auto &succ : ti->getSuccessors()) { - auto *succBlock = succ.getBB(); - - // If we do not have any arguments, then continue. - if (succBlock->args_empty()) - continue; - - for (auto *succArg : succBlock->getSILPhiArguments()) { - // If we have an any value, just continue. - if (succArg->getOwnershipKind() == oldOwnership) { - succArg->setOwnershipKind(newOwnership); - } - } - } - return; - } - - assert(i->hasResults()); - for (SILValue result : i->getResults()) { - if (auto *svi = dyn_cast(result)) { - if (svi->getOwnershipKind() == oldOwnership) { - svi->setOwnershipKind(newOwnership); - } - continue; - } - - if (auto *ofci = dyn_cast(result)) { - if (ofci->getOwnershipKind() == oldOwnership) { - ofci->setOwnershipKind(newOwnership); - } - continue; - } - - if (auto *sei = dyn_cast(result)) { - if (sei->getOwnershipKind() == oldOwnership) { - sei->setOwnershipKind(newOwnership); - } - continue; - } - - if (auto *mvir = dyn_cast(result)) { - if (mvir->getOwnershipKind() == oldOwnership) { - mvir->setOwnershipKind(newOwnership); - } - continue; - } - - llvm_unreachable("unhandled forwarding instruction?!"); - } -} - -void OwnershipLiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() && { - while (!ownershipForwardingUses.empty()) { - auto *i = ownershipForwardingUses.back()->getUser(); - ownershipForwardingUses = ownershipForwardingUses.drop_back(); - convertInstructionOwnership(i, ValueOwnershipKind::Owned, - ValueOwnershipKind::Guaranteed); - } -} - -void OwnershipLiveRange::convertToGuaranteedAndRAUW( - SILValue newGuaranteedValue, InstModCallbacks callbacks) && { - auto *value = cast(introducer.value); - while (!destroyingUses.empty()) { - auto *d = destroyingUses.back(); - destroyingUses = destroyingUses.drop_back(); - callbacks.deleteInst(d->getUser()); - ++NumEliminatedInsts; - } - - callbacks.eraseAndRAUWSingleValueInst(value, newGuaranteedValue); - - // Then change all of our guaranteed forwarding insts to have guaranteed - // ownership kind instead of what ever they previously had (ignoring trivial - // results); - std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); -} - -// TODO: If this is useful, move onto OwnedValueIntroducer itself? -static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) { - switch (introducer.kind) { - case OwnedValueIntroducerKind::Phi: { - auto *phiArg = cast(introducer.value); - phiArg->setOwnershipKind(ValueOwnershipKind::Guaranteed); - return phiArg; - } - case OwnedValueIntroducerKind::Struct: { - auto *si = cast(introducer.value); - si->setOwnershipKind(ValueOwnershipKind::Guaranteed); - return si; - } - case OwnedValueIntroducerKind::Tuple: { - auto *ti = cast(introducer.value); - ti->setOwnershipKind(ValueOwnershipKind::Guaranteed); - return ti; - } - case OwnedValueIntroducerKind::Copy: - case OwnedValueIntroducerKind::LoadCopy: - case OwnedValueIntroducerKind::Apply: - case OwnedValueIntroducerKind::BeginApply: - case OwnedValueIntroducerKind::TryApply: - case OwnedValueIntroducerKind::LoadTake: - case OwnedValueIntroducerKind::FunctionArgument: - case OwnedValueIntroducerKind::PartialApplyInit: - case OwnedValueIntroducerKind::AllocBoxInit: - case OwnedValueIntroducerKind::AllocRefInit: - return SILValue(); - } -} - -void OwnershipLiveRange::convertJoinedLiveRangePhiToGuaranteed( - DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch, - InstModCallbacks callbacks) && { - - // First convert the phi value itself to be guaranteed. - SILValue phiValue = convertIntroducerToGuaranteed(introducer); - - // Then insert end_borrows at each of our destroys if we are consuming. We - // have to convert the phi to guaranteed first since otherwise, the ownership - // check when we create the end_borrows will trigger. - if (introducer.hasConsumingGuaranteedOperands()) { - insertEndBorrowsAtDestroys(phiValue, deadEndBlocks, scratch); - } - - // Then eliminate all of the destroys... - while (!destroyingUses.empty()) { - auto *d = destroyingUses.back(); - destroyingUses = destroyingUses.drop_back(); - callbacks.deleteInst(d->getUser()); - ++NumEliminatedInsts; - } - - // and change all of our guaranteed forwarding insts to have guaranteed - // ownership kind instead of what ever they previously had (ignoring trivial - // results); - std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed(); -} - -OwnershipLiveRange::HasConsumingUse_t -OwnershipLiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const { - // First do a quick check if we have /any/ unknown consuming - // uses. If we do not have any, return false early. - if (unknownConsumingUses.empty()) { - return HasConsumingUse_t::No; - } - - // Ok, we do have some unknown consuming uses. If we aren't assuming we are at - // the fixed point yet, just bail. - if (!assumingAtFixPoint) { - return HasConsumingUse_t::Yes; - } - - // We do not know how to handle yet cases where an owned value is used by - // multiple phi nodes. So we bail early if unknown consuming uses is > 1. - // - // TODO: Build up phi node web. - auto *op = getSingleUnknownConsumingUse(); - if (!op) { - return HasConsumingUse_t::Yes; - } - - // Make sure our single unknown consuming use is a branch inst. If not, bail, - // this is a /real/ unknown consuming use. - if (!OwnershipPhiOperand::get(op)) { - return HasConsumingUse_t::Yes; - } - - // Otherwise, setup the phi to incoming value map mapping the block arguments - // to our introducer. - return HasConsumingUse_t::YesButAllPhiArgs; -} +using namespace swift::semanticarc; //===----------------------------------------------------------------------===// // Address Written To Analysis @@ -679,9 +51,9 @@ OwnershipLiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const { /// Returns true if we were able to ascertain that either the initialValue has /// no write uses or all of the write uses were writes that we could understand. -static bool -constructCacheValue(SILValue initialValue, - SmallVectorImpl &wellBehavedWriteAccumulator) { +bool swift::semanticarc::constructCacheValue( + SILValue initialValue, + SmallVectorImpl &wellBehavedWriteAccumulator) { SmallVector worklist(initialValue->getUses()); while (!worklist.empty()) { @@ -815,227 +187,11 @@ constructCacheValue(SILValue initialValue, namespace { -/// A visitor that optimizes ownership instructions and eliminates any trivially -/// dead code that results after optimization. It uses an internal worklist that -/// is initialized on construction with targets to avoid iterator invalidation -/// issues. Rather than revisit the entire CFG like SILCombine and other -/// visitors do, we maintain a visitedSinceLastMutation list to ensure that we -/// revisit all interesting instructions in between mutations. -struct SemanticARCOptVisitor - : SILInstructionVisitor { - /// Our main worklist. We use this after an initial run through. - SmallBlotSetVector worklist; - - /// A set of values that we have visited since the last mutation. We use this - /// to ensure that we do not visit values twice without mutating. - /// - /// This is specifically to ensure that we do not go into an infinite loop - /// when visiting phi nodes. - SmallBlotSetVector visitedSinceLastMutation; - - SILFunction &F; - Optional TheDeadEndBlocks; - ValueLifetimeAnalysis::Frontier lifetimeFrontier; - SmallMultiMapCache addressToExhaustiveWriteListCache; - - /// Are we assuming that we reached a fix point and are re-processing to - /// prepare to use the phiToIncomingValueMultiMap. - bool assumingAtFixedPoint = false; - - /// A map from a value that acts as a "joined owned introducer" in the def-use - /// graph. - /// - /// A "joined owned introducer" is a value with owned ownership whose - /// ownership is derived from multiple non-trivial owned operands of a related - /// instruction. Some examples are phi arguments, tuples, structs. Naturally, - /// all of these instructions must be non-unary instructions and only have - /// this property if they have multiple operands that are non-trivial. - /// - /// In such a case, we can not just treat them like normal forwarding concepts - /// since we can only eliminate optimize such a value if we are able to reason - /// about all of its operands together jointly. This is not amenable to a - /// small peephole analysis. - /// - /// Instead, as we perform the peephole analysis, using the multimap, we map - /// each joined owned value introducer to the set of its @owned operands that - /// we thought we could convert to guaranteed only if we could do the same to - /// the joined owned value introducer. Then once we finish performing - /// peepholes, we iterate through the map and see if any of our joined phi - /// ranges had all of their operand's marked with this property by iterating - /// over the multimap. Since we are dealing with owned values and we know that - /// our LiveRange can not see through joined live ranges, we know that we - /// should only be able to have a single owned value introducer for each - /// consumed operand. - FrozenMultiMap joinedOwnedIntroducerToConsumedOperands; - - /// If set to true, then we should only run cheap optimizations that do not - /// build up data structures or analyze code in depth. - /// - /// As an example, we do not do load [copy] optimizations here since they - /// generally involve more complex analysis, but simple peepholes of - /// copy_values we /do/ allow. - bool onlyGuaranteedOpts; - - using FrozenMultiMapRange = - decltype(joinedOwnedIntroducerToConsumedOperands)::PairToSecondEltRange; - - explicit SemanticARCOptVisitor(SILFunction &F, bool onlyGuaranteedOpts) - : F(F), addressToExhaustiveWriteListCache(constructCacheValue), - onlyGuaranteedOpts(onlyGuaranteedOpts) {} - - DeadEndBlocks &getDeadEndBlocks() { - if (!TheDeadEndBlocks) - TheDeadEndBlocks.emplace(&F); - return *TheDeadEndBlocks; - } - - /// Given a single value instruction, RAUW it with newValue, add newValue to - /// the worklist, and then call eraseInstruction on i. - void eraseAndRAUWSingleValueInstruction(SingleValueInstruction *i, SILValue newValue) { - worklist.insert(newValue); - for (auto *use : i->getUses()) { - for (SILValue result : use->getUser()->getResults()) { - worklist.insert(result); - } - } - i->replaceAllUsesWith(newValue); - eraseInstructionAndAddOperandsToWorklist(i); - } - - /// Add all operands of i to the worklist and then call eraseInstruction on - /// i. Assumes that the instruction doesnt have users. - void eraseInstructionAndAddOperandsToWorklist(SILInstruction *i) { - // Then copy all operands into the worklist for future processing. - for (SILValue v : i->getOperandValues()) { - worklist.insert(v); - } - eraseInstruction(i); - } - - /// Pop values off of visitedSinceLastMutation, adding .some values to the - /// worklist. - void drainVisitedSinceLastMutationIntoWorklist() { - while (!visitedSinceLastMutation.empty()) { - Optional nextValue = visitedSinceLastMutation.pop_back_val(); - if (!nextValue.hasValue()) { - continue; - } - worklist.insert(*nextValue); - } - } - - /// Remove all results of the given instruction from the worklist and then - /// erase the instruction. Assumes that the instruction does not have any - /// users left. - void eraseInstruction(SILInstruction *i) { - // Remove all SILValues of the instruction from the worklist and then erase - // the instruction. - for (SILValue result : i->getResults()) { - worklist.erase(result); - visitedSinceLastMutation.erase(result); - } - i->eraseFromParent(); - - // Add everything else from visitedSinceLastMutation to the worklist. - drainVisitedSinceLastMutationIntoWorklist(); - } - - InstModCallbacks getCallbacks() { - return InstModCallbacks( - [this](SILInstruction *inst) { eraseInstruction(inst); }, - [](SILInstruction *) {}, [](SILValue, SILValue) {}, - [this](SingleValueInstruction *i, SILValue value) { - eraseAndRAUWSingleValueInstruction(i, value); - }); - } - - /// The default visitor. - bool visitSILInstruction(SILInstruction *i) { - assert(!isGuaranteedForwardingInst(i) && - "Should have forwarding visitor for all ownership forwarding " - "instructions"); - return false; - } - - bool visitCopyValueInst(CopyValueInst *cvi); - bool visitBeginBorrowInst(BeginBorrowInst *bbi); - bool visitLoadInst(LoadInst *li); - static bool shouldVisitInst(SILInstruction *i) { - switch (i->getKind()) { - default: - return false; - case SILInstructionKind::CopyValueInst: - case SILInstructionKind::BeginBorrowInst: - case SILInstructionKind::LoadInst: - return true; - } - } - -#define FORWARDING_INST(NAME) \ - bool visit##NAME##Inst(NAME##Inst *cls) { \ - for (SILValue v : cls->getResults()) { \ - worklist.insert(v); \ - } \ - return false; \ - } - FORWARDING_INST(Tuple) - FORWARDING_INST(Struct) - FORWARDING_INST(Enum) - FORWARDING_INST(OpenExistentialRef) - FORWARDING_INST(Upcast) - FORWARDING_INST(UncheckedRefCast) - FORWARDING_INST(ConvertFunction) - FORWARDING_INST(RefToBridgeObject) - FORWARDING_INST(BridgeObjectToRef) - FORWARDING_INST(UnconditionalCheckedCast) - FORWARDING_INST(UncheckedEnumData) - FORWARDING_INST(MarkUninitialized) - FORWARDING_INST(SelectEnum) - FORWARDING_INST(DestructureStruct) - FORWARDING_INST(DestructureTuple) - FORWARDING_INST(TupleExtract) - FORWARDING_INST(StructExtract) - FORWARDING_INST(OpenExistentialValue) - FORWARDING_INST(OpenExistentialBoxValue) - FORWARDING_INST(MarkDependence) - FORWARDING_INST(InitExistentialRef) - FORWARDING_INST(DifferentiableFunction) - FORWARDING_INST(LinearFunction) - FORWARDING_INST(DifferentiableFunctionExtract) - FORWARDING_INST(LinearFunctionExtract) -#undef FORWARDING_INST - -#define FORWARDING_TERM(NAME) \ - bool visit##NAME##Inst(NAME##Inst *cls) { \ - for (auto succValues : cls->getSuccessorBlockArgumentLists()) { \ - for (SILValue v : succValues) { \ - worklist.insert(v); \ - } \ - } \ - return false; \ - } - - FORWARDING_TERM(SwitchEnum) - FORWARDING_TERM(CheckedCastBranch) - FORWARDING_TERM(Branch) -#undef FORWARDING_TERM - - bool isWrittenTo(LoadInst *li, const OwnershipLiveRange &lr); - - bool processWorklist(); - bool optimize(); - - bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi); - bool eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi); - bool tryJoiningCopyValueLiveRangeWithOperand(CopyValueInst *cvi); - bool performPostPeepholeOwnedArgElimination(); -}; - } // end anonymous namespace static llvm::cl::opt -VerifyAfterTransform("sil-semantic-arc-opts-verify-after-transform", - llvm::cl::init(false), llvm::cl::Hidden); + VerifyAfterTransform("sil-semantic-arc-opts-verify-after-transform", + llvm::cl::init(false), llvm::cl::Hidden); static bool canEliminatePhi( SemanticARCOptVisitor::FrozenMultiMapRange optimizableIntroducerRange, @@ -1392,11 +548,9 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { while (!endBorrows.empty()) { auto *ebi = endBorrows.pop_back_val(); eraseInstruction(ebi); - ++NumEliminatedInsts; } eraseAndRAUWSingleValueInstruction(bbi, bbi->getOperand()); - ++NumEliminatedInsts; return true; } @@ -1436,7 +590,8 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { // are within the borrow scope. // // TODO: This needs a better name. -bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst *cvi) { +bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization( + CopyValueInst *cvi) { // For now, do not run this optimization. This is just to be careful. if (onlyGuaranteedOpts) return false; @@ -1611,13 +766,13 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(CopyValueInst // Otherwise, our copy must truly not be needed, o RAUW and convert to // guaranteed! std::move(lr).convertToGuaranteedAndRAUW(cvi->getOperand(), getCallbacks()); - ++NumEliminatedInsts; return true; } /// If cvi only has destroy value users, then cvi is a dead live range. Lets /// eliminate all such dead live ranges. -bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) { +bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue( + CopyValueInst *cvi) { // This is a cheap optimization generally. // See if we are lucky and have a simple case. @@ -1625,7 +780,6 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) if (auto *dvi = dyn_cast(op->getUser())) { eraseInstruction(dvi); eraseInstructionAndAddOperandsToWorklist(cvi); - NumEliminatedInsts += 2; return true; } } @@ -1649,10 +803,8 @@ bool SemanticARCOptVisitor::eliminateDeadLiveRangeCopyValue(CopyValueInst *cvi) // Now that we have a truly dead live range copy value, eliminate it! while (!destroys.empty()) { eraseInstruction(destroys.pop_back_val()); - ++NumEliminatedInsts; } eraseInstructionAndAddOperandsToWorklist(cvi); - ++NumEliminatedInsts; return true; } @@ -1812,7 +964,6 @@ bool SemanticARCOptVisitor::tryJoiningCopyValueLiveRangeWithOperand( if (canSafelyJoinSimpleRange(operand, dvi, cvi)) { eraseInstruction(dvi); eraseAndRAUWSingleValueInstruction(cvi, operand); - NumEliminatedInsts += 2; return true; } } @@ -1857,8 +1008,7 @@ namespace { /// written to again. In both cases, we can convert load [copy] -> load_borrow /// safely. class StorageGuaranteesLoadVisitor - : public AccessUseDefChainVisitor -{ + : public AccessUseDefChainVisitor { // The outer SemanticARCOptVisitor. SemanticARCOptVisitor &ARCOpt; @@ -1867,7 +1017,7 @@ class StorageGuaranteesLoadVisitor // The current address being visited. SILValue currentAddress; - + Optional isWritten; public: @@ -1880,11 +1030,9 @@ class StorageGuaranteesLoadVisitor currentAddress = nullptr; isWritten = written; } - - void next(SILValue address) { - currentAddress = address; - } - + + void next(SILValue address) { currentAddress = address; } + void visitNestedAccess(BeginAccessInst *access) { // First see if we have read/modify. If we do not, just look through the // nested access. @@ -1901,9 +1049,7 @@ class StorageGuaranteesLoadVisitor // scope. If so, we may be able to use a load_borrow here! SmallVector endScopeUses; transform(access->getEndAccesses(), std::back_inserter(endScopeUses), - [](EndAccessInst *eai) { - return &eai->getAllOperands()[0]; - }); + [](EndAccessInst *eai) { return &eai->getAllOperands()[0]; }); SmallPtrSet visitedBlocks; LinearLifetimeChecker checker(visitedBlocks, ARCOpt.getDeadEndBlocks()); if (!checker.validateLifetime(access, endScopeUses, @@ -1930,7 +1076,7 @@ class StorageGuaranteesLoadVisitor return answer(true); } - + void visitArgumentAccess(SILFunctionArgument *arg) { // If this load_copy is from an indirect in_guaranteed argument, then we // know for sure that it will never be written to. @@ -2007,15 +1153,15 @@ class StorageGuaranteesLoadVisitor // able to also to promote load [copy] from such args to load_borrow. return answer(true); } - + void visitGlobalAccess(SILValue global) { return answer(!AccessedStorage(global, AccessedStorage::Global) - .isLetAccess(&ARCOpt.F)); + .isLetAccess(&ARCOpt.F)); } - + void visitClassAccess(RefElementAddrInst *field) { currentAddress = nullptr; - + // We know a let property won't be written to if the base object is // guaranteed for the duration of the access. // For non-let properties conservatively assume they may be written to. @@ -2071,15 +1217,13 @@ class StorageGuaranteesLoadVisitor baseObject, endScopeInsts, liveRange.getAllConsumingUses()); return answer(foundError); } - + // TODO: Handle other access kinds? void visitBase(SILValue base, AccessedStorage::Kind kind) { return answer(true); } - void visitNonAccess(SILValue addr) { - return answer(true); - } + void visitNonAccess(SILValue addr) { return answer(true); } void visitCast(SingleValueInstruction *cast, Operand *parentAddr) { return next(parentAddr->get()); @@ -2166,8 +1310,6 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) { lr.insertEndBorrowsAtDestroys(lbi, getDeadEndBlocks(), lifetimeFrontier); std::move(lr).convertToGuaranteedAndRAUW(lbi, getCallbacks()); - ++NumEliminatedInsts; - ++NumLoadCopyConvertedToLoadBorrow; return true; } diff --git a/lib/SILOptimizer/Transforms/CMakeLists.txt b/lib/SILOptimizer/Transforms/CMakeLists.txt index c51043a1910c3..ac3718b6cf20f 100644 --- a/lib/SILOptimizer/Transforms/CMakeLists.txt +++ b/lib/SILOptimizer/Transforms/CMakeLists.txt @@ -31,7 +31,6 @@ target_sources(swiftSILOptimizer PRIVATE RedundantLoadElimination.cpp RedundantOverflowCheckRemoval.cpp ReleaseDevirtualizer.cpp - SemanticARCOpts.cpp SILCodeMotion.cpp SILLowerAggregateInstrs.cpp SILMem2Reg.cpp diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a82b389991788..5f324d7579e64 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -56,8 +56,33 @@ void ConstraintSystem::PotentialBindings::inferTransitiveBindings( auto &bindings = relatedBindings->getSecond(); - // Infer transitive protocol requirements. - llvm::copy(bindings.Protocols, std::back_inserter(Protocols)); + // FIXME: This is a workaround necessary because solver doesn't filter + // bindings based on protocol requirements placed on a type variable. + // + // Forward propagate (subtype -> supertype) only literal conformance + // requirements since that helps solver to infer more types at + // parameter positions. + // + // \code + // func foo(_: String, _: T) -> T { + // fatalError() + // } + // + // func bar(_: Any?) {} + // + // func test() { + // bar(foo("", "")) + // } + // \endcode + // + // If one of the literal arguments doesn't propagate its + // `ExpressibleByStringLiteral` conformance, we'd end up picking + // `T` with only one type `Any?` which is incorrect. + llvm::copy_if(bindings.Protocols, std::back_inserter(Protocols), + [](const Constraint *protocol) { + return protocol->getKind() == + ConstraintKind::LiteralConformsTo; + }); // Infer transitive defaults. llvm::copy(bindings.Defaults, std::back_inserter(Defaults)); @@ -906,15 +931,7 @@ bool ConstraintSystem::PotentialBindings::infer( case ConstraintKind::ConformsTo: case ConstraintKind::SelfObjectOfProtocol: - // Swift 3 allowed the use of default types for normal conformances - // to expressible-by-literal protocols. - if (cs.getASTContext().LangOpts.EffectiveLanguageVersion[0] >= 4) - return false; - - if (!constraint->getSecondType()->is()) - return false; - - LLVM_FALLTHROUGH; + return false; case ConstraintKind::LiteralConformsTo: { // Record constraint where protocol requirement originated diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 6562cc33e8915..6536516e55959 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -643,7 +643,7 @@ Optional> GenericArgumentsMismatchFailure::getDiagnosticFor( void GenericArgumentsMismatchFailure::emitNoteForMismatch(int position) { auto *locator = getLocator(); - // Since there could be implicit conversions assoicated with argument + // Since there could be implicit conversions associated with argument // to parameter conversions, let's use parameter type as a source of // generic parameter information. auto paramSourceTy = diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 004dc8342f786..c3450ad9f39fd 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1328,17 +1328,11 @@ namespace { Type resolveTypeReferenceInExpression(TypeRepr *repr, TypeResolverContext resCtx, - OpenUnboundGenericTypeFn unboundTyOpener) { - if (!unboundTyOpener) { - unboundTyOpener = [](auto unboundTy) { - // FIXME: Don't let unbound generic types escape type resolution. - // For now, just return the unbound generic type. - return unboundTy; - }; - } - const auto result = - TypeResolution::forContextual(CS.DC, resCtx, unboundTyOpener) - .resolveType(repr); + const ConstraintLocatorBuilder &locator) { + // Introduce type variables for unbound generics. + const auto opener = OpenUnboundGenericType(CS, locator); + const auto result = TypeResolution::forContextual(CS.DC, resCtx, opener) + .resolveType(repr); if (result->hasError()) { return Type(); } @@ -1364,8 +1358,7 @@ namespace { auto *repr = E->getTypeRepr(); assert(repr && "Explicit node has no type repr!"); type = resolveTypeReferenceInExpression( - repr, TypeResolverContext::InExpression, - OpenUnboundGenericType(CS, locator)); + repr, TypeResolverContext::InExpression, locator); } if (!type || type->hasError()) return Type(); @@ -2058,48 +2051,36 @@ namespace { // parameter or return type is omitted, a fresh type variable is used to // stand in for that parameter or return type, allowing it to be inferred // from context. - auto getExplicitResultType = [&]() -> Type { - if (!closure->hasExplicitResultType()) { - return Type(); - } + Type resultTy = [&] { + if (closure->hasExplicitResultType()) { + if (auto declaredTy = closure->getExplicitResultType()) { + return declaredTy; + } - if (auto declaredTy = closure->getExplicitResultType()) { - return declaredTy; + const auto resolvedTy = resolveTypeReferenceInExpression( + closure->getExplicitResultTypeRepr(), + TypeResolverContext::InExpression, + CS.getConstraintLocator(closure, + ConstraintLocator::ClosureResult)); + if (resolvedTy) + return resolvedTy; } - return resolveTypeReferenceInExpression( - closure->getExplicitResultTypeRepr(), - TypeResolverContext::InExpression, nullptr); - }; - - Type resultTy; - auto *resultLoc = - CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult); - if (auto explicityTy = getExplicitResultType()) { - resultTy = explicityTy; - } else { - auto getContextualResultType = [&]() -> Type { - if (auto contextualType = CS.getContextualType(closure)) { - if (auto fnType = contextualType->getAs()) - return fnType->getResult(); - } - return Type(); - }; - - if (auto contextualResultTy = getContextualResultType()) { - resultTy = contextualResultTy; - } else { - // If no return type was specified, create a fresh type - // variable for it and mark it as possible hole. - // - // If this is a multi-statement closure, let's mark result - // as potential hole right away. - resultTy = CS.createTypeVariable( - resultLoc, - shouldTypeCheckInEnclosingExpression(closure) - ? 0 : TVO_CanBindToHole); + if (auto contextualType = CS.getContextualType(closure)) { + if (auto fnType = contextualType->getAs()) + return fnType->getResult(); } - } + + // If no return type was specified, create a fresh type + // variable for it and mark it as possible hole. + // + // If this is a multi-statement closure, let's mark result + // as potential hole right away. + return Type(CS.createTypeVariable( + CS.getConstraintLocator(closure, ConstraintLocator::ClosureResult), + shouldTypeCheckInEnclosingExpression(closure) ? 0 + : TVO_CanBindToHole)); + }(); return FunctionType::get(closureParams, resultTy, extInfo); } @@ -2364,9 +2345,7 @@ namespace { const Type castType = resolveTypeReferenceInExpression( isPattern->getCastTypeRepr(), TypeResolverContext::InExpression, - OpenUnboundGenericType(CS, - locator.withPathElement( - LocatorPathElt::PatternMatch(pattern)))); + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); if (!castType) return Type(); auto *subPattern = isPattern->getSubPattern(); @@ -2417,32 +2396,31 @@ namespace { FunctionRefKind functionRefKind = FunctionRefKind::Compound; if (enumPattern->getParentType() || enumPattern->getParentTypeRepr()) { // Resolve the parent type. - Type parentType = [&]() -> Type { - if (auto preTy = enumPattern->getParentType()) { - return preTy; + const auto parentType = [&] { + auto *const patternMatchLoc = CS.getConstraintLocator( + locator, {LocatorPathElt::PatternMatch(pattern), + ConstraintLocator::ParentType}); + + // FIXME: Sometimes the parent type is realized eagerly in + // ResolvePattern::visitUnresolvedDotExpr, so we have to open it + // ex post facto. Remove this once we learn how to resolve patterns + // while generating constraints to keep the opening of generic types + // contained within the type resolver. + if (const auto preresolvedTy = enumPattern->getParentType()) { + const auto openedTy = + CS.openUnboundGenericTypes(preresolvedTy, patternMatchLoc); + assert(openedTy); + return openedTy; } + return resolveTypeReferenceInExpression( enumPattern->getParentTypeRepr(), - TypeResolverContext::InExpression, [](auto unboundTy) { - // FIXME: We ought to pass an OpenUnboundGenericType object - // rather than calling CS.openUnboundGenericType below, but - // sometimes the parent type is resolved eagerly in - // ResolvePattern::visitUnresolvedDotExpr, letting unbound - // generics escape. - return unboundTy; - }); + TypeResolverContext::InExpression, patternMatchLoc); }(); if (!parentType) return Type(); - parentType = CS.openUnboundGenericTypes( - parentType, CS.getConstraintLocator( - locator, {LocatorPathElt::PatternMatch(pattern), - ConstraintLocator::ParentType})); - - assert(parentType); - // Perform member lookup into the parent's metatype. Type parentMetaType = MetatypeType::get(parentType); CS.addValueMemberConstraint( @@ -2986,8 +2964,7 @@ namespace { // Validate the resulting type. const auto toType = resolveTypeReferenceInExpression( repr, TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3013,8 +2990,7 @@ namespace { auto *const repr = expr->getCastTypeRepr(); const auto toType = resolveTypeReferenceInExpression( repr, TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3046,8 +3022,7 @@ namespace { auto *const repr = expr->getCastTypeRepr(); const auto toType = resolveTypeReferenceInExpression( repr, TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3074,8 +3049,7 @@ namespace { auto &ctx = CS.getASTContext(); const auto toType = resolveTypeReferenceInExpression( expr->getCastTypeRepr(), TypeResolverContext::ExplicitCastExpr, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, CS.getConstraintLocator(expr))); + CS.getConstraintLocator(expr)); if (!toType) return nullptr; @@ -3283,9 +3257,9 @@ namespace { Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) { if (auto *placeholderRepr = E->getPlaceholderTypeRepr()) { // Just resolve the referenced type. - // FIXME: The type reference needs to be opened into context. return resolveTypeReferenceInExpression( - placeholderRepr, TypeResolverContext::InExpression, nullptr); + placeholderRepr, TypeResolverContext::InExpression, + CS.getConstraintLocator(E)); } auto locator = CS.getConstraintLocator(E); @@ -3356,9 +3330,7 @@ namespace { // If a root type was explicitly given, then resolve it now. if (auto rootRepr = E->getRootType()) { const auto rootObjectTy = resolveTypeReferenceInExpression( - rootRepr, TypeResolverContext::InExpression, - // Introduce type variables for unbound generics. - OpenUnboundGenericType(CS, locator)); + rootRepr, TypeResolverContext::InExpression, locator); if (!rootObjectTy || rootObjectTy->hasError()) return Type(); diff --git a/lib/Sema/DerivedConformanceDifferentiable.cpp b/lib/Sema/DerivedConformanceDifferentiable.cpp index 451dc2c65efad..5ab7ffc81c1ef 100644 --- a/lib/Sema/DerivedConformanceDifferentiable.cpp +++ b/lib/Sema/DerivedConformanceDifferentiable.cpp @@ -32,12 +32,37 @@ using namespace swift; +/// Return true if `move(along:)` can be invoked on the given `Differentiable`- +/// conforming property. +/// +/// If the given property is a `var`, return true because `move(along:)` can be +/// invoked regardless. Otherwise, return true if and only if the property's +/// type's 'Differentiable.move(along:)' witness is non-mutating. +static bool canInvokeMoveAlongOnProperty( + VarDecl *vd, ProtocolConformanceRef diffableConformance) { + assert(diffableConformance && "Property must conform to 'Differentiable'"); + // `var` always supports `move(along:)` since it is mutable. + if (vd->getIntroducer() == VarDecl::Introducer::Var) + return true; + // When the property is a `let`, the only case that would be supported is when + // it has a `move(along:)` protocol requirement witness that is non-mutating. + auto interfaceType = vd->getInterfaceType(); + auto &C = vd->getASTContext(); + auto witness = diffableConformance.getWitnessByName( + interfaceType, DeclName(C, C.Id_move, {C.Id_along})); + if (!witness) + return false; + auto *decl = cast(witness.getDecl()); + return decl->isNonMutating(); +} + /// Get the stored properties of a nominal type that are relevant for /// differentiation, except the ones tagged `@noDerivative`. static void -getStoredPropertiesForDifferentiation(NominalTypeDecl *nominal, DeclContext *DC, - SmallVectorImpl &result, - bool includeLetProperties = false) { +getStoredPropertiesForDifferentiation( + NominalTypeDecl *nominal, DeclContext *DC, + SmallVectorImpl &result, + bool includeLetPropertiesWithNonmutatingMoveAlong = false) { auto &C = nominal->getASTContext(); auto *diffableProto = C.getProtocol(KnownProtocolKind::Differentiable); for (auto *vd : nominal->getStoredProperties()) { @@ -53,15 +78,18 @@ getStoredPropertiesForDifferentiation(NominalTypeDecl *nominal, DeclContext *DC, // Skip stored properties with `@noDerivative` attribute. if (vd->getAttrs().hasAttribute()) continue; - // Skip `let` stored properties if requested. - // `mutating func move(along:)` cannot be synthesized to update `let` - // properties. - if (!includeLetProperties && vd->isLet()) - continue; if (vd->getInterfaceType()->hasError()) continue; auto varType = DC->mapTypeIntoContext(vd->getValueInterfaceType()); - if (!TypeChecker::conformsToProtocol(varType, diffableProto, nominal)) + auto conformance = TypeChecker::conformsToProtocol( + varType, diffableProto, nominal); + if (!conformance) + continue; + // Skip `let` stored properties with a mutating `move(along:)` if requested. + // `mutating func move(along:)` cannot be synthesized to update `let` + // properties. + if (!includeLetPropertiesWithNonmutatingMoveAlong && + !canInvokeMoveAlongOnProperty(vd, conformance)) continue; result.push_back(vd); } @@ -782,18 +810,18 @@ static void checkAndDiagnoseImplicitNoDerivative(ASTContext &Context, continue; // Check whether to diagnose stored property. auto varType = DC->mapTypeIntoContext(vd->getValueInterfaceType()); - bool conformsToDifferentiable = - !TypeChecker::conformsToProtocol(varType, diffableProto, nominal) - .isInvalid(); + auto diffableConformance = + TypeChecker::conformsToProtocol(varType, diffableProto, nominal); // If stored property should not be diagnosed, continue. - if (conformsToDifferentiable && !vd->isLet()) + if (diffableConformance && + canInvokeMoveAlongOnProperty(vd, diffableConformance)) continue; // Otherwise, add an implicit `@noDerivative` attribute. vd->getAttrs().add(new (Context) NoDerivativeAttr(/*Implicit*/ true)); auto loc = vd->getAttributeInsertionLoc(/*forModifier*/ false); assert(loc.isValid() && "Expected valid source location"); // Diagnose properties that do not conform to `Differentiable`. - if (!conformsToDifferentiable) { + if (!diffableConformance) { Context.Diags .diagnose( loc, diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 94478abf4f50f..df4080e85a1ba 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2901,18 +2901,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { auto nominal = evaluateOrDefault( Ctx.evaluator, CustomAttrNominalRequest{attr, dc}, nullptr); - // If there is no nominal type with this name, complain about this being - // an unknown attribute. if (!nominal) { - std::string typeName; - if (auto typeRepr = attr->getTypeRepr()) { - llvm::raw_string_ostream out(typeName); - typeRepr->print(out); - } else { - typeName = attr->getType().getString(); - } - - diagnose(attr->getLocation(), diag::unknown_attribute, typeName); attr->setInvalid(); return; } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index ed98ea9976f26..724aef5da6b76 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5413,16 +5413,12 @@ class TypeDeserializer { if (!diffKind.hasValue()) MF.fatal(); - const clang::FunctionType *clangFunctionType = nullptr; + const clang::Type *clangFunctionType = nullptr; if (clangFunctionTypeID) { auto clangType = MF.getClangType(clangFunctionTypeID); if (!clangType) return clangType.takeError(); - // FIXME: allow block pointers here. - clangFunctionType = - dyn_cast_or_null(clangType.get()); - if (!clangFunctionType) - MF.fatal(); + clangFunctionType = clangType.get(); } auto extInfo = diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index 772afeb1e98f2..d811f53d89036 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -135,7 +135,8 @@ lookupTypeWitness(const std::string &MangledTypeName, auto SubstitutedTypeName = readTypeRef(AssocTy, AssocTy->SubstitutedTypeName); auto Demangled = demangleTypeRef(SubstitutedTypeName); - auto *TypeWitness = swift::Demangle::decodeMangledType(*this, Demangled); + auto *TypeWitness = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); AssociatedTypeCache.insert(std::make_pair(key, TypeWitness)); return TypeWitness; @@ -155,7 +156,8 @@ lookupSuperclass(const TypeRef *TR) { return nullptr; auto Demangled = demangleTypeRef(readTypeRef(FD, FD->Superclass)); - auto Unsubstituted = swift::Demangle::decodeMangledType(*this, Demangled); + auto Unsubstituted = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); if (!Unsubstituted) return nullptr; @@ -226,7 +228,8 @@ bool TypeRefBuilder::getFieldTypeRefs( } auto Demangled = demangleTypeRef(readTypeRef(Field,Field->MangledTypeName)); - auto Unsubstituted = swift::Demangle::decodeMangledType(*this, Demangled); + auto Unsubstituted = + swift::Demangle::decodeMangledType(*this, Demangled).getType(); if (!Unsubstituted) return false; @@ -304,7 +307,7 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { if (CR->hasMangledTypeName()) { auto MangledName = readTypeRef(CR, CR->MangledTypeName); auto DemangleTree = demangleTypeRef(MangledName); - TR = swift::Demangle::decodeMangledType(*this, DemangleTree); + TR = swift::Demangle::decodeMangledType(*this, DemangleTree).getType(); } Info.CaptureTypes.push_back(TR); } @@ -316,7 +319,7 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef CD) { if (MSR->hasMangledTypeName()) { auto MangledName = readTypeRef(MSR, MSR->MangledTypeName); auto DemangleTree = demangleTypeRef(MangledName); - TR = swift::Demangle::decodeMangledType(*this, DemangleTree); + TR = swift::Demangle::decodeMangledType(*this, DemangleTree).getType(); } const MetadataSource *MS = nullptr; @@ -344,12 +347,17 @@ TypeRefBuilder::dumpTypeRef(RemoteRef MangledName, auto DemangleTree = demangleTypeRef(MangledName); auto TypeName = nodeToString(DemangleTree); fprintf(file, "%s\n", TypeName.c_str()); - auto TR = swift::Demangle::decodeMangledType(*this, DemangleTree); - if (!TR) { + auto Result = swift::Demangle::decodeMangledType(*this, DemangleTree); + if (Result.isError()) { + auto *Error = Result.getError(); + char *ErrorStr = Error->copyErrorString(); auto str = getTypeRefString(MangledName); - fprintf(file, "!!! Invalid typeref: %s\n", std::string(str.begin(), str.end()).c_str()); + fprintf(file, "!!! Invalid typeref: %s - %s\n", + std::string(str.begin(), str.end()).c_str(), ErrorStr); + Error->freeErrorString(ErrorStr); return; } + auto TR = Result.getType(); TR->dump(file); fprintf(file, "\n"); } diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 92853104c25ea..b10f8981149be 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -298,7 +298,7 @@ swift_reflection_typeRefForMangledTypeName(SwiftReflectionContextRef ContextRef, const char *MangledTypeName, uint64_t Length) { auto Context = ContextRef->nativeContext; - auto TR = Context->readTypeFromMangledName(MangledTypeName, Length); + auto TR = Context->readTypeFromMangledName(MangledTypeName, Length).getType(); return reinterpret_cast(TR); } diff --git a/stdlib/public/core/Integers.swift b/stdlib/public/core/Integers.swift index 7c637c6fe7bc9..1b7ba775989ff 100644 --- a/stdlib/public/core/Integers.swift +++ b/stdlib/public/core/Integers.swift @@ -1211,7 +1211,7 @@ public protocol BinaryInteger : /// /// - Parameter rhs: The value to divide this value by. /// - Returns: A tuple containing the quotient and remainder of this value - /// divided by `rhs`. The remainder has the same sign as `rhs`. + /// divided by `rhs`. The remainder has the same sign as `lhs`. func quotientAndRemainder(dividingBy rhs: Self) -> (quotient: Self, remainder: Self) diff --git a/stdlib/public/runtime/CompatibilityOverride.def b/stdlib/public/runtime/CompatibilityOverride.def index 40f6a055b3857..1489ee07e1eeb 100644 --- a/stdlib/public/runtime/CompatibilityOverride.def +++ b/stdlib/public/runtime/CompatibilityOverride.def @@ -142,7 +142,7 @@ OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::, (const void *pattern, const void *arguments), (pattern, arguments)) -OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift::, +OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeLookupErrorOr, , SWIFT_CC(swift), swift::, (MetadataRequest request, Demangler &demangler, Demangle::NodePointer node, @@ -150,7 +150,7 @@ OVERRIDE_METADATALOOKUP(getTypeByMangledNode, TypeInfo, , SWIFT_CC(swift), swift SubstGenericParameterFn substGenericParam, SubstDependentWitnessTableFn substWitnessTable), (request, demangler, node, arguments, substGenericParam, substWitnessTable)) -OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeInfo, , SWIFT_CC(swift), swift::, +OVERRIDE_METADATALOOKUP(getTypeByMangledName, TypeLookupErrorOr, , SWIFT_CC(swift), swift::, (MetadataRequest request, StringRef typeName, const void * const *arguments, diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 1dfe45dd8c62f..a8e9f2a1b81eb 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -2916,24 +2916,22 @@ getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self) { StringRef superclassName = Demangle::makeSymbolicMangledNameStringRef(superclassNameBase); SubstGenericParametersFromMetadata substitutions(self); - MetadataResponse response = - swift_getTypeByMangledName(request, superclassName, - substitutions.getGenericArgs(), + auto result = swift_getTypeByMangledName( + request, superclassName, substitutions.getGenericArgs(), [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getResponse(); - auto superclass = response.Value; - if (!superclass) { - fatalError(0, - "failed to demangle superclass of %s from mangled name '%s'\n", - self->getDescription()->Name.get(), - superclassName.str().c_str()); + }); + if (auto *error = result.getError()) { + fatalError( + 0, "failed to demangle superclass of %s from mangled name '%s': %s\n", + self->getDescription()->Name.get(), superclassName.str().c_str(), + error->copyErrorString()); } - return response; + return result.getType().getResponse(); } else { return MetadataResponse(); } @@ -4928,12 +4926,12 @@ swift_getAssociatedTypeWitnessSlowImpl( Demangle::makeSymbolicMangledNameStringRef(mangledNameBase); // Demangle the associated type. - MetadataResponse response; + TypeLookupErrorOr result = TypeInfo(); if (inProtocolContext) { // The protocol's Self is the only generic parameter that can occur in the // type. - response = - swift_getTypeByMangledName(request, mangledName, nullptr, + result = swift_getTypeByMangledName( + request, mangledName, nullptr, [conformingType](unsigned depth, unsigned index) -> const Metadata * { if (depth == 0 && index == 0) return conformingType; @@ -4950,7 +4948,7 @@ swift_getAssociatedTypeWitnessSlowImpl( return swift_getAssociatedConformanceWitness(wtable, conformingType, type, reqBase, dependentDescriptor); - }).getResponse(); + }); } else { // The generic parameters in the associated type name are those of the // conforming type. @@ -4960,29 +4958,30 @@ swift_getAssociatedTypeWitnessSlowImpl( auto originalConformingType = findConformingSuperclass(conformingType, conformance); SubstGenericParametersFromMetadata substitutions(originalConformingType); - response = swift_getTypeByMangledName(request, mangledName, - substitutions.getGenericArgs(), - [&substitutions](unsigned depth, unsigned index) { - return substitutions.getMetadata(depth, index); - }, - [&substitutions](const Metadata *type, unsigned index) { - return substitutions.getWitnessTable(type, index); - }).getResponse(); + result = swift_getTypeByMangledName( + request, mangledName, substitutions.getGenericArgs(), + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }); } + auto *error = result.getError(); + MetadataResponse response = result.getType().getResponse(); auto assocTypeMetadata = response.Value; - - if (!assocTypeMetadata) { + if (error || !assocTypeMetadata) { + const char *errStr = error ? error->copyErrorString() + : "NULL metadata but no error was provided"; auto conformingTypeNameInfo = swift_getTypeName(conformingType, true); StringRef conformingTypeName(conformingTypeNameInfo.data, conformingTypeNameInfo.length); StringRef assocTypeName = findAssociatedTypeName(protocol, assocType); fatalError(0, "failed to demangle witness for associated type '%s' in " - "conformance '%s: %s' from mangled name '%s'\n", - assocTypeName.str().c_str(), - conformingTypeName.str().c_str(), - protocol->Name.get(), - mangledName.str().c_str()); + "conformance '%s: %s' from mangled name '%s' - %s\n", + assocTypeName.str().c_str(), conformingTypeName.str().c_str(), + protocol->Name.get(), mangledName.str().c_str(), errStr); } assert((uintptr_t(assocTypeMetadata) & @@ -5935,7 +5934,7 @@ void swift::verifyMangledNameRoundtrip(const Metadata *metadata) { nullptr, [](unsigned, unsigned){ return nullptr; }, [](const Metadata *, unsigned) { return nullptr; }) - .getMetadata(); + .getType().getMetadata(); if (metadata != result) swift::warning(RuntimeErrorFlagNone, "Metadata mangled name failed to roundtrip: %p -> %s -> %p\n", diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 93ed4227928eb..c0c2f7c0c4c9d 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -961,13 +961,54 @@ getLocalGenericParams(const ContextDescriptor *context) { return genericContext->getGenericParams().slice(startParamIndex); } -static bool +static llvm::Optional _gatherGenericParameters(const ContextDescriptor *context, llvm::ArrayRef genericArgs, const Metadata *parent, llvm::SmallVectorImpl &genericParamCounts, llvm::SmallVectorImpl &allGenericArgsVec, Demangler &demangler) { + auto makeCommonErrorStringGetter = [&] { + auto metadataVector = genericArgs.vec(); + return [=] { + std::string str; + + str += "_gatherGenericParameters: context: "; + + SymbolInfo contextInfo; + if (lookupSymbol(context, &contextInfo)) { + str += contextInfo.symbolName.get(); + str += " "; + } + + char *contextStr; + swift_asprintf(&contextStr, "%p", context); + str += contextStr; + free(contextStr); + + str += " <"; + + bool first = true; + for (const Metadata *metadata : genericArgs) { + if (!first) + str += ", "; + first = false; + str += nameForMetadata(metadata); + } + + str += "> "; + + str += "parent: "; + if (parent) + str += nameForMetadata(parent); + else + str += ""; + str += " - "; + + return str; + }; + }; + // Figure out the various levels of generic parameters we have in // this type. (void)_gatherGenericParameterCounts(context, @@ -981,7 +1022,15 @@ _gatherGenericParameters(const ContextDescriptor *context, } else if (genericArgs.size() == numTotalGenericParams && !parent) { // Okay: genericArgs is the complete set of generic arguments. } else { - return false; + auto commonString = makeCommonErrorStringGetter(); + auto genericArgsSize = genericArgs.size(); + return TypeLookupError([=] { + return commonString() + "incorrect number of generic args (" + + std::to_string(genericArgsSize) + "), " + + std::to_string(getLocalGenericParams(context).size()) + + " local params, " + std::to_string(numTotalGenericParams) + + " total params"; + }); } // If there are generic parameters at any level, check the generic @@ -1008,15 +1057,30 @@ _gatherGenericParameters(const ContextDescriptor *context, auto genericParams = generics->getGenericParams(); unsigned n = genericParams.size(); if (allGenericArgs.size() != n) { - return false; + auto commonString = makeCommonErrorStringGetter(); + auto argsVecSize = allGenericArgsVec.size(); + return TypeLookupError([=] { + return commonString() + "have " + std::to_string(argsVecSize) + + "generic args, expected " + std::to_string(n); + }); } for (unsigned i = 0; i != n; ++i) { const auto ¶m = genericParams[i]; - if (param.getKind() != GenericParamKind::Type) - return false; - if (param.hasExtraArgument()) - return false; - + if (param.getKind() != GenericParamKind::Type) { + auto commonString = makeCommonErrorStringGetter(); + return TypeLookupError([=] { + return commonString() + "param " + std::to_string(i) + + " has unexpected kind " + + std::to_string(static_cast(param.getKind())); + }); + } + if (param.hasExtraArgument()) { + auto commonString = makeCommonErrorStringGetter(); + return TypeLookupError([=] { + return commonString() + "param " + std::to_string(i) + + "has extra argument"; + }); + } if (param.hasKeyArgument()) allGenericArgsVec.push_back(allGenericArgs[i]); } @@ -1028,26 +1092,33 @@ _gatherGenericParameters(const ContextDescriptor *context, // any extra arguments we need for the instantiation function. SubstGenericParametersFromWrittenArgs substitutions(allGenericArgs, genericParamCounts); - bool failed = - _checkGenericRequirements(generics->getGenericRequirements(), - allGenericArgsVec, + auto error = _checkGenericRequirements( + generics->getGenericRequirements(), allGenericArgsVec, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); - if (failed) - return false; + if (error) + return *error; // If we still have the wrong number of generic arguments, this is // some kind of metadata mismatch. if (generics->getGenericContextHeader().getNumArguments() != - allGenericArgsVec.size()) - return false; + allGenericArgsVec.size()) { + auto commonString = makeCommonErrorStringGetter(); + auto argsVecSize = allGenericArgsVec.size(); + return TypeLookupError([=] { + return commonString() + "generic argument count mismatch, expected " + + std::to_string( + generics->getGenericContextHeader().getNumArguments()) + + ", have " + std::to_string(argsVecSize); + }); + } } - return true; + return llvm::None; } namespace { @@ -1175,7 +1246,7 @@ class DecodedMetadataBuilder { Demangle::NodeFactory &getNodeFactory() { return demangler; } - BuiltType + TypeLookupErrorOr resolveOpaqueType(NodePointer opaqueDecl, llvm::ArrayRef> genericArgs, unsigned ordinal) { @@ -1193,12 +1264,10 @@ class DecodedMetadataBuilder { llvm::SmallVector genericParamCounts; llvm::SmallVector allGenericArgsVec; - if (!_gatherGenericParameters(outerContext, - allGenericArgs, - BuiltType(), /* no parent */ - genericParamCounts, allGenericArgsVec, - demangler)) - return BuiltType(); + if (auto error = _gatherGenericParameters( + outerContext, allGenericArgs, BuiltType(), /* no parent */ + genericParamCounts, allGenericArgsVec, demangler)) + return *error; auto mangledName = descriptor->getUnderlyingTypeArgument(ordinal); SubstGenericParametersFromMetadata substitutions(descriptor, @@ -1210,7 +1279,7 @@ class DecodedMetadataBuilder { }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } BuiltTypeDecl createTypeDecl(NodePointer node, @@ -1245,8 +1314,9 @@ class DecodedMetadataBuilder { return ProtocolDescriptorRef(); #endif } - - BuiltType createObjCClassType(const std::string &mangledName) const { + + TypeLookupErrorOr + createObjCClassType(const std::string &mangledName) const { #if SWIFT_OBJC_INTEROP auto objcClass = objc_getClass(mangledName.c_str()); return swift_getObjCClassMetadata((const ClassMetadata *)objcClass); @@ -1255,7 +1325,7 @@ class DecodedMetadataBuilder { #endif } - BuiltType + TypeLookupErrorOr createBoundGenericObjCClassType(const std::string &mangledName, llvm::ArrayRef args) const { // Generic arguments of lightweight Objective-C generic classes are not @@ -1263,24 +1333,25 @@ class DecodedMetadataBuilder { return createObjCClassType(mangledName); } - BuiltType createNominalType(BuiltTypeDecl metadataOrTypeDecl, - BuiltType parent) const { + TypeLookupErrorOr + createNominalType(BuiltTypeDecl metadataOrTypeDecl, BuiltType parent) const { // Treat nominal type creation the same way as generic type creation, // but with no generic arguments at this level. return createBoundGenericType(metadataOrTypeDecl, { }, parent); } - BuiltType createTypeAliasType(BuiltTypeDecl typeAliasDecl, - BuiltType parent) const { + TypeLookupErrorOr createTypeAliasType(BuiltTypeDecl typeAliasDecl, + BuiltType parent) const { // We can't support sugared types here since we have no way to // resolve the underlying type of the type alias. However, some // CF types are mangled as type aliases. return createNominalType(typeAliasDecl, parent); } - BuiltType createBoundGenericType(BuiltTypeDecl anyTypeDecl, - llvm::ArrayRef genericArgs, - BuiltType parent) const { + TypeLookupErrorOr + createBoundGenericType(BuiltTypeDecl anyTypeDecl, + llvm::ArrayRef genericArgs, + BuiltType parent) const { auto typeDecl = dyn_cast(anyTypeDecl); if (!typeDecl) { if (auto protocol = dyn_cast(anyTypeDecl)) @@ -1294,13 +1365,11 @@ class DecodedMetadataBuilder { llvm::SmallVector genericParamCounts; llvm::SmallVector allGenericArgsVec; - if (!_gatherGenericParameters(typeDecl, - genericArgs, - parent, - genericParamCounts, allGenericArgsVec, - demangler)) - return BuiltType(); - + if (auto error = _gatherGenericParameters(typeDecl, genericArgs, parent, + genericParamCounts, + allGenericArgsVec, demangler)) + return *error; + // Call the access function. auto accessFunction = typeDecl->getAccessFunction(); if (!accessFunction) return BuiltType(); @@ -1308,8 +1377,8 @@ class DecodedMetadataBuilder { return accessFunction(MetadataState::Abstract, allGenericArgsVec).Value; } - BuiltType createBuiltinType(StringRef builtinName, - StringRef mangledName) const { + TypeLookupErrorOr createBuiltinType(StringRef builtinName, + StringRef mangledName) const { #define BUILTIN_TYPE(Symbol, _) \ if (mangledName.equals(#Symbol)) \ return &METADATA_SYM(Symbol).base; @@ -1317,19 +1386,19 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType createMetatypeType( + TypeLookupErrorOr createMetatypeType( BuiltType instance, llvm::Optional repr = None) const { return swift_getMetatypeMetadata(instance); } - BuiltType createExistentialMetatypeType( + TypeLookupErrorOr createExistentialMetatypeType( BuiltType instance, llvm::Optional repr = None) const { return swift_getExistentialMetatypeMetadata(instance); } - BuiltType + TypeLookupErrorOr createProtocolCompositionType(llvm::ArrayRef protocols, BuiltType superclass, bool isClassBound) const { // Determine whether we have a class bound. @@ -1349,13 +1418,13 @@ class DecodedMetadataBuilder { protocols.size(), protocols.data()); } - BuiltType createDynamicSelfType(BuiltType selfType) const { + TypeLookupErrorOr createDynamicSelfType(BuiltType selfType) const { // Free-standing mangled type strings should not contain DynamicSelfType. return BuiltType(); } - BuiltType createGenericTypeParameterType(unsigned depth, - unsigned index) const { + TypeLookupErrorOr + createGenericTypeParameterType(unsigned depth, unsigned index) const { // Use the callback, when provided. if (substGenericParameter) return substGenericParameter(depth, index); @@ -1363,7 +1432,7 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType + TypeLookupErrorOr createFunctionType(llvm::ArrayRef> params, BuiltType result, FunctionTypeFlags flags) const { llvm::SmallVector paramTypes; @@ -1386,7 +1455,7 @@ class DecodedMetadataBuilder { result); } - BuiltType createImplFunctionType( + TypeLookupErrorOr createImplFunctionType( Demangle::ImplParameterConvention calleeConvention, llvm::ArrayRef> params, llvm::ArrayRef> results, @@ -1396,8 +1465,9 @@ class DecodedMetadataBuilder { return BuiltType(); } - BuiltType createTupleType(llvm::ArrayRef elements, - std::string labels) const { + TypeLookupErrorOr + createTupleType(llvm::ArrayRef elements, + std::string labels) const { auto flags = TupleTypeFlags().withNumElements(elements.size()); if (!labels.empty()) flags = flags.withNonConstantLabels(true); @@ -1408,13 +1478,15 @@ class DecodedMetadataBuilder { .Value; } - BuiltType createDependentMemberType(StringRef name, BuiltType base) const { + TypeLookupErrorOr createDependentMemberType(StringRef name, + BuiltType base) const { // Should not have unresolved dependent member types here. return BuiltType(); } - BuiltType createDependentMemberType(StringRef name, BuiltType base, - BuiltProtocolDecl protocol) const { + TypeLookupErrorOr + createDependentMemberType(StringRef name, BuiltType base, + BuiltProtocolDecl protocol) const { #if SWIFT_OBJC_INTEROP if (protocol.isObjC()) return BuiltType(); @@ -1438,14 +1510,14 @@ class DecodedMetadataBuilder { *assocType).Value; } -#define REF_STORAGE(Name, ...) \ - BuiltType create##Name##StorageType(BuiltType base) { \ - ReferenceOwnership.set##Name(); \ - return base; \ +#define REF_STORAGE(Name, ...) \ + TypeLookupErrorOr create##Name##StorageType(BuiltType base) { \ + ReferenceOwnership.set##Name(); \ + return base; \ } #include "swift/AST/ReferenceStorage.def" - BuiltType createSILBoxType(BuiltType base) const { + TypeLookupErrorOr createSILBoxType(BuiltType base) const { // FIXME: Implement. return BuiltType(); } @@ -1454,22 +1526,23 @@ class DecodedMetadataBuilder { return ReferenceOwnership; } - BuiltType createOptionalType(BuiltType base) { + TypeLookupErrorOr createOptionalType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createArrayType(BuiltType base) { + TypeLookupErrorOr createArrayType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createDictionaryType(BuiltType key, BuiltType value) { + TypeLookupErrorOr createDictionaryType(BuiltType key, + BuiltType value) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } - BuiltType createParenType(BuiltType base) { + TypeLookupErrorOr createParenType(BuiltType base) { // Mangled types for building metadata don't contain sugared types return BuiltType(); } @@ -1478,13 +1551,12 @@ class DecodedMetadataBuilder { } SWIFT_CC(swift) -static TypeInfo swift_getTypeByMangledNodeImpl( - MetadataRequest request, - Demangler &demangler, - Demangle::NodePointer node, - const void * const *origArgumentVector, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +static TypeLookupErrorOr +swift_getTypeByMangledNodeImpl(MetadataRequest request, Demangler &demangler, + Demangle::NodePointer node, + const void *const *origArgumentVector, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { // Simply call an accessor function if that's all we got. if (node->getKind() == Node::Kind::AccessorFunctionReference) { // The accessor function is passed the pointer to the original argument @@ -1504,22 +1576,23 @@ static TypeInfo swift_getTypeByMangledNodeImpl( DecodedMetadataBuilder builder(demangler, substGenericParam, substWitnessTable); auto type = Demangle::decodeMangledType(builder, node); - if (!type) { - return {MetadataResponse{nullptr, MetadataState::Complete}, - TypeReferenceOwnership()}; + if (type.isError()) { + return *type.getError(); + } + if (!type.getType()) { + return TypeLookupError("NULL type but no error provided"); } - return {swift_checkMetadataState(request, type), - builder.getReferenceOwnership()}; + return TypeInfo{swift_checkMetadataState(request, type.getType()), + builder.getReferenceOwnership()}; } SWIFT_CC(swift) -static TypeInfo swift_getTypeByMangledNameImpl( - MetadataRequest request, - StringRef typeName, - const void * const *origArgumentVector, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +static TypeLookupErrorOr +swift_getTypeByMangledNameImpl(MetadataRequest request, StringRef typeName, + const void *const *origArgumentVector, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { DemanglerForRuntimeTypeResolution> demangler; NodePointer node; @@ -1587,7 +1660,7 @@ swift_getTypeByMangledNameInEnvironment( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1607,7 +1680,7 @@ swift_getTypeByMangledNameInEnvironmentInMetadataState( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1626,7 +1699,7 @@ swift_getTypeByMangledNameInContext( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1646,7 +1719,7 @@ swift_getTypeByMangledNameInContextInMetadataState( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); } /// Demangle a mangled name, but don't allow symbolic references. @@ -1661,7 +1734,7 @@ swift_stdlib_getTypeByMangledNameUntrusted(const char *typeNameStart, } return swift_getTypeByMangledName(MetadataState::Complete, typeName, nullptr, - {}, {}).getMetadata(); + {}, {}).getType().getMetadata(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1680,7 +1753,7 @@ swift_getOpaqueTypeMetadata(MetadataRequest request, }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getResponse(); + }).getType().getResponse(); } SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT @@ -1735,7 +1808,7 @@ getObjCClassByMangledName(const char * _Nonnull typeName, }, [&](const Metadata *type, unsigned index) { return nullptr; - }).getMetadata(); + }).getType().getMetadata(); } else { metadata = swift_stdlib_getTypeByMangledNameUntrusted(typeStr.data(), typeStr.size()); @@ -2068,7 +2141,7 @@ void swift::gatherWrittenGenericArgs( }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); - }).getMetadata(); + }).getType().getMetadata(); continue; } diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index c46e8ff9a9b38..2f5e7232cc55d 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -17,7 +17,10 @@ #ifndef SWIFT_RUNTIME_PRIVATE_H #define SWIFT_RUNTIME_PRIVATE_H +#include + #include "swift/Demangling/Demangler.h" +#include "swift/Demangling/TypeLookupError.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Metadata.h" @@ -77,6 +80,8 @@ class TypeInfo { const Metadata *getMetadata() const { return Response.Value; } MetadataResponse getResponse() const { return Response; } + operator bool() const { return getMetadata(); } + #define REF_STORAGE(Name, ...) \ bool is##Name() const { return ReferenceOwnership.is##Name(); } #include "swift/AST/ReferenceStorage.def" @@ -369,7 +374,7 @@ class TypeInfo { /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. SWIFT_CC(swift) - TypeInfo swift_getTypeByMangledNode( + TypeLookupErrorOr swift_getTypeByMangledNode( MetadataRequest request, Demangler &demangler, Demangle::NodePointer node, @@ -384,7 +389,7 @@ class TypeInfo { /// \p substWitnessTable Function that provides witness tables given a /// particular dependent conformance index. SWIFT_CC(swift) - TypeInfo swift_getTypeByMangledName( + TypeLookupErrorOr swift_getTypeByMangledName( MetadataRequest request, StringRef typeName, const void * const *arguments, @@ -447,12 +452,12 @@ class TypeInfo { /// generic requirements (e.g., those that need to be /// passed to an instantiation function) will be added to this vector. /// - /// \returns true if an error occurred, false otherwise. - bool _checkGenericRequirements( - llvm::ArrayRef requirements, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable); + /// \returns the error if an error occurred, None otherwise. + llvm::Optional _checkGenericRequirements( + llvm::ArrayRef requirements, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable); /// A helper function which avoids performing a store if the destination /// address already contains the source value. This is useful when diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index e373d2a50848e..c434011faea85 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -165,15 +165,16 @@ ProtocolConformanceDescriptor::getWitnessTable(const Metadata *type) const { llvm::SmallVector conditionalArgs; if (hasConditionalRequirements()) { SubstGenericParametersFromMetadata substitutions(type); - bool failed = - _checkGenericRequirements(getConditionalRequirements(), conditionalArgs, + auto error = _checkGenericRequirements( + getConditionalRequirements(), conditionalArgs, [&substitutions](unsigned depth, unsigned index) { return substitutions.getMetadata(depth, index); }, [&substitutions](const Metadata *type, unsigned index) { return substitutions.getWitnessTable(type, index); }); - if (failed) return nullptr; + if (error) + return nullptr; } return swift_getWitnessTable(this, type, conditionalArgs.data()); @@ -642,31 +643,36 @@ static bool isSubclass(const Metadata *subclass, const Metadata *superclass) { }); } -bool swift::_checkGenericRequirements( - llvm::ArrayRef requirements, - llvm::SmallVectorImpl &extraArguments, - SubstGenericParameterFn substGenericParam, - SubstDependentWitnessTableFn substWitnessTable) { +llvm::Optional swift::_checkGenericRequirements( + llvm::ArrayRef requirements, + llvm::SmallVectorImpl &extraArguments, + SubstGenericParameterFn substGenericParam, + SubstDependentWitnessTableFn substWitnessTable) { for (const auto &req : requirements) { // Make sure we understand the requirement we're dealing with. - if (!req.hasKnownKind()) return true; + if (!req.hasKnownKind()) + return TypeLookupError("unknown kind"); // Resolve the subject generic parameter. - const Metadata *subjectType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getParam(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!subjectType) - return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getParam(), extraArguments.data(), + substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + const Metadata *subjectType = result.getType().getMetadata(); // Check the requirement. switch (req.getKind()) { case GenericRequirementKind::Protocol: { const WitnessTable *witnessTable = nullptr; if (!_conformsToProtocol(nullptr, subjectType, req.getProtocol(), - &witnessTable)) - return true; + &witnessTable)) { + const char *protoName = + req.getProtocol() ? req.getProtocol().getName() : ""; + return TypeLookupError( + "subject type %s does not conform to protocol %s", req.getParam(), + protoName); + } // If we need a witness table, add it. if (req.getProtocol().needsWitnessTable()) { @@ -679,17 +685,19 @@ bool swift::_checkGenericRequirements( case GenericRequirementKind::SameType: { // Demangle the second type under the given substitutions. - auto otherType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getMangledTypeName(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!otherType) return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto otherType = result.getType().getMetadata(); assert(!req.getFlags().hasExtraArgument()); // Check that the types are equivalent. - if (subjectType != otherType) return true; + if (subjectType != otherType) + return TypeLookupError("subject type %s does not match %s", + req.getParam(), req.getMangledTypeName()); continue; } @@ -698,22 +706,24 @@ bool swift::_checkGenericRequirements( switch (req.getLayout()) { case GenericRequirementLayoutKind::Class: if (!subjectType->satisfiesClassConstraint()) - return true; + return TypeLookupError( + "subject type %s does not satisfy class constraint", + req.getParam()); continue; } // Unknown layout. - return true; + return TypeLookupError("unknown layout kind %u", req.getLayout()); } case GenericRequirementKind::BaseClass: { // Demangle the base type under the given substitutions. - auto baseType = - swift_getTypeByMangledName(MetadataState::Abstract, - req.getMangledTypeName(), - extraArguments.data(), - substGenericParam, substWitnessTable).getMetadata(); - if (!baseType) return true; + auto result = swift_getTypeByMangledName( + MetadataState::Abstract, req.getMangledTypeName(), + extraArguments.data(), substGenericParam, substWitnessTable); + if (result.getError()) + return *result.getError(); + auto baseType = result.getType().getMetadata(); // If the type which is constrained to a base class is an existential // type, and if that existential type includes a superclass constraint, @@ -725,7 +735,8 @@ bool swift::_checkGenericRequirements( } if (!isSubclass(subjectType, baseType)) - return true; + return TypeLookupError("%s is not subclass of %s", req.getParam(), + req.getMangledTypeName()); continue; } @@ -737,11 +748,12 @@ bool swift::_checkGenericRequirements( } // Unknown generic requirement kind. - return true; + return TypeLookupError("unknown generic requirement kind %u", + req.getKind()); } // Success! - return false; + return llvm::None; } const Metadata *swift::findConformingSuperclass( diff --git a/stdlib/public/runtime/ReflectionMirror.mm b/stdlib/public/runtime/ReflectionMirror.mm index 821fecfb7fb0b..0206237d2b44b 100644 --- a/stdlib/public/runtime/ReflectionMirror.mm +++ b/stdlib/public/runtime/ReflectionMirror.mm @@ -400,27 +400,33 @@ static bool _shouldReportMissingReflectionMetadataWarnings() { auto typeName = field.getMangledTypeName(); SubstGenericParametersFromMetadata substitutions(base); - auto typeInfo = swift_getTypeByMangledName(MetadataState::Complete, - typeName, - substitutions.getGenericArgs(), - [&substitutions](unsigned depth, unsigned index) { - return substitutions.getMetadata(depth, index); - }, - [&substitutions](const Metadata *type, unsigned index) { - return substitutions.getWitnessTable(type, index); - }); + auto result = swift_getTypeByMangledName( + MetadataState::Complete, typeName, substitutions.getGenericArgs(), + [&substitutions](unsigned depth, unsigned index) { + return substitutions.getMetadata(depth, index); + }, + [&substitutions](const Metadata *type, unsigned index) { + return substitutions.getWitnessTable(type, index); + }); // If demangling the type failed, pretend it's an empty type instead with // a log message. - if (!typeInfo.getMetadata()) { + TypeInfo typeInfo; + if (result.isError()) { typeInfo = TypeInfo({&METADATA_SYM(EMPTY_TUPLE_MANGLING), MetadataState::Complete}, {}); + + auto *error = result.getError(); + char *str = error->copyErrorString(); missing_reflection_metadata_warning( - "warning: the Swift runtime was unable to demangle the type " - "of field '%*s'. the mangled type name is '%*s'. this field will " - "show up as an empty tuple in Mirrors\n", - (int)name.size(), name.data(), - (int)typeName.size(), typeName.data()); + "warning: the Swift runtime was unable to demangle the type " + "of field '%*s'. the mangled type name is '%*s': %s. this field will " + "show up as an empty tuple in Mirrors\n", + (int)name.size(), name.data(), (int)typeName.size(), typeName.data(), + str); + error->freeErrorString(str); + } else { + typeInfo = result.getType(); } auto fieldType = FieldType(typeInfo.getMetadata()); diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index f377a77d6f106..ba33dd19abd69 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -26,11 +26,13 @@ #include "llvm/ADT/StringRef.h" #include "swift/Basic/Lazy.h" #include "swift/Runtime/Casting.h" +#include "swift/Runtime/Debug.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/Heap.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/ObjCBridge.h" +#include "swift/Runtime/Portability.h" #include "swift/Strings.h" #include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/AssertionReporting.h" @@ -39,7 +41,6 @@ #include "Private.h" #include "SwiftObject.h" #include "WeakReference.h" -#include "swift/Runtime/Debug.h" #if SWIFT_OBJC_INTEROP #include #endif diff --git a/stdlib/public/stubs/Assert.cpp b/stdlib/public/stubs/Assert.cpp index 62135b2312b1c..fa0169a5ad6b8 100644 --- a/stdlib/public/stubs/Assert.cpp +++ b/stdlib/public/stubs/Assert.cpp @@ -12,6 +12,7 @@ #include "swift/Runtime/Config.h" #include "swift/Runtime/Debug.h" +#include "swift/Runtime/Portability.h" #include "../SwiftShims/AssertionReporting.h" #include #include diff --git a/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift b/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift index f4b50fb19fa21..228c782375eba 100644 --- a/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift +++ b/test/AutoDiff/Sema/DerivedConformances/class_differentiable.swift @@ -29,26 +29,62 @@ func testEmpty() { assertConformsToAdditiveArithmetic(Empty.TangentVector.self) } +protocol DifferentiableWithNonmutatingMoveAlong: Differentiable {} +extension DifferentiableWithNonmutatingMoveAlong { + func move(along _: TangentVector) {} +} + +class EmptyWithInheritedNonmutatingMoveAlong: DifferentiableWithNonmutatingMoveAlong { + typealias TangentVector = Empty.TangentVector + var zeroTangentVectorInitializer: () -> TangentVector { { .init() } } + static func proof_that_i_have_nonmutating_move_along() { + let empty = EmptyWithInheritedNonmutatingMoveAlong() + empty.move(along: .init()) + } +} + +class EmptyWrapper: Differentiable {} +func testEmptyWrapper() { + assertConformsToAdditiveArithmetic(Empty.TangentVector.self) + assertConformsToAdditiveArithmetic(EmptyWrapper.TangentVector.self) +} + // Test structs with `let` stored properties. // Derived conformances fail because `mutating func move` requires all stored // properties to be mutable. -class ImmutableStoredProperties: Differentiable { +class ImmutableStoredProperties: Differentiable { var okay: Float // expected-warning @+1 {{stored property 'nondiff' has no derivative because 'Int' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let nondiff: Int - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let diff: Float - init() { + let letClass: Empty // No error on class-bound differentiable `let` with a non-mutating 'move(along:)'. + + let letClassWithInheritedNonmutatingMoveAlong: EmptyWithInheritedNonmutatingMoveAlong + + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + let letClassGeneric: T // Error due to lack of non-mutating 'move(along:)'. + + let letClassWrappingGeneric: EmptyWrapper // No error on class-bound differentiable `let` with a non-mutating 'move(along:)'. + + init(letClassGeneric: T) { okay = 0 nondiff = 0 diff = 0 + letClass = Empty() + self.letClassGeneric = letClassGeneric + self.letClassWrappingGeneric = EmptyWrapper() } } func testImmutableStoredProperties() { - _ = ImmutableStoredProperties.TangentVector(okay: 1) + _ = ImmutableStoredProperties.TangentVector( + okay: 1, + letClass: Empty.TangentVector(), + letClassWithInheritedNonmutatingMoveAlong: Empty.TangentVector(), + letClassWrappingGeneric: EmptyWrapper.TangentVector()) } class MutableStoredPropertiesWithInitialValue: Differentiable { var x = Float(1) @@ -56,7 +92,7 @@ class MutableStoredPropertiesWithInitialValue: Differentiable { } // Test class with both an empty constructor and memberwise initializer. class AllMixedStoredPropertiesHaveInitialValue: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let x = Float(1) var y = Float(1) // Memberwise initializer should be `init(y:)` since `x` is immutable. @@ -550,7 +586,7 @@ struct Generic {} extension Generic: Differentiable where T: Differentiable {} class WrappedProperties: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable; add an explicit '@noDerivative' attribute}} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable or have a non-mutating 'move(along:)'; add an explicit '@noDerivative' attribute}} @ImmutableWrapper var immutableInt: Generic = Generic() // expected-warning @+1 {{stored property 'mutableInt' has no derivative because 'Generic' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} diff --git a/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift b/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift index e75b2730232f5..d8ec6e56c588f 100644 --- a/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift +++ b/test/AutoDiff/Sema/DerivedConformances/struct_differentiable.swift @@ -11,6 +11,35 @@ func testEmpty() { assertConformsToAdditiveArithmetic(Empty.TangentVector.self) } +struct EmptyWithConcreteNonmutatingMoveAlong: Differentiable { + typealias TangentVector = Empty.TangentVector + var zeroTangentVectorInitializer: () -> TangentVector { { .init() } } + func move(along _: TangentVector) {} + static func proof_that_i_have_nonmutating_move_along() { + let empty = Self() + empty.move(along: .init()) + } +} + +protocol DifferentiableWithNonmutatingMoveAlong: Differentiable {} +extension DifferentiableWithNonmutatingMoveAlong { + func move(along _: TangentVector) {} +} + +struct EmptyWithInheritedNonmutatingMoveAlong: DifferentiableWithNonmutatingMoveAlong { + typealias TangentVector = Empty.TangentVector + var zeroTangentVectorInitializer: () -> TangentVector { { .init() } } + static func proof_that_i_have_nonmutating_move_along() { + let empty = Self() + empty.move(along: .init()) + } +} + +class EmptyClass: Differentiable {} +func testEmptyClass() { + assertConformsToAdditiveArithmetic(EmptyClass.TangentVector.self) +} + // Test interaction with `AdditiveArithmetic` derived conformances. // Previously, this crashed due to duplicate memberwise initializer synthesis. struct EmptyAdditiveArithmetic: AdditiveArithmetic, Differentiable {} @@ -21,14 +50,24 @@ struct EmptyAdditiveArithmetic: AdditiveArithmetic, Differentiable {} struct ImmutableStoredProperties: Differentiable { var okay: Float - // expected-warning @+1 {{stored property 'nondiff' has no derivative because 'Int' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute, or conform 'ImmutableStoredProperties' to 'AdditiveArithmetic'}} {{3-3=@noDerivative }} + // expected-warning @+1 {{stored property 'nondiff' has no derivative because 'Int' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let nondiff: Int - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute, or conform 'ImmutableStoredProperties' to 'AdditiveArithmetic}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'ImmutableStoredProperties' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let diff: Float + + let nonmutatingMoveAlongStruct: EmptyWithConcreteNonmutatingMoveAlong + + let inheritedNonmutatingMoveAlongStruct: EmptyWithInheritedNonmutatingMoveAlong + + let diffClass: EmptyClass // No error on class-bound `let` with a non-mutating `move(along:)`. } func testImmutableStoredProperties() { - _ = ImmutableStoredProperties.TangentVector(okay: 1) + _ = ImmutableStoredProperties.TangentVector( + okay: 1, + nonmutatingMoveAlongStruct: Empty.TangentVector(), + inheritedNonmutatingMoveAlongStruct: Empty.TangentVector(), + diffClass: EmptyClass.TangentVector()) } struct MutableStoredPropertiesWithInitialValue: Differentiable { var x = Float(1) @@ -36,7 +75,7 @@ struct MutableStoredPropertiesWithInitialValue: Differentiable { } // Test struct with both an empty constructor and memberwise initializer. struct AllMixedStoredPropertiesHaveInitialValue: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'AllMixedStoredPropertiesHaveInitialValue' requires all stored properties not marked with `@noDerivative` to be mutable or have a non-mutating 'move(along:)'; use 'var' instead, or add an explicit '@noDerivative' attribute}} {{3-3=@noDerivative }} let x = Float(1) var y = Float(1) // Memberwise initializer should be `init(y:)` since `x` is immutable. @@ -363,7 +402,7 @@ struct Generic {} extension Generic: Differentiable where T: Differentiable {} struct WrappedProperties: Differentiable { - // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable; add an explicit '@noDerivative' attribute}} + // expected-warning @+1 {{synthesis of the 'Differentiable.move(along:)' requirement for 'WrappedProperties' requires 'wrappedValue' in property wrapper 'ImmutableWrapper' to be mutable or have a non-mutating 'move(along:)'; add an explicit '@noDerivative' attribute}} @ImmutableWrapper var immutableInt: Generic // expected-warning @+1 {{stored property 'mutableInt' has no derivative because 'Generic' does not conform to 'Differentiable'; add an explicit '@noDerivative' attribute}} diff --git a/test/AutoDiff/validation-test/class_differentiation.swift b/test/AutoDiff/validation-test/class_differentiation.swift index 2ae7619486214..ee76c21ba1d87 100644 --- a/test/AutoDiff/validation-test/class_differentiation.swift +++ b/test/AutoDiff/validation-test/class_differentiation.swift @@ -524,4 +524,19 @@ ClassTests.test("ClassProperties") { gradient(at: Super(base: 2)) { foo in foo.squared }) } +ClassTests.test("LetProperties") { + final class Foo: Differentiable { + var x: Tracked + init(x: Tracked) { self.x = x } + } + final class Bar: Differentiable { + let x = Foo(x: 2) + } + let bar = Bar() + let grad = gradient(at: bar) { bar in (bar.x.x * bar.x.x).value } + expectEqual(Bar.TangentVector(x: .init(x: 6.0)), grad) + bar.move(along: grad) + expectEqual(8.0, bar.x.x) +} + runAllTests() diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift new file mode 100644 index 0000000000000..15b149a89c5dd --- /dev/null +++ b/test/ClangImporter/objc_async.swift @@ -0,0 +1,24 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify + +// REQUIRES: objc_interop +import Foundation +import ObjCConcurrency + +func testSlowServer(slowServer: SlowServer) async { + let _: Int = await slowServer.doSomethingSlow("mail") + let _: Bool = await slowServer.checkAvailability() + let _: String = await slowServer.findAnswer() ?? "nope" + let _: String = await slowServer.findAnswerFailingly() ?? "nope" + // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} + // FIXME: expected-error@-2{{call can throw, but it is not marked with 'try'}} + let _: Void = await slowServer.doSomethingFun("jump") + let _: (Int) -> Void = slowServer.completionHandler +} + +func testSlowServerOldSchool(slowServer: SlowServer) { + var i1: Int = 0 + slowServer.doSomethingSlow("mail") { i in + i1 = i + } + print(i1) +} diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index b6f99b7350729..0b77ce6ac87b2 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -1030,3 +1030,15 @@ func sr12815() { .doesntExist2() { $0 } } } + +// Make sure we can infer generic arguments in an explicit result type. +let explicitUnboundResult1 = { () -> Array in [0] } +let explicitUnboundResult2: (Array) -> Array = { + (arr: Array) -> Array in [0] +} +// FIXME: Should we prioritize the contextual result type and infer Array +// rather than using a type variable in these cases? +// expected-error@+1 {{unable to infer closure type in the current context}} +let explicitUnboundResult3: (Array) -> Array = { + (arr: Array) -> Array in [true] +} diff --git a/test/Driver/badJ.swift b/test/Driver/badJ.swift new file mode 100644 index 0000000000000..9f27b6735d52f --- /dev/null +++ b/test/Driver/badJ.swift @@ -0,0 +1,2 @@ +// RUN: not %swiftc_driver %S/../Inputs/empty.swift -jsnort 2>&1 | %FileCheck %s +// CHECK-NOT: Stack dump diff --git a/test/IDE/complete_exception.swift b/test/IDE/complete_exception.swift index 48d222ced7565..a5cef6bb5175b 100644 --- a/test/IDE/complete_exception.swift +++ b/test/IDE/complete_exception.swift @@ -5,11 +5,13 @@ // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH2 | %FileCheck %s -check-prefix=CATCH2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW2 | %FileCheck %s -check-prefix=THROW2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=CATCH3 | %FileCheck %s -check-prefix=CATCH3 +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=THROW3 | %FileCheck %s -check-prefix=THROW3 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH1 | %FileCheck %s -check-prefix=CATCH1 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW1 | %FileCheck %s -check-prefix=THROW1 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_CATCH2 | %FileCheck %s -check-prefix=CATCH2 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW2 | %FileCheck %s -check-prefix=THROW2 +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_THROW3 | %FileCheck %s -check-prefix=THROW3 // RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=INSIDE_CATCH1 > %t.inside_catch1 // RUN: %FileCheck %s -check-prefix=STMT < %t.inside_catch1 @@ -70,10 +72,10 @@ func test001() { do {} catch #^CATCH1^# // CATCH1: Begin completions -// CATCH1-DAG: Decl[Enum]/CurrModule: Error4[#Error4#]; name=Error4{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error3[#Error3#]; name=Error3{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error2[#Error2#]; name=Error2{{$}} -// CATCH1-DAG: Decl[Class]/CurrModule: Error1[#Error1#]; name=Error1{{$}} +// CATCH1-DAG: Decl[Enum]/CurrModule/TypeRelation[Convertible]: Error4[#Error4#]; name=Error4{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error3[#Error3#]; name=Error3{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error2[#Error2#]; name=Error2{{$}} +// CATCH1-DAG: Decl[Class]/CurrModule/TypeRelation[Convertible]: Error1[#Error1#]; name=Error1{{$}} // CATCH1-DAG: Keyword[let]/None: let{{; name=.+$}} // CATCH1-DAG: Decl[Class]/CurrModule: NoneError1[#NoneError1#]; name=NoneError1{{$}} // CATCH1-DAG: Decl[Class]/OtherModule[Foundation]/IsSystem: NSError[#NSError#]{{; name=.+$}} @@ -126,11 +128,20 @@ func test005() { // CATCH3: End completions } +func testInvalid() { + try throw Error4.#^THROW3^# +// THROW3: Begin completions +// THROW3: Decl[EnumElement]/CurrNominal: E1[#Error4#]{{; name=.+$}} +// THROW3: Decl[EnumElement]/CurrNominal: E2({#Int32#})[#Error4#]{{; name=.+$}} +// THROW3: End completions +} + //===--- Top-level throw/catch do {} catch #^TOP_LEVEL_CATCH1^# {} throw #^TOP_LEVEL_THROW1^# do {} catch Error4.#^TOP_LEVEL_CATCH2^# {} throw Error4.#^TOP_LEVEL_THROW2^# +try throw Error4.#^TOP_LEVEL_THROW3^# //===--- Inside catch body diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 80d50f2c3e4f1..53d20a8a07d36 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -109,9 +109,9 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERICPARAM_21 | %FileCheck %s -check-prefix=GENERICPARAM_1 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DECL_MEMBER_INIT_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3_NOTIDEAL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_1 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_2 | %FileCheck %s -check-prefix=UNRESOLVED_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=DEFAULT_ARG_3 | %FileCheck %s -check-prefix=UNRESOLVED_3 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPEPARAM_IN_CONTEXTTYPE_1 | %FileCheck %s -check-prefix=TYPEPARAM_IN_CONTEXTTYPE_1 diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift new file mode 100644 index 0000000000000..7d3029fff8be6 --- /dev/null +++ b/test/IDE/print_clang_objc_async.swift @@ -0,0 +1,20 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -source-filename %s -module-to-print=ObjCConcurrency -function-definitions=false -enable-experimental-concurrency > %t/ObjCConcurrency.printed.txt +// RUN: %FileCheck -input-file %t/ObjCConcurrency.printed.txt %s + +// REQUIRES: objc_interop + +// CHECK-LABEL: class SlowServer : NSObject { +// CHECK-DAG: func doSomethingSlow(_ operation: String, completionHandler handler: @escaping (Int) -> Void) +// CHECK-DAG: func doSomethingSlow(_ operation: String) async -> Int +// CHECK-DAG: func doSomethingDangerous(_ operation: String, completionHandler handler: ((String?, Error?) -> Void)? = nil) +// CHECK-DAG: func doSomethingDangerous(_ operation: String) async throws -> String? +// CHECK-DAG: func checkAvailability(completionHandler: @escaping (Bool) -> Void) +// CHECK-DAG: func checkAvailability() async -> Bool +// CHECK-DAG: func findAnswer(completionHandler handler: @escaping (String?, Error?) -> Void) +// CHECK-DAG: func findAnswer() async throws -> String? +// CHECK-DAG: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws +// CHECK-DAG: func findAnswerFailingly() async throws -> String? +// CHECK-DAG: func doSomethingFun(_ operation: String) async +// CHECK: {{^[}]$}} diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h new file mode 100644 index 0000000000000..b03e3530a4968 --- /dev/null +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -0,0 +1,16 @@ +@import Foundation; +@import ctypes; + +#pragma clang assume_nonnull begin + +@interface SlowServer : NSObject +-(void)doSomethingSlow:(NSString *)operation completionHandler:(void (^)(NSInteger))handler; +-(void)doSomethingDangerous:(NSString *)operation completionHandler:(void (^ _Nullable)(NSString *_Nullable, NSError * _Nullable))handler; +-(void)checkAvailabilityWithCompletionHandler:(void (^)(BOOL isAvailable))completionHandler; +-(void)findAnswerAsynchronously:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswer(completionHandler:)"))); +-(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)"))); +-(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler; +@property(readwrite) void (^completionHandler)(NSInteger); +@end + +#pragma clang assume_nonnull end diff --git a/test/Inputs/clang-importer-sdk/usr/include/module.map b/test/Inputs/clang-importer-sdk/usr/include/module.map index 61b062ab524eb..366101cbbb723 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/module.map +++ b/test/Inputs/clang-importer-sdk/usr/include/module.map @@ -136,3 +136,8 @@ module WinBOOL { header "winbool.h" export * } + +module ObjCConcurrency { + header "ObjCConcurrency.h" + export * +} \ No newline at end of file diff --git a/test/NameLookup/Inputs/custom_attrs_A.swift b/test/NameLookup/Inputs/custom_attrs_A.swift new file mode 100644 index 0000000000000..c8035b4fa167e --- /dev/null +++ b/test/NameLookup/Inputs/custom_attrs_A.swift @@ -0,0 +1,13 @@ +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +@_functionBuilder +public struct Builder { + static func buildBlock(_ component: T) -> T { component } +} diff --git a/test/NameLookup/Inputs/custom_attrs_B.swift b/test/NameLookup/Inputs/custom_attrs_B.swift new file mode 100644 index 0000000000000..c8035b4fa167e --- /dev/null +++ b/test/NameLookup/Inputs/custom_attrs_B.swift @@ -0,0 +1,13 @@ +@propertyWrapper +public struct Wrapper { + public var wrappedValue: Value + + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + } +} + +@_functionBuilder +public struct Builder { + static func buildBlock(_ component: T) -> T { component } +} diff --git a/test/NameLookup/custom_attrs_ambiguous.swift b/test/NameLookup/custom_attrs_ambiguous.swift new file mode 100644 index 0000000000000..b549e34e273ba --- /dev/null +++ b/test/NameLookup/custom_attrs_ambiguous.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_A.swift +// RUN: %target-swift-frontend -emit-module -I %t -o %t %S/Inputs/custom_attrs_B.swift +// RUN: %target-swift-frontend -typecheck -verify -verify-ignore-unknown -I %t %s +import custom_attrs_A +import custom_attrs_B + +// SR-13470 + +struct Test { + @Wrapper var x: Int = 17 + // expected-error@-1 {{ambiguous use of attribute 'Wrapper'}} + // expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Wrapper' in module 'custom_attrs_A'}} {{4-4=custom_attrs_A.}} + // expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Wrapper' in module 'custom_attrs_B'}} {{4-4=custom_attrs_B.}} + + init(@Builder closure: () -> Int) {} + // expected-error@-1 {{ambiguous use of attribute 'Builder'}} + // expected-note@-2 {{use 'custom_attrs_A.' to reference the attribute 'Builder' in module 'custom_attrs_A'}} {{9-9=custom_attrs_A.}} + // expected-note@-3 {{use 'custom_attrs_B.' to reference the attribute 'Builder' in module 'custom_attrs_B'}} {{9-9=custom_attrs_B.}} +} + diff --git a/test/Sema/editor_placeholders.swift b/test/Sema/editor_placeholders.swift index dedeb59a440db..cf14d23b74fd2 100644 --- a/test/Sema/editor_placeholders.swift +++ b/test/Sema/editor_placeholders.swift @@ -2,6 +2,7 @@ func foo(_ x: Int) -> Int {} func foo(_ x: Float) -> Float {} +func foo(_ t: T) -> T {} var v = foo(<#T##x: Float##Float#>) // expected-error {{editor placeholder}} v = "" // expected-error {{cannot assign value of type 'String' to type 'Float'}} @@ -36,3 +37,6 @@ func test_ambiguity_with_placeholders(pairs: [(rank: Int, count: Int)]) -> Bool // expected-error@-1 {{editor placeholder in source file}} // expected-error@-2 {{ambiguous use of 'subscript(_:)'}} } + +let unboundInPlaceholder1: Array = <#T##Array#> // expected-error{{editor placeholder in source file}} +let unboundInPlaceholder2: Array = foo(<#T##t: Array##Array#>) // expected-error{{editor placeholder in source file}} diff --git a/test/TypeDecoder/opaque_return_type.swift b/test/TypeDecoder/opaque_return_type.swift index 94b139ad48d1e..327e4860570d8 100644 --- a/test/TypeDecoder/opaque_return_type.swift +++ b/test/TypeDecoder/opaque_return_type.swift @@ -12,6 +12,12 @@ var prop: some P { return 0 } func bar() -> some Sequence { return [] } +struct G {} + +extension G where T == Int { + var baz: some P { return 0 } +} + // DEMANGLE: $s18opaque_return_type3fooQryFQOyQo_ // CHECK: some P @@ -21,5 +27,8 @@ func bar() -> some Sequence { return [] } // DEMANGLE: $s18opaque_return_type3barQryFQOyQo_ // CHECK: some Sequence +// DEMANGLE: $s18opaque_return_type1GVAASiRszlE3bazQrvpQOySi_Qo_ +// CHECK: some P + // DEMANGLE: $s18opaque_return_type3barQryFQOyQo_7ElementSTQxD // CHECK: (some Sequence).Element diff --git a/test/stdlib/Reflection_jit.swift b/test/stdlib/Reflection_jit.swift index c36b6b6e8cc9f..8704ba6069ceb 100644 --- a/test/stdlib/Reflection_jit.swift +++ b/test/stdlib/Reflection_jit.swift @@ -2,3 +2,8 @@ // RUN: %target-jit-run -parse-stdlib %S/Reflection.swift -- %S/Inputs/shuffle.jpg | %FileCheck %S/Reflection.swift // REQUIRES: swift_interpreter + +// Only run this test when we build host tools (e.g. bin/swift-frontend), avoid running it for standalone stdlib builds. +// Standalone stdlib builds use downloadable toolchains from swift.org, which are codesigned in a way that doesn't let +// the interpreter process (swift-frontend) dynamically load locally-built modules (libswiftCore). +// REQUIRES: swift_tools_extra diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 9ab571c111982..318fc77746dd7 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -192,6 +192,8 @@ static int run_driver(StringRef ExecName, if (C) { std::unique_ptr TQ = TheDriver.buildTaskQueue(*C); + if (!TQ) + return 1; return C->performJobs(std::move(TQ)); } diff --git a/tools/swift-reflection-dump/swift-reflection-dump.cpp b/tools/swift-reflection-dump/swift-reflection-dump.cpp index c06ec35de5f81..8846f4452e855 100644 --- a/tools/swift-reflection-dump/swift-reflection-dump.cpp +++ b/tools/swift-reflection-dump/swift-reflection-dump.cpp @@ -668,12 +668,15 @@ static int doDumpReflectionSections(ArrayRef BinaryFilenames, Demangle::Demangler Dem; auto Demangled = Dem.demangleType(Line); - auto *TypeRef = - swift::Demangle::decodeMangledType(builder, Demangled); - if (TypeRef == nullptr) { - fprintf(file, "Invalid typeref:%s\n", Line.c_str()); + auto Result = swift::Demangle::decodeMangledType(builder, Demangled); + if (Result.isError()) { + auto *error = Result.getError(); + char *str = error->copyErrorString(); + fprintf(file, "Invalid typeref:%s - %s\n", Line.c_str(), str); + error->freeErrorString(str); continue; } + auto TypeRef = Result.getType(); TypeRef->dump(file); auto *TypeInfo = builder.getTypeConverter().getTypeInfo(TypeRef); diff --git a/unittests/ClangImporter/ClangImporterTests.cpp b/unittests/ClangImporter/ClangImporterTests.cpp index 4068678fd8433..e353fba57745f 100644 --- a/unittests/ClangImporter/ClangImporterTests.cpp +++ b/unittests/ClangImporter/ClangImporterTests.cpp @@ -6,10 +6,6 @@ #include "swift/Basic/SourceManager.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangImporterOptions.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/FileSystemStatCache.h" -#include "clang/Frontend/CompilerInstance.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -58,6 +54,7 @@ TEST(ClangImporterTest, emitPCHInMemory) { // Create the includes. std::string include = createFilename(temp, "include"); ASSERT_FALSE(llvm::sys::fs::create_directory(include)); + options.ExtraArgs.emplace_back("-nosysteminc"); options.ExtraArgs.emplace_back((llvm::Twine("-I") + include).str()); ASSERT_FALSE(emitFileWithContents(include, "module.modulemap", "module A {\n" @@ -91,105 +88,3 @@ TEST(ClangImporterTest, emitPCHInMemory) { ASSERT_FALSE(emitFileWithContents(PCH, "garbage")); ASSERT_TRUE(importer->canReadPCH(PCH)); } - -class ForgetfulStatCache : public clang::FileSystemStatCache { -private: - std::unique_ptr RealStatCache; - -public: - ForgetfulStatCache() { - RealStatCache = std::make_unique(); - } - ~ForgetfulStatCache() = default; - - std::error_code getStat(StringRef Path, llvm::vfs::Status &Status, - bool isFile, - std::unique_ptr *F, - llvm::vfs::FileSystem &FS) override { - if (llvm::sys::path::extension(Path) == ".pcm") { - const auto UID = llvm::sys::fs::UniqueID(1, std::numeric_limits::max()); - Status = llvm::vfs::Status(Path, UID, - /*MTime*/{}, /*User*/0, /*Group*/0, - /*Size*/0, - llvm::sys::fs::file_type::regular_file, - llvm::sys::fs::perms::all_all); - return std::error_code(); - } - - return clang::FileSystemStatCache::get(Path, Status, isFile, F, - RealStatCache.get(), FS); - } -}; - -TEST(ClangImporterTest, missingSubmodule) { - // Create a temporary cache on disk and clean it up at the end. - ClangImporterOptions options; - SmallString<256> temp; - ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory( - "ClangImporterTest.missingSubmodule", temp)); - SWIFT_DEFER { llvm::sys::fs::remove_directories(temp); }; - - // Create a cache subdirectory for the modules and PCH. - std::string cache = createFilename(temp, "cache"); - ASSERT_FALSE(llvm::sys::fs::create_directory(cache)); - options.ModuleCachePath = cache; - options.PrecompiledHeaderOutputDir = cache; - - // Create the includes. - std::string include1 = createFilename(temp, "include1"); - ASSERT_FALSE(llvm::sys::fs::create_directory(include1)); - - std::string include2 = createFilename(temp, "include2"); - ASSERT_FALSE(llvm::sys::fs::create_directory(include2)); - - options.ExtraArgs.emplace_back((llvm::Twine("-I") + include1).str()); - options.ExtraArgs.emplace_back((llvm::Twine("-I") + include2).str()); - options.ExtraArgs.emplace_back("-DEXTRA_C_DEFINE=2"); - - ASSERT_FALSE(emitFileWithContents(include1, "module.modulemap", - "module CLib1 {\n" - " umbrella header \"" + include1 + "/Clib1.h\"\n" - " export * \n" - "}\n" - "module CLib2 {\n" - " umbrella header \"" + include2 + "/Clib2.h\"\n" - " export * \n" - "}\n")); - ASSERT_FALSE(emitFileWithContents(include1, "CLib1.h", - "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" - "#error \"unexpected compiler flags\"\n" - "#endif\n" - "void foo(void);\n")); - ASSERT_FALSE(emitFileWithContents(include2, "CLib2.h", - "#if !defined(EXTRA_C_DEFINE) || EXTRA_C_DEFINE != 2\n" - "#error \"unexpected compiler flags\"\n" - "#endif\n" - "void foo(void);\n")); - - // Set up the importer. - swift::LangOptions langOpts; - langOpts.Target = llvm::Triple("x86_64", "apple", "darwin"); - swift::TypeCheckerOptions typeckOpts; - INITIALIZE_LLVM(); - swift::SearchPathOptions searchPathOpts; - swift::SourceManager sourceMgr; - swift::DiagnosticEngine diags(sourceMgr); - std::unique_ptr context( - ASTContext::get(langOpts, typeckOpts, searchPathOpts, sourceMgr, diags)); - auto importer = ClangImporter::create(*context, options); - - // Install a stats cache that conveniently "forgets" that PCMs have different - // underlying FileEntry values. - importer->getClangInstance() - .getFileManager() - .setStatCache(std::make_unique()); - - context->addModuleLoader(std::move(importer)); - - auto CLib1 = context->getIdentifier("CLib1"); - auto CLib2 = context->getIdentifier("CLib2"); - // The first load succeeds and primes the ModuleManager with PCM data. - (void)context->getModule({ Located{ CLib1, {} } }); - // The second load fails because we collide in the ModuleManager. - ASSERT_TRUE(context->getModule({ Located{ CLib2, {} } }) == nullptr); -} diff --git a/unittests/runtime/CompatibilityOverride.cpp b/unittests/runtime/CompatibilityOverride.cpp index 80a9b086980b9..6056f26117930 100644 --- a/unittests/runtime/CompatibilityOverride.cpp +++ b/unittests/runtime/CompatibilityOverride.cpp @@ -35,8 +35,8 @@ namespace { return MetadataResponse{nullptr, MetadataState::Complete}; } - template<> - TypeInfo getEmptyValue() { + template <> + TypeLookupErrorOr getEmptyValue>() { return TypeInfo(); } } @@ -172,13 +172,13 @@ TEST_F(CompatibilityOverrideTest, test_swift_getTypeByMangledNode) { Demangler demangler; auto Result = swift_getTypeByMangledNode(MetadataState::Abstract, demangler, nullptr, nullptr, nullptr,nullptr); - ASSERT_EQ(Result.getMetadata(), nullptr); + ASSERT_EQ(Result.getType().getMetadata(), nullptr); } TEST_F(CompatibilityOverrideTest, test_swift_getTypeByMangledName) { auto Result = swift_getTypeByMangledName(MetadataState::Abstract, "", nullptr, nullptr, nullptr); - ASSERT_EQ(Result.getMetadata(), nullptr); + ASSERT_EQ(Result.getType().getMetadata(), nullptr); } TEST_F(CompatibilityOverrideTest, test_swift_getAssociatedTypeWitnessSlow) { diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 0892aee12e7c8..b1fe751b1ac4f 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1303,6 +1303,8 @@ mixin-preset= mixin_osx_package_test mixin_lightweight_assertions,no-stdlib-asserts +# SKIP LLDB TESTS (67923799) +skip-test-lldb [preset: buildbot_osx_package,no_assertions] mixin-preset= @@ -1313,8 +1315,6 @@ dash-dash no-assertions -# SKIP LLDB TESTS (67923799) -skip-test-lldb [preset: buildbot_osx_package,no_assertions,lto] mixin-preset=buildbot_osx_package,no_assertions diff --git a/utils/build-script-impl b/utils/build-script-impl index 1e80510fa7ad2..4c4d499371b9a 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -1407,6 +1407,13 @@ for host in "${ALL_HOSTS[@]}"; do fi fi + if [[ "${NATIVE_CLANG_TOOLS_PATH}" ]] ; then + common_cmake_options_host+=( + -DCMAKE_C_COMPILER="${NATIVE_CLANG_TOOLS_PATH}/clang" + -DCMAKE_CXX_COMPILER="${NATIVE_CLANG_TOOLS_PATH}/clang++" + ) + fi + llvm_cmake_options=( "${llvm_cmake_options[@]}" -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" diff --git a/utils/sil-mode.el b/utils/sil-mode.el index fe29c99d9b58b..59cc44ba0c4c7 100644 --- a/utils/sil-mode.el +++ b/utils/sil-mode.el @@ -259,6 +259,7 @@ ;; *NOTE* viewcfg must be in the $PATH and .dot files should be associated with ;; the graphviz app. (defvar sil-mode-viewcfg-program-name "viewcfg") +(defvar sil-mode-viewcfg-renderer "dot") (defvar sil-mode-viewcfg-buffer-name "*viewcfg*") (defcustom sil-mode-viewcfg-command-default "viewcfg" @@ -281,7 +282,8 @@ (process-connection-type nil)) (let ((p (start-process sil-mode-viewcfg-program-name sil-mode-viewcfg-buffer-name - sil-mode-viewcfg-command))) + sil-mode-viewcfg-command + (concat "--renderer=" sil-mode-viewcfg-renderer)))) (process-send-region p brace-start brace-end) (process-send-eof p))))) diff --git a/utils/swift-api-dump.py b/utils/swift-api-dump.py index 9022e9c89d5b2..d44b33ae19839 100755 --- a/utils/swift-api-dump.py +++ b/utils/swift-api-dump.py @@ -102,6 +102,8 @@ def create_parser(): parser.add_argument('--enable-infer-import-as-member', action='store_true', help='Infer when a global could be imported as a ' + 'member.') + parser.add_argument('--enable-experimental-concurrency', action='store_true', + help='Enable experimental concurrency model.') parser.add_argument('-swift-version', metavar='N', help='the Swift version to use') parser.add_argument('-show-overlay', action='store_true', @@ -328,6 +330,8 @@ def main(): extra_args = ['-skip-imports'] if args.enable_infer_import_as_member: extra_args = extra_args + ['-enable-infer-import-as-member'] + if args.enable_experimental_concurrency: + extra_args = extra_args + ['-enable-experimental-concurrency'] if args.swift_version: extra_args = extra_args + ['-swift-version', '%s' % args.swift_version] diff --git a/utils/swift_build_sdk_interfaces.py b/utils/swift_build_sdk_interfaces.py index 900bae30064e0..c8381193a9bd4 100755 --- a/utils/swift_build_sdk_interfaces.py +++ b/utils/swift_build_sdk_interfaces.py @@ -354,6 +354,14 @@ def process_module_files(pool, module_files): return overall_exit_status +def getSDKVersion(sdkroot): + settingPath = os.path.join(sdkroot, 'SDKSettings.json') + with open(settingPath) as json_file: + data = json.load(json_file) + return data['Version'] + fatal("Failed to get SDK version from: " + settingPath) + + def main(): global args, shared_output_lock parser = create_parser() @@ -373,6 +381,12 @@ def main(): if not os.path.isdir(args.sdk): fatal("invalid SDK: " + args.sdk) + # if the given output dir ends with 'prebuilt-modules', we should + # append the SDK version number so all modules will built into + # the SDK-versioned sub-directory. + if os.path.basename(args.output_dir) == 'prebuilt-modules': + args.output_dir = os.path.join(args.output_dir, getSDKVersion(args.sdk)) + xfails = () if args.ignore_non_stdlib_failures: if args.xfails: diff --git a/utils/swift_build_support/swift_build_support/cmake.py b/utils/swift_build_support/swift_build_support/cmake.py index 5ef74e0cf5ee4..85a97dfa747e2 100644 --- a/utils/swift_build_support/swift_build_support/cmake.py +++ b/utils/swift_build_support/swift_build_support/cmake.py @@ -18,6 +18,7 @@ from __future__ import absolute_import, unicode_literals import os +import platform import re from numbers import Number @@ -255,6 +256,9 @@ def build_cmake(self, source_root, build_root): # the source and build the source if necessary. Returns the path to the # cmake binary. def check_cmake_version(self, source_root, build_root): + if platform.system() != 'Linux': + return + cmake_source_dir = os.path.join(source_root, 'cmake') # If the source is not checked out then don't attempt to build cmake. if not os.path.isdir(cmake_source_dir): diff --git a/validation-test/compiler_crashers_2_fixed/sr13461.swift b/validation-test/compiler_crashers_2_fixed/sr13461.swift new file mode 100644 index 0000000000000..b6bda64bdce53 --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr13461.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -o /dev/null %s +// REQUIRES: asserts + +final class Klass { + static var current: Klass { + fatalError() + } +} +private struct Build { + let val: T + unowned let unownedBinding = Klass.current + unowned(unsafe) let unownedUnsafeBinding = Klass.current + weak var weakBinding = Klass.current +} +private func phase(_ val: T) -> Build { + return Build(val: val) +}